程序实现了一个虚拟机, 有自己的栈和内存区, 虚拟机的内存段被固定映射到0x200000, 代码区被随机映射:相关结构体:写个脚本导出函数让AI分析各个操作码对应的操作(我到底什么时候能用上MCP啊):同时拷打AI写出汇编工具:程序中所有涉及到数组的操作都对下标进行了检验, 有关下标唯一的漏洞我只在push_imm里找到一个, 在栈指针自增后是先进行赋值再检验下标的, 可以越界访问1个qword, 但是我不知道有什么用.另外程序还对地址值有检验, 要通过vm来执行类似mov [r0], r1的操作时会对r0检验其最高位是否置1, 这是vm对有效地址打的tag, 同时通过运算得出的地址(通过是否带tag来判断)会检测其是否在vm的栈和内存段的合法区域, 不在的话会强制转化为内存段的初始位置.而在限制运算得到地址这一点上程序对寄存器之间的运算和寄存器与立即数之间的运算是不同的, 以乘法为例:寄存器之间的运算只会在运算前寄存器本身就是带tag的地址值才会进行检测, 而和立即数的运算会在运算完后检测算出的结果是否为地址值并检测合法性.另外一点就出在check_addr上:本来只需要检验地址是否落在固定映射的mem上就好了, stack本身是随机映射的, 用户不应该能得到其地址值.综合以上两点可以通过爆破的方式得到stack被映射到的地址, 伪代码如下:调试可以发现vm栈段被映射到的地址低12位是320h: 最高1字节就选定为0x55, 只需要爆破中间56位即可, bytecode如下:上面说过寄存器和立即数的运算会导致检测地址, 所以这里存起来的实际上是(TAG | addr) >> 1, 在要算出地址时与存放了2的r4做乘法即可得到带tag地址且不会被检测, 这一步在本机上的爆破时间不超过5秒, 但是靶机很可能程序给的100秒alarm都不够用, 要多尝试几次, 至此我们获得了任意已知地址读写的能力.在得到了一个堆地址后就好办很多了, 注意到开头push了两次吗? 现在只需要将堆上vm代码段的地址放入vm的栈基址中, 再进行一次pop操作就能将一个mmap出的地址送入寄存器中, 而mmap出的地址与ld间有固定偏移: 而ld中存放了大量libc中的地址: 这里就选择这个_dl_catch_exception, 它与mem段的偏移为0x3018, 这一步的bytecode:程序开启了got表保护, 不用思考改got来getshell了, 我的想法是通过libc中的environ泄露栈地址来修改函数返回地址写入ROP链, 这一步的bytecode:接下来就没什么好说的了, 写入返回到system('/bin/sh')的ROP链, 这里直接返回上去栈会不对齐, 要多放一个retvm *do_malloc()
---
来源: 看雪论坛
原文链接: https://bbs.kanxue.com/thread-288321.htm
[原创]KCTF2025 day8 wp
161 浏览
0 回复
暂无回复,快来抢沙发吧!