2022网鼎杯一道pwn boom2,虚拟机。
跟boom1不同之处在于,boom1的虚拟机接受输入为C语言源码,而boom2接受的为opcode。
这种自定义opcode的虚拟机一般是要先静态分析看都支持哪些操作指令,如果实在遇到不好分析的指令也可以手动fuzz+gdb调试猜出来。这道题首先看一下保护:
保护全开。
看一下main,这题虚拟机整个逻辑都在main里了。
main函数依旧是非常长,不过这里可以比较明显的看出一个超大的switch case,应该就是表示不同的操作码opcode了。主要工作在于分析出不同的opcode分别表示什么指令以及其指令格式。
首先注意程序开头的操作,malloc了两个非常大的空间,其中一个用于接收我们用户的输入,即机器码用于执行。还有一个用于当作虚拟机的栈,并对栈进行了初始化。
对不同opcode表示的指令做静态分析,这里给出结果:
实际上整个虚拟机就是虚拟栈的rsp指针和一个寄存器reg来实现。
突破口在于虚拟栈初始化时携带了程序真实栈地址,存在了逃逸的可能。
可以看到这个0x85000长的段即为两次malloc分配到的段,用于当作输入数据缓冲区和虚拟栈。gdb看一下虚拟栈初始化后的情况:
这里看到虚拟栈的rsp+0x8出存在一个程序真实段的地址,经调试发现其与main函数返回地址libc_start_main存在固定偏移为 ret_addr =stack_leek-0x118。
则我们考虑通过这个真实栈地址进行虚拟机逃逸,覆盖main函数返回地址为one_gadget。攻击思路如下:
1、首先借助虚拟栈上存在的真实栈地址,计算得到main函数返回地址的位置
2、借助指令9可以拿到libc_start_main地址,再次计算偏移可以得到libc基地址
3、再次计算偏移得到one_gadget地址。
4、借助指令11可以向main返回地址处写入one_gadget,劫持程序getshell。
【注意】程序指令集中加减法计算后会将结果pop到reg中,需要再次压栈操作进行后续计算。
完整exp如下:
攻击脚本运行结果:
成功getshell。
这题主要的难点在于理解虚拟栈的用法,进而就可以比较简单的理解虚拟机自定义的指令集了,指令1、9~30都是比较容易看出来的,指令0、6、8不是那么好看可能需要调试+猜想,不过不用这几条指令也可以完成攻击了。
【注】写本文章过程中作者参考了CSDN博客
https://blog.csdn.net/Breeze_CAT/article/details/106078982#:~:text=2020%E5%B9%B4%E7%AC%AC%E4%BA%8C%E5%B1%8A%E7%BD%91%E9%BC%8E
By Del0n1x
Keyboard