大致框架
官網的一個簡單樣例
from pwn import * context(arch = 'i386', os = 'linux') r = remote('exploitme.example.com', 31337) # EXPLOIT CODE GOES HERE r.send(asm(shellcraft.sh())) r.interactive()
基本上仿造這個格式就可以寫exp了。
from pwn import *
用來導入pwntools模塊
context(arch = 'i386', os = 'linux')
設置目標機的信息
r = remote('exploitme.example.com', 31337)
用來建立一個遠程連接,url或者ip作為地址,然后指明端口
這里也可以僅僅使用本地文件,調試時方便:
r = process("./test")
test即為文件名,這使得改變遠程和本地十分方便.
asm(shellcraft.sh())
asm()函數接收一個字符串作為參數,得到匯編碼的機器代碼。
比如
>>> asm('mov eax, 0') '\xb8\x00\x00\x00\x00'
shellcraft模塊是shellcode的模塊,包含一些生成shellcode的函數。
其中的子模塊聲明架構,比如shellcraft.arm 是ARM架構的,shellcraft.amd64是AMD64架構,shellcraft.i386是Intel 80386架構的,以及有一個shellcraft.common是所有架構通用的。
而這里的shellcraft.sh()則是執行/bin/sh的shellcode了
r.send()將shellcode發送到遠程連接
最后,
r.interactive()
將控制權交給用戶,這樣就可以使用打開的shell了
Context設置
context
是pwntools用來設置環境的功能。在很多時候,由於二進制文件的情況不同,我們可能需要進行一些環境設置才能夠正常運行exp,比如有一些需要進行匯編,但是32的匯編和64的匯編不同,如果不設置context會導致一些問題。
一般來說我們設置context只需要簡單的一句話:
context(os='linux', arch='amd64', log_level='debug')
這句話的意思是:
1. os設置系統為linux系統,在完成ctf題目的時候,大多數pwn題目的系統都是linux
2. arch設置架構為amd64,可以簡單的認為設置為64位的模式,對應的32位模式是’i386’
3. log_level設置日志輸出的等級為debug,這句話在調試的時候一般會設置,這樣pwntools會將完整的io過程都打印下來,使得調試更加方便,可以避免在完成CTF題目時出現一些和IO相關的錯誤。
數據打包
數據打包,即將整數值轉換為32位或者64位地址一樣的表示方式,比如0x400010表示為\x10\x00\x40一樣,這使得我們構造payload變得很方便
用法:
* p32/p64
: 打包一個整數,分別打包為32或64位
* u32/u64
: 解包一個字符串,得到整數
p對應pack,打包,u對應unpack,解包,簡單好記
payload = p32(0xdeadbeef) # pack 32 bits number
數據輸出
如果需要輸出一些信息,最好使用pwntools自帶的,因為和pwntools本來的格式吻合,看起來也比較舒服,用法:
some_str = "hello, world" log.info(some_str)
其中的info代表是log等級,也可以使用其他log等級。
Cyclic Pattern
Cyclic pattern是一個很強大的功能,大概意思就是,使用pwntools生成一個pattern,pattern就是指一個字符串,可以通過其中的一部分數據去定位到他在一個字符串中的位置。
在我們完成棧溢出題目的時候,使用pattern可以大大的減少計算溢出點的時間。
用法:
cyclic(0x100) # 生成一個0x100大小的pattern,即一個特殊的字符串 cyclic_find(0x61616161) # 找到該數據在pattern中的位置 cyclic_find('aaaa') # 查找位置也可以使用字符串去定位
比如,我們在棧溢出的時候,首先構造
cyclic(0x100)
,或者更長長度的pattern,進行輸入,輸入后pc的值變味了0x61616161,那么我們通過cyclic_find(0x61616161)
就可以得到從哪一個字節開始會控制PC寄存器了,避免了很多沒必要的計算。
匯編與shellcode
有的時候我們需要在寫exp的時候用到簡單的shellcode,pwntools提供了對簡單的shellcode的支持。
首先,常用的,也是最簡單的shellcode,即調用/bin/sh
可以通過shellcraft得到:
注意,由於各個平台,特別是32位和64位的shellcode不一樣,所以最好先設置context。
print(shellcraft.sh()) # 打印出shellcode
不過,現在我們看到的shellcode還是匯編代碼,不是能用的機器碼,所以還需要進行一次匯編
print(asm(shellcraft.sh())) # 打印出匯編后的shellcode
asm可以對匯編代碼進行匯編,不過pwntools目前的asm實現還有一些缺陷,比如不能支持相對跳轉等等,只可以進行簡單的匯編操作。如果需要更復雜一些的匯編功能,可以使用
keystone-engine
項目,這里就不再贅述了。
asm也是架構相關,所以一定要先設置context,避免一些意想不到的錯誤。