# pwnable.tw bookwriter ubuntu16,libc 2.23
# 保护:
1 | giantbranch@ubuntu:~/Desktop/pwnabletw/BookWriter$ checksec --file=bookwriter |
# 主要代码:
1 | giantbranch@ubuntu:~/Desktop/pwnabletw/BookWriter$ ./bookwriter |
# main 函数
1 | void __fastcall main(int a1, char **a2, char **a3) |
# readname 函数
1 | __int64 sub_400BDF() |
# menu 函数
1 | int menu() |
# add 函数
1 | int add() |
# info 函数
1 | int info() |
# edit 函数
1 | int edit() |
这里虽然会更新 size,但是如果我们读入的 text,刚好填充 chunk 的用户区,但是结尾不是’\x00’,更新长度的时候,会把下一个堆块的 size 算进去。造成溢出,如果是 topchunk_size 溢出会更多
# updatename 函数
1 | unsigned __int64 updatename() |
# 思路
程序的一的大漏洞就是对于输入的字符串只会检查换行符,而不会见擦汗输入是否有结束符,输入 name 的时候,党字符串长度为 64 时,就会接上 gptr, 导致堆地址泄露。
程序没有 free,如何泄露 libc?
add 时,判断错误,会导致读入第九个在 sizelist 的位置,所以修改 sizelist [0] 的大小为 0,readck 允许读入为 0。导致再次 edit ptrlist [0] 时读入很大数据溢出。修改 topchunk:
edit 时,对于非对齐输入,会造成溢出,将下一个堆块的 size,计入当前堆块 content 的 size,topchunk 溢出 3 字节,其他堆块溢出 1 字节。通过三字节的溢出将 topchunk 变得很小。
updatename 的时候使用了 scnaf ("% d"),这有点奇怪,因为 scanf 需要用到堆空间来处理缓存:(这里没有任何利用价值):
关键还是在于修改 topchunk , 党 topchunk_size 小于申请的空间大小时,会释放 topchunk 进入 unsortedbins, 这样就会造成 libc 及地址的泄露。
当我们已经申请了 8 个 page,如果 size [0]==0,就可以绕过检查,额外申请出来一个 page,将地址返回写道 size [0],那么,再次 edit ptrlist [0] 时,就可以实现溢出,(因为地址很大,足够溢出)
同时,更新 comment 的长度依旧是检查空字符,所以可以使的更新后对应的 size 为 0,导致第九个指针变量为 0,可以继续申请堆块,这个可以重复利用。
如此,旧的 topchunk 在 unsortedbins 里面,修改其 bk 指针,利用 unsortedbins attack 实现任意地址写大数据(main_arena+88),
1 | pwndbg> p _IO_list_all |
下面很巧妙的东西来了,我们伪造_IO_list_all 的数据为 main_arena+88, 那么原本_ _chain 指向下一个 file 结构体,结果 fakeFIlede 的 chain 就是 smallbins 的入口马志翔里面的第一个 chunk。所以我们将 unsortedbin 里面的 topchunk 的 size 变小,就会进入 smallbin,然后 fakefile_chain -->topchunk,我们利用溢出将 topchunk 伪造成另一个 fakefile2,并伪造 vtable。伪造 fakefile2 要注意, flag 写入的是‘/bin/sh\x00’ , 因为 vtable 里面的函数大多会以 flag 作为自己的参数。
1 | pwndbg> p *(struct _IO_FILE_plus *) 0x7f7ba0e30b78 <main_arena+88> //伪造的第一个fakefile |
下面要考虑的就是如何出发伪造的 vtable。首先我们这里的 fakefile2 取代了 stdout,所以当程序报错的输出信息时,会调用 over_flow,我们在伪造的 vtable 对应位置写入 system。
# exp
1 | from pwn import * |