本文为看雪论坛优秀文章
看雪论坛作者ID:techliu
目录
一、shellcode1.1 简介1.2 缓冲区溢出
- 溢出示例
- 规划缓冲区
1.3 字符串转16进制脚本1.4 几种技巧
- 跳板技术
- 抬高栈顶保护shellcode
二、windbg配置mona2.1 参考2.2 配置符号路径2.3 安装python2.4 配置windbg2.5 常用mona命令
- 显示载入的模块
- 查找机器码
2.6 在Immunity Debugger中使用mona三、Freefloat FTP Server 1.0 溢出漏洞分析3.1 简介3.2 参考3.3 分析工具3.4 Start
- fuzzing
找到覆盖返回地址的偏移
- 利用
3.5 End四、End
一、shellcode
>>>>1.1 简介
和Exploit的关系?犹如导弹研究者和利用导弹者。
Exploit负责将程序导向shellcode,shellcode又称为payload。通常缓冲区溢出漏洞中可利用shellcode技术。最关键的是如何让程序交出控制权给shellcode。
>>>>1.2 缓冲区溢出
缓冲区(Buffer)又称为缓存(Cache),是内存空间的一部分。简单理解,可以把它想象成一段栈空间。当一个程序在对缓冲区进行写操作时是很危险的,如果写入的数据超过缓冲区大小则会将缓冲区之外的数据覆盖,造成数据溢出。
溢出示例
shellcode就是利用了缓冲区存放,例如下面这段程序就存在溢出漏洞:
#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);//over flowed here!
return authenticated;
}
main()
{
int valid_flag=0;
char password[1024];
FILE * fp;
LoadLibrary("user32.dll");//prepare for messagebox
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);
}
在上面的程序中,verify_password函数中存在大小为44的缓冲区buffer,后面没用做长度校验,直接使用了strcpy进行了填充,导致缓冲区溢出。该程序可通过在password.txt中填充shellcode,通过OD动态调试发现buffer缓冲区之后存放了函数返回地址,只要再把该地址覆盖成shellcode的入口地址就能执行shellcode。
规划缓冲区
在填充shellcode的时候还要合理规划缓冲区,此处shellcode填充在返回地址之前,导致shellcode的大小被限制在44,所以shellcode可以填充在函数返回地址之后。缓冲区中被放的数据可以有以下几种:1. 填充物:一般是nop指令,函数返回地址只要落在该范围内就能顺序到达shellcode位置。2. 被覆盖的返回地址:可以是shellcode入口地址、跳转指令地址、近似到shellcode的nop填充物地址。3. shellcode机器码
摆放方式:
![6cc11ae8f32962eb791b8ee52bcdf76a.png](https://img-blog.csdnimg.cn/img_convert/6cc11ae8f32962eb791b8ee52bcdf76a.png)
>>>>1.3 字符串转16进制脚本
str_to_little_endian.py
溢出中的shellcode通常把静态数据(例如字符串)存储到栈中,例如存储“techliu”到栈中:
xor ebx, ebx
push ebx
push 0x0075696C
push 0x68636574
这是根据栈的存储特性以及小端序特性写入到内存中的,这样在内容中就能看到'techliu'的字符串,前两行是将字符串结束符NULL入栈,但是不能直接push 0,因为可能会发生shellcode截断。
而且push只能操作DWORD大小的数据,所以需要对字符串进行切分入栈。
在py脚本后直接跟转化的字符串即可,生成的16进制从下到上依次入栈。
脚本内容:
import struct
import sys
BLOCK = 4
if __name__ == '__main__':
des_str = sys.argv[1]
if not des_str:
print("Not argv[1]!")
exit(0)
if isinstance(des_str, str):
des_str = des_str.encode()
# str_len = len(des_str);
s