先来看一下 c 程序
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
char sh[]="/bin/sh";
int init_func(){
setvbuf(stdin,0,2,0);
setvbuf(stdout,0,2,0);
setvbuf(stderr,0,2,0);
return 0;
}
int func(char *cmd){
system(cmd);
return 0;
}
int main(){
char a[8] = {};
char b[8] = {};
//char a[1] = {'b'};
puts("input:");
gets(a);
printf(a);
if(b[0]=='a'){
func(sh);
}
return 0;
}
编译执行 (这边先用 64 位处理编译了)
gcc x.c -o x
./x
程序的大致功能是输入一个字符串然后输出这个字符串。
反汇编一下
gdb ./x
start
disassemble main
可以发现关键就在于 cmp 处。如果 al = 0x61 就会跳过 jne 执行后面的代码。
在 cmp 处打一个断点。
b *0x00005555555552d2
我们发现 eax 寄存器的值由上一步决定
于是在这 里也打一个断点
b *0x00005555555552ce
从 0x00005555555552ce 出看一下 20 行汇编代码
x/20i 0x00005555555552ce
eax = byte ptr [rbp -0x10] ,所以我们看一下 rbp -0x10 地址处有些什么内容
我们先执行到这一步,然后查看 rbp -0x10 地址处前 20个字节的数据。
x/20b $rbp-0x10
可以看到第一个字节是 0x00 ,现在我们尝试把他第一个字节改为 0x61
查看值 rbp-0x10
p $rbp-0x10
然后修改这处地址第一个字节
set *0x7fffffffe030 = 0x61
然后继续执行果然进入了函数里拿到 shell 了,本地验证了一下只要让 al = 0x61 就是能拿到 shell 的。
现在我们从头开始分析,怎么才能让 al 的值变为 0x61 呢?我们可以看到系统调用了 gets 函数读取输入赋给 [rbp -0x18]
而 gets 函数很危险,碰到换行符才会停止输入
我们输入 aaa 再看看 0x7fffffffe028
可以发现确实写进去了 有三个 616161,那如果我们写多点岂不是就能使之溢出覆盖 0x0x7fffffffe030 处的值了?
没错,这样就能拿到 shell 了。