Before all
很感謝台灣好厲駭這次邀請了Angel Boy大大來講Windows Pwn這個神祕的課程領域,過去對於windows pwn的利用多半是HTB/THM上面的靶機,直接把linux的想法套上去就很常出bug,有這次的機會把我拉回來看底層的基本知識&理解真的很開心w
Basic Tools & Reverse
課程 or 之前看過ㄉ就放一放
只想再說一遍:~~Reverse不只是一隻龍或一個阿嬤的天下啊~~~
但這篇就先當作讀者會用ghidra/ida而且會linux stack pwn了 :D
PE-Bear
Link: https://github.com/hasherezade/pe-bear/releases/tag/v0.7.0
下載後直接丟進去就可以方便的瀏覽 strings/Header 等資料ㄌ,disasm還是交給idaㄅ
然後也可以很方便的改這些 Headers
DLL Export Viewer
把dll拖進去會dump所有調用的函數出來
拿Kernel32為例:
windbg & x64dbg
只能說,都是很棒的動態debugger,但我還不太會用
紀錄幾個windbg坑:
- 需要跳成管理員權限
- launch app 的時候選項開 debug(advance)
- breakpoint 可以設 condition:
1
bp 0xdeadbeef ".if @eax = 0xc0004321 {} .else {gc}"
winchecksec
https://github.com/trailofbits/winchecksec
有release下載,用法跟 linux 裡面的 checksec 一模一樣
rp
Windows上的ROP Gadget
Link: https://github.com/0vercl0k/rp
保護機制
老生常談啊 💤
- DEP:NX,可寫不可執行,可執行不可寫
- Security Cookie(GS):Stack Canary
- ASLR:地址隨機化,開了在Windows上面Stack和Heap每次都會變,但library裡的只有重開機會改變
- CFG:linker時先規範哪些函數可被調用
- ACG:禁止 allocated 一塊 r/w/x 的記憶體
- Child Process Policy:不可以開child process(也就是不能winExec(“cmd.exe”))
一些結構
DLL
動態連結的函式庫,可以縮解程式碼量(如果g++/gcc compile加上--static
flag就會把它包進去)
- ntdll.dll:包含windows原生地底層api,通常不會直接呼叫
- kernel32.dll:包含win32常被呼叫的函數
- kernelbase.dll:在Win7以後的版本把kernel32.dll重要的函數移到這邊
- msvcrt.dll/ucrtbase.dl:windows上的C語言函式庫
IAT
存放使用函數的指標陣列,在loading time時就會載入
題解 Write Up
Source: https://github.com/William957-web/Angel-Boy-Windows-Pwn-Holihigh/tree/main
題目架設我是下載了 Nmap(link),開啟ncat
然後:
1 | ncat -kvl 10001 -c .\bofeasy.exe |
就可以在 port 10001 nc/telnet 到目標了
WinMagic
很開心的打開Binary後發現有symbol
Source Code:
1 | //g++ -g -o Winmagic.exe .\Winmagic.cpp -fpermissive |
下斷點:
1 | bp get_flag |
接著進到get_flag後就不斷的按p
進到下一步
進到main+0x60
和main+0x67
後用r
去設定rax的value(CMP是對RAX)
改掉rax值
1 | r rax=0xddaa |
最後進到get_flag3就可以看到leak的資料惹w
bofeasy
先RECON
沒有開Canary,安心overflow
會給main address,overflow的size是48,如果要蓋掉ret address記得蓋8 bytes rbp
l33t函數:
注意到MOV和PUSH RBP都在main做過了,應該跳到 SUB RSP,0x20
這邊
距離算一下是 50
P.S. Windows要CRLF,自己改r.newline
Exploit.py
1 | from pwn import * |
ret2lib
整體流程為:取得IAT表位置->取得IAT表中某個函數的地址 -> 用它算出KERNEL32 base -> 跳回去
Source Code:
1 | //g++ -g -o ret2lib.exe ret2lib.cpp |
看的出來gets可以BOF,一開始就會給main地址並且允許你取得一個地址上的值
RECON:
沒有GS(Canary),但有開ASLR和NX,先用windbg觀察:
算一下
可以取得main-base偏移量是5253
接著透過PE-Bear的Optional Header找到IAT表OFFSET:
給了一些kernel32上的functions,如果可以取得上面的值就可以算出與kernel32的距離
像這樣 :D
取得我想要用的KERNEL32!GetLastErrorStub與KERNEL32的距離(88976)
接下來就是嘗試跳到WinExec上做RCE
這邊我用dll export viewer的結果算完全是錯的…
所以改用windbg的功能,lm後再kernel32上點兩下,就會有一堆function可以選
找到winexec就能算距離了
接著是overflow,開ghidra
padding大小是0x128,蓋成winexec後用windbg的lauch process功能追到這次執行會發現沒跑到winexec…
跟linux pwn時一些像system的函數一樣,這種函數在stack上要對齊0x10,所以塞一個return
使用main上面的ret(base_addr+0x1600/main+0x17b)
再追進去會發現一開始拿來當name的whale120
(我輸入的name)被當成rcx去跑winexec,是因為最後那個getname(&name)
的緣故,所以輸入cmd.exe\x00
即可getshell!
Exploit
1 | from pwn import * |
PWNED!!
ret2sc
目前ret2shellcode是壞掉的,改天再來補(實務上拿msfvenom改好像蠻方便 😅)
大致流程就是從 Export Directory Table 找到WinExec的RVA,然後把他抓出來打shell