FreeBuf|图解利用虚函数过GS保护( 三 )


FreeBuf|图解利用虚函数过GS保护
文章图片
首先我们发现原始参数(0x41402138)不在栈中 , 因此无法跳回原始参数的内存空间继续执行 , 但是惊喜的发现buf首地址0x18FE24=ESP+4 , 又想到跳转后紧接着执行的是call操作 , 于是我们只需要执行“poppopret”指令序列(后面简称ppt)后就可以转到buf首地址0x0018FE24执行 , 因为call操作会将返回地址入栈 , 即esp为0x0018FE1C , 然后poppop,在ret时 , 栈顶为0x0018FE24 , 就可以转到0x0018FE24处执行了 。 因此这时候 , 我们只需要找到ppt指令序列地址 , 可以利用ImmunityDebugger工具找 , 加载当前exe , 使用!monaseh指令搜索ppt指令地址 , 如下图 。 我们搜到7个ppt指令序列地址 , 这里选择的要求为ppt指令序列操作不影响当前程序流程 , 因此选择不带ebpesp这种指令就可 。 这里我们选择的ppt指令序列地址为0x41401353 。
FreeBuf|图解利用虚函数过GS保护
文章图片
这时候的payload结构为:
FreeBuf|图解利用虚函数过GS保护
文章图片
写到这里 , 可能会想到ppt地址后 , 直接+shellcode+填充+原始参数地址 , 即可构成最后的payload , 但是事实却没有这么简单 , 我看了很多网上的教程 , 大部分人都只写到了这里 , 便可以成功执行shellcode , 但是我仔细调试了代码 , 发现是存在问题的 。 因为跳到buf内存后 , 0x0018FE24处是个ppt指令序列地址0x41401353 , 就会再一次执行ppt指令序列 , 因此又如何重新返回当前shellcode内存空间呢?这里我们先理下整体思路 , 如下图所示 。
FreeBuf|图解利用虚函数过GS保护
文章图片
我们需要知道局部变量buf和原始参数的值都为payload数据 。 然后结合上图对跳转进行如下分析:
1跳:利用局部变量buf溢出 , 将虚表指针精准覆盖为原始参数地址0x41402138 , 因此通过虚表指针跳到原始参数地址处 。
2跳:和经过分析栈状态 , 发现不可能跳回原始参数内存区域 , 但发现局部变量buf地址=当前esp+4 , 并且知道将执行call指令 , 结合上述两点 , 只需要将虚函数地址填为ppt指令序列地址 , 即可完成跳到shellcode内存区域 , 即局部变量buf地址处 。 这里2跳是指将虚函数地址填充为ppt指令序列地址 , 然后执行虚函数 , 即call0x41401353(ppt指令序列地址) , 将返回地址入栈 。
3跳:跳转到ppt指令序列地址 , 执行popecx;popecx;后 , 栈顶为0x0018fe24 , 然后ret , 跳到局部变量buf首地址处 。
4跳:这个时候 , 跳到局部变量buf首地址处 , 发现 , 当前地址0x0018fe24内容为ppt指令序列地址0x41401353 , 因此会再一次pop两次出0x0018fe28和0x0018fe2C的值 , 然后执行ret指令 , 挑转到0x18ffe30处 , EIP=???(图中) , 跳到???处 。 这个的???必须是地址 , 并且能够再次跳回shellcode栈空间执行 , 这时候观察堆栈状态 , 发现esp=0x0018fe34 , 因此很容易想到0x0018fe30处填的是jmpesp地址 。
5跳:jmpesp , 跳到0x0018FE34 , 执行shellcode 。
FreeBuf|图解利用虚函数过GS保护
文章图片
jmpesp地址可以利用ImmunityDebugger工具搜索:得到jmpesp地址为0x414010af
指令:
!monajmp-resp这里需要说明0x0018fe28和0x0018fe2C地址的值可以任意填充,不影响程序流程的指令即可 , 这里用“x90”填充 , 因为他们只需要pop出去就可 。
我习惯shellcode前加nop滑轨方便调试 , 因此综上分析 , 最终payload结构为: