1. babyrop
leak MSVCR100 address => get system address => get address of "cmd.exe" => overwrite return address with system address
|
|
payload:
padding + system_address + 4 bytes padding + cmd_address
2. babyrop2
leak babyrop2 address and stack address => get system("cmd.exe") address and seh address => get first seh handler offset
|
|
代码说明:
首先通过第一个printf函数leak出babyrop2地址、main函数中的ebp地址
然后通过ida获取message在main函数中偏移为[bp-CCh],通过windbg获取first seh的地址与ebp之间的差值为0x38。于是结合起来就可以获得填充字符串数offset
通过windbg获取first ebp所指向的下一个seh地址 与 ebp之间的差值 0x94
为了让程序进入异常,我们在payload后加入很长的字符串
payload:
padding + next_seh_address + system_cmd_address + long string
3. babyvtable
了解一些基本的知识点
3.1 windows x64平台fastcall调用约定
前四个整型或指针类型参数由RCX,RDX,R8,R9依次传递,前四个浮点类型参数由XMM0,XMM1,XMM2,XMM3依次传递。
调用函数为前四个参数在调用栈上保留相应的空间,称作shadow space或spill slot。即使被调用方没有或小于4个参数,调用函数仍然保留那么多的栈空间,这有助于在某些特殊情况下简化调用约定。
除前四个参数以外的任何其他参数通过栈来传递,从右至左依次入栈。
由调用函数负责清理调用栈。
小于等于64位的整型或指针类型返回值由RAX传递。
浮点返回值由XMM0传递。
更大的返回值(比如结构体),由调用方在栈上分配空间,并有RCX持有该空间的指针并传递给被调用函数,因此整型参数使用的寄存器依次右移一格,实际只可以利用3个寄存器,其余参数入栈。函数调用结束后,RAX返回该空间的指针。
除RCX,RDX,R8,R9以外,RAX、R10、R11、XMM4 和 XMM5也是易变化的(volatile)寄存器。
RBX, RBP, RDI, RSI, R12, R14, R14, and R15寄存器则必须在使用时进行保护。
在寄存器中,所有参数都是右对齐的。小于64位的参数并不进行高位零扩展,也就是高位是无法预测的垃圾数据。
3.2 C++虚表介绍
当类继承了父类(父类含有虚函数),或者类中本来就有虚函数,那么编译之后就会产生虚表。
若A、B继承了父类C,那么在rdata段中将会有连续的空间存放A继承C的虚函数指针、B继承C的虚函数指针
实例化A或B时,将会在栈空间,则地址的首部放置指向虚表的指针,紧接着是各个局部变量;若是new()创建,则是堆空间,这时局部变量放置到栈空间,malloc的变量放置到堆空间。
3.3 本题说明
由于babyvtable使用了CFG、ASLR、DEP、GS、SafeSEHOP等完整的防护机制,看起来貌似无懈可击,但是CFG的堆地址随机分配,导致我们可以让结构体、结构体中某个变量malloc出的字符串 并不一定成前后分布。
兴许堆地址随机分配,可能造成结构体中某个变量malloc出的字符串在结构体的前面,从而堆溢出造成覆盖虚表指针等操作!
CFG打开一般需要连续malloc同一区域16次,或不连续malloc同一区域0x40次!打开CFG,意味着malloc的堆地址随机化!
由于windows的ASLR是以页为单位,也即4k大小为单位,所以为了判断某个位置是否是结构体开始的虚表指针,只需要查找其在主程序中的位置偏移(33f0),由于页为4k,大小为1.5个字节,所以比较的时候是ord(msg[i])==0xf0 and (ord(msg[i+1])&0x0f)==0x3
idata段中放置了一些程序用到的函数iat