-
-
[分享]《0day漏洞分析第二版》第十章第3节覆盖虚函数突破GS修改实验记录
-
发表于:
2020-11-26 11:37
7966
-
[分享]《0day漏洞分析第二版》第十章第3节覆盖虚函数突破GS修改实验记录
首先书上的shellcode代码肯定先要修改一番了。可以使用之前帖子中的代码作为shellcode。
https://bbs.pediy.com/thread-246532.htm
新的代码如图-1所示:
图-1 shellcode突破GS测试代码
这里的shellcode尾部先补充0x90填充满200字节,按照failwest的实验步骤,目的是先观察一下内存布局。
代码使用vs2010,设置禁用优化选项,配置关闭数据执行保护(DEP),将“是(/NXCOMPAT)”修改为“否(/NXCOMPAT:NO)”,最后build release版本。编译后用ollydbg加载,如图-2所示。
图-2 ollydbg加载初始
看了直傻眼呀,对照书上说要弄清楚变量与虚标指针在内存中的详细布局,让ollydbg加载程序,在执行完strcpy后暂停。目前,我完全没有看到strcpy在哪儿。只能一直F8,一路跟踪,直到如图-3所示位置,按下F7,进入main函数。
图-3 找到wmain入口
继续往后根据直到调用gsv函数,就可以看到strcpy了,如图-4所示,由于vs2010根本不再支持strcpy了。所以,这里我用strcpy_s代替它,并且特意把最大缓冲区值设置成400,比buf的200要大,可以溢出。
图-4 找到strcpy_s位置
执行strcpy_s先看看没有溢出时,内存的布局,如图-5所示。
图-5 buf缓冲区后的栈中情况
接下来就开始脱离failwest原书的内容了。本来这里是failwest最精妙的设计,shellcode的头部是x04\x2b\x99\x7C,既是虚函数的挑板地址,又是被跳回来后可以无关痛痒被执行的代码。可是在现在的系统中,这个方法失效了。因为vs2010编译的代码和寄存器信息都变化了,原书中这里call eax,而现在虚函数是call edx,相对应的寄存器也都变化,包括x04\x2b\x99\x7C地址也不再是“pop pop retn”了。所以无法照抄,只能利用failwest的思想继续试验下去了。就好像failwest把大块肉夹到我嘴里,现在一张桌子隔开了我们,还差一点我才能吃到它。肉不能往前了,那就我自己往上凑吧。
首先观察发现需让字符串结束符’\0’覆盖虚表指针最低位即可让其指向shellcode的头部这部分是相同的。然后发现eax和原书中不同,指向的就是shellcode的头部,如图-6所示,我是不是可以利用call eax做为挑板,再次跳回shellcode执行呢?
图-6 栈溢出后的寄存器信息
这个可行性很大,果断动手,将挑板地址设置为Kernel.dll中的call eax指令。重新加载跟踪发现一直到call eax都正常,跳回来以后出事了,因为新的Kernel.dll地址位置作为指令时,不再是可以无害执行的了,如图-7所示。
图-7 call eax只能当跳板地址,不能当无害指令
我想着,既然跳板地址是一定要设置在这里的,又不可以无害执行,能否找一个能跳回来又能绕开这个跳板的指令呢?我突然想到了eax+4的地址,不就是shellcode真正的头吗?是不是可以利用call [eax+4]?没有多想,我立刻激动的开始行动,只是担心能不能找到call [eax+4]的地址。皇天不负有心人,居然真的有,如图-8所示,仅仅2个啊,太激动了,我仿佛看到成功就在眼前了,嘴里哼着这都不成功敢不敢直播吃鸡蛋壳。
图-8 call [eax+4]居然都有,虽然只有2条
编译重启动ollydbg加载,轻车熟路跟踪到内核的call [eax+4]位置,再按下F7,下面只能是阳光大道了。可是,我没看到我的shellcode,ollydbg提示崩溃停止了。什么鬼?什么情况?为什么?我是谁?我从哪里来?我要去哪里?到了这里,接下去就是查找原因。理论上我的思想也是很光辉的呀,为什么ollydbg不承认呢?它是嫉妒我吗?当然不是,只能默默的找原因。离开了failwest的带路,哪怕是吃一口摆在眼前的肥肉,我都做不到。那还研究个啥。
一天时间流逝,我到了深夜在床上还在思考为什么会这样。第二天早晨朦朦胧的时候,突然感觉想到了什么。赶紧继续研究,[eax+4]和我想的eax+4不是一回事。果然,在ollydbg执行call [eax+4]之前,我先看了一下[eax+4],那一堆无意义的内容,根本不是我想要跳回的eax+4这个地址。我现在知道我是谁了,可是我要去哪里?
我想要不然试试debug模式?也许在那里溢出后,能有新的突破?于是编译,加载,跟踪,傻眼了。Debug模式更狠毒,它的GS居然在虚函数调用之前就先检查strcpy_s是否存在栈溢出了,连溢出的虚表地址都用不上,shellcode的跳板地址都没机会露面,如图-9所示。
图-9 debug模式更狠,GS在虚函数之前就开始检查,失败!
我又回到了我要去哪里的状态下,我想连汇编都敢啃的人,没有人会在这里退缩。我要的是思路,于是回过头,我继续按照failwest的指点,去寄存器里找信息。图-6中,我看到esp和ebp,发现ebp指向了栈中的shellcode中而且是\x90的区域。我仿佛看到了力量,满血复活,接下来就是找call ebp的地址,还有编辑出jmp回去shellcode头部的指令替换ebp这部分的的\x90指令。经历过多次失败,我就算看到希望,也不敢哼着这都不成功直播吃蛋壳的话了。Call ebp地址如图-10所示。
图-10 call ebp尚有几条
将地址编译,重加载,跟踪,到了call ebp,心情紧张的按下F7,看到了自己的shellcode,看到了一串串的nop指令。我终于再次回到了我的shellcode中了,回家的感觉真好。我用最笨的办法,计算我的shellcode头部位置,减去当前nop的位置,然后反汇编jmp到shellcode绕开跳板指令的位置,得到汇编跳转指令,替换,然后编译,重加载。这次如果不行,真可以直播吃蛋壳了吧?隐隐感觉可能不行,反正就是不行。一路跟踪,jmp也跳到了我要的shellcode真正的头部位置,不再跟踪了,点下运行。看到了熟悉的对话框。真的成功了。最终的代码如图-11所示。
图-10 最终的shellcode及其填充代码
[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!