题目
题目源码如下
1 | // gcc houseofsome.c -o houseofsome -Wl,--dynamic-linker=./ld-linux-x86-64.so.2 -Wl,--rpath=./ |
除了题目代码之外,还自己编译了一个libc,加上patch,patch如下
1 | diff --git a/libio/libioP.h b/libio/libioP.h |
可以看到,这里对wide_data加上了patch
编译时的命令如下
1 | CC="gcc" CXX="g++" CFLAGS="-g -g3 -ggdb -gdwarf-4 -Og -Wno-error" CXXFLAGS="-g -g3 -ggdb -gdwarf-4 -Og -Wno-error" ../configure --prefix=/home/csome/houseofsome/glibc-2.38/build/x64 --disable-werror --enable-bind-now |
详细编译流程见Tover师兄写的https://0xffff.one/d/337
简单的分析
通过逆向可以知道,这里泄露libc是通过scanf的未写入trick实现的,但是ida观察init函数的时候,并不能发现stdin等数据放到了栈上,需要gdb观察或者阅读汇编才知道。
1 | int init() { |
其次,这里有一次libc内任意地址写\x00的机会在draw功能
1 | void draw() { |
还有一个小trick——fopen,这个函数会使用malloc分配一个IO_FILE_plus结构,作为打开文件的管理块,并通过头插法进入IO_list_all
1 | void change_dev() { |
总结一下
- 已知Libc地址
- 一次libc内任意地址写1字节\x00,off by null
- fopen能使得IO_list_all内写入heap地址
- 由于wide_data的vtable加入了check,故不能使用apple2的链条
House of Some
详细的原理见https://blog.csome.cc/p/house-of-some/
其使用的方法是
- 已知glibc基地址
- 可控的已知地址(可写入内容构造fake file)
- 需要一次libc内任意地址写可控地址
- 程序能正常退出或者通过exit()退出
其中,条件1和4程序中可以直接满足,条件3可以通过fopen和一次off by null完成,条件2可以被弱化,并不需要已知,只需要可控即可
那么在构造任意地址写的fake_file的过程中,需要wide_data指针,这个指针需要在可控地址位置——堆内,但是我们并不能泄露堆地址
1 | fake_file_read = flat({ |
这里需要使用largebin的next size指针残留,构造出一个合法的wide_data地址
风水脚本如下
1 | name(0x2b0-1, flat({ |
最后伪造,这里中间需要使用指针残留,所以伪造fake_file的时候,需要分开
1 | environ = libc.symbols['__environ'] |
最后houseofsome一把梭
完整exp
House_of_some工具见https://github.com/CsomePro/House-of-Some
1 | from pwn import * |
碎碎念
出题的时候有些疏忽,忘记了main_arena也在libc内,导致可以off by null修改TopChunk,使得最后能够控制堆结构
但是这个方法较为复杂,是一个小小的非预期,在比赛还有30个小时多的时候放出,在14小时之后出现第一血,最后只有5解出,算是预期之内吧
接下来如果还有机会,我还会带来更加有趣的利用手法的题目,敬请期待吧
作者: Csome