PWN 常用指令與概念

pwn 常用指令以及概念紀錄

前置概念

保護

Pwntool 的 pwn checksec 指令,可以簡單測啟用哪些保護

pwn checksec /bin/sh

透過checksec下去檢測有哪些保護啟用

gdb-peda$ checksec

輸出結果

CANARY    : disabled
FORTIFY   : disabled
NX        : ENABLED
PIE       : disabled
RELRO     : Partial

可以看到 NX 啟動,表示可能需要透過 ROP 的方式下去進行利用, 也可能是透過其他漏洞如格式化字串

另外參見 漏洞緩解技術

利用方法

x86 32 bit ROP

@plt
ret pop*2
parameter1
parameter2
@plt
ret pop*1
parameter1

下面兩個 parameter 要 pop 掉,如果後面還有 gadget 的話

工具

反組譯查看函數

objdump -M intel -d ./echo | less

透過 gdb 檢查流程

gdb ./echo

載入 gdb peda,不過現在比較建議使用 gef 還不錯

source /usr/share/peda/peda.py

註: 如 ida pro 與 ghidra 在某些情況下可能無法得出可以讀的程式碼,可以嘗試不同反編譯工具。

GEF 可以參考手冊 Home - GEF - GDB Enhanced Features documentation

符號與區段

顯示符號表

nm ./a.out

顯示區段

readelf -x .text ./a.out
objdump -s -j .text ./a.out

其他指令參見逆向

/proc/self/maps

pwntools 可以透過 Process.libs() 來取得 /proc/self/maps ,或許在除錯 PIE 的時候有用

注: 其他關於 proc 相關的可以參見文檔,想說怎麽不過設計在 proc 中

https://docs.pwntools.com/en/stable/util/proc.html

Shellcode

產生 shellcode

shellcode search arm
shellcode get 904

pwntools

我們可以使用 pwntools 的 pwn 一行產生 shellcode

pwn shellcraft sh | pwn disasm

熟悉 pwntools 方便編寫 shellcode ,如構造一個 echo 的 shellcode 如下

from pwnlib import shellcraft
shellcraft.echo('hello world!')

如果需要填充用 flat() 即可,比方說填充 buffer 有用,或者說前面需要擺放 /bin/sh 時可以用來填充一定長度。

from pwn import *
flat({0: 'ABC', 5: 'CBA'}, filler = b' ', length = 10)

參見官方文檔

大部分 shellcode 都必須編寫 ROP chain,可以採用 pwntools 的 ROP 輔助編寫

如果需要產生 jmp 的 shellcode,pwntools 的 asm() 是可以使用 label 的,這部分因為實際上 asm 只是去透過 gnu as 進行處理, 因此我們可以寫出如下的 code

asm("""
loop:
	nop
	nop
	nop
	jmp loop
""")

補齊

如16位元/bin///sh採用多個/來避免生成的shellcode含有\0字元

from pwn import *
context.arch='amd64'
pwn.asm('mov rax, 0x732f2f2f6e69622f')
pwn.asm('mov rax, 0x732f6e69622f')

Shellcode: Nop Sled

透過nop指令增加shellcode的命中機率, 在shellcode之前的記憶體放入大量的nop

除錯

ELF

利用方法

ret2csu

.fini.fini_array ,執行順序依序為 .fini_array[1], .fini_array[0], .fini

參見

ASLR繞過

利用漏洞找到 Return Address,並且找到相關如 libc.textstack 的 Base 用於利用。

利用方法

ROP

無法確定 libc 記憶體

無法確定 libc 記憶體,或者有啟用 ASLR 的時候,根據情況

請參見 Pwn学习笔记20:其他一些技术

pwntool 設定 base address

在 leak address 之後為了方便使用,會設置 base address

e = ELF('./a.out')
e.address = 0x400000

並且在使用 ROP 的時候會需要配置 base adderss

base = 0x400000
rop = ROP(e, base)

任意寫

絕對 Virtual Address

bss, data 這幾個 section 所屬的 segment 是可以任意寫

stack pivoting 到可控地址上。

相對 Virtual Address

技巧

PIE 取得 base address

https://tianstcht.github.io/pwntools的使用技巧/

如果是透過 ld.so 請透過以下指令

c.libs()[c.cwd + c.argv[3].decode(‘utf8’).strip(’.’)]

除錯技巧

採用GDB Script來避免重複的動作, 如:

  1. 啟動gdb時啟動peda
  2. peda start
  3. 一些script來找到目的位置。

為了檢驗payload是否正常運作而不斷重複以上步驟, 將導致效率低下。

如果採用 pwntools ,可以直接在呼叫 gdb.attach() 時執行指令,來增加效率 。1

反向除錯

參見除錯

查詢 libc 地址

在 gdb 可以透過以下指令查詢 libc 地址,以及連結的 libc

info proc mapping

參見[Why does ldd and (gdb) info sharedlibrary show a different library base address?]

在 python script 切換到 interact

因為大多數在解題的時候,會針對特定程式的 function 寫一個 python function 進行控制,部分題目可能不能輸入 ascii,可以在我們腳本中使用輔助送出結構化的封包。

import code
# ...
def do_something(a):
  c.sendline(a)

code.interact(local=locals())

查詢特定版本的 glibc

可以透過 此網站 來找特定版本的 glibc

計算 offset

指定 libc

pwntools 可以透過此方式指定使用的 libc,有 hooking 的意味,請務必注意 lib 前面的 ./

c = process('echo2', env={'LD_PRELOAD': './libc-2.23.so.x86_64'})
# 請務必修改 libc 名稱
c = process('echo2', env={'LD_LIBRARY_PATH': '.'})

shell 可以透過此指令

LD_PRELOAD=/tmp/libc-2.23.so.x86_64 ./a.out
LD_LIBRARY_PATH=. ./a.out

採用 LD_LIBRARY_PATH 時請務必修改 libc 的名詞,並且透過 ldd 檢查連結的 library。

mv libc_32.so.6 libc.so.6
LD_LIBRARY_PATH=. ldd ./seethefile

輸出會出現以下訊息,代表已經透過該目錄的 libc

libc.so.6 => ./libc.so.6

如果不修改名詞會導致載入系統上的 libc

問題

參考

指定 ld.so

關於 pwn 的時 libc 的坑的解決辦法

請參考 Handling SO Hell in CTF Pwn - BrieflyX’s Base

或者在 pwntool 之下,則可以參考以下方法。

pwn_file="./ld-2.29.so --library-path ./lib/ ./babyheap"
p = process(pwn_file.split())

參見於文章提到 starctf2019 的方法

注意: ELF 如果為 x86 ,則 ld, libc 都要為 x86,反之為 x64 都要採用 x64 版本的 ld, libc

採用此方法載入時如果要找到基準位置請改成

c.libs()[c.cwd + c.argv[3].decode('utf8').strip('.')]

危險函數

參見


CTF PWN