Poetrier
ida 找 flag,這題給了一個假 flag,然後

我就以為是假 flag 的頭改成 is1ab{} 就可以提交了,然後我看 program 的邏輯是只要輸入的 string 與設定的不符,就會死掉,不存在其他 branch,所以這個一定就是真的 flag 但不知道為啥不能 submit,是後面被 hints 有「真正的」 Flag
我又跑去檢查了一下程式流程圖,發現只要 string 匹配錯誤就是 return,與我之前的觀察沒有差別,完全找不到 flag

ida 左邊看 function

然後就看到藏著 flag 的 function
Secret letter
寫的很長,可以直接往下滑看綠色框的內容,綠色框是解題邏輯,下面寫的是我的解題過程。剛剛 demo 發現其實有一些細節寫得不是很詳細,綠色方框下面我又補了實際操作
file
| |
有給 Source Code
Source code
| |
整體流程是 main() call letter(), letter() 接收127 bytes,然後接收兩個數字,分別為 num_1 and num_2,並且執行 fun[num_1] += num_2;
有趣的地方是這個 fun 是一個 pointer to int ,然後 index and value 可控,這樣就可以覆蓋到不應該覆蓋到的地方
c 語言的陣列其實是一種指標
- *fun[0] == fun
- *(fun + 1) == fun[1]
這邊先記著我們有一個方法可以任意地址覆寫,繼續 trace code,可以看到接下來 call 了 verify(number, strlen(number)); 而 verify 是一個 function pointer,指到的地方是 secert(),接下來看 secert() 的行為,目標很明確,我們需要讓 lucky_number(number, len) == 48763333 這個為真,才能印出 flag,那關鍵就會是 lucky_number()
可以知道 lucky_number() 是將我們第一次輸入的字符逐個與 0xfe xor 後放入 sum,並且 return sum,而這個 sum 要與 48763333 相等
看起來其實不難,就是回推數值,基本上可以手算,或者自動的一個一個 try ,不過我想節省時間所以先去算他的 boundary,發現 sum 最大的可能會出現在每個累加的 bytes 都是1,也就是 sum += number[i] ^ 0xfe ; 中的 number[i] ^ 0xfe always 為 0xff,0xff 等於十進制的255,然後 loop 最多127次,這樣 sum 的最大值為 255 * 127 = 32385,遠不及我們想要的48763333
好了,到這邊其實很明顯了,正常方式過不了,只能用到前面的任意位置複寫,然後要複寫的東西也很明顯,就是 verify() 的內容, ***verify()***是一個 function pointer,也就是 verify() 所存的 data 是 sercet() 的 address,那我們只要把地址複寫跳過檢查的這個動作就可以 x 了,複寫的值就是 sercet() + offset,加上這個 offset 就是為了跳過檢查(lucky_number(number, len) == 48763333)的 address,這個可以用 objdump -d 去看組語,pattern 應該很明顯應該是 cmp $register 48763333,需要注意的是 constant(48763333) 應該是 hex 表示,蓋掉之後就可以 bypass 驗證,拿到 flag 了
大致流程就是透過 fun[num_1] = num_2 達成任意位置複寫任意數值,fun[num_1] 要控成 verify() 的地址,然後使用 num_2 填入 sercet() bypass address,sercet() bypass address 的核心概念就是 lucky_number(number, len) == 48763333的下一條指令地址,基本上我們就是要 bypass 這個指令的檢查,從而得到 flag
流程是那樣,實際上你要找到 fun[0] 的 address,你需要把斷點下在 letter() (其實理論上哪裡都可以只要在 fun[num_1] = num_2 之前就行)

然後一直 ni 就可以看到
0x555555558080 <fun>

去看一眼
0x555555558080附近長怎樣,因為這是我們能複寫的地方
| |
第四行的
0x555555558050 <verify>: 0x00005555555551f0 0x0000000000000000存放 verify 所指向的 function 也就是 sercet(),很明顯這是我們要覆寫的位置,那我們要覆寫什麼東西呢?
| |
原本存的是 secret + 0 的 address 所以會從 secret + 0 執行,但我們想 bypass 檢查,也就是 call lucky number那邊,所以我們把 value 改成 secret + 79,他就會 bypass 掉檢查了,然後因為是 fun[num_1] += num_2 所以我們只要輸入 offset(這邊的話就是 79) 就可以了
| |
突然想到,這題其實是需要先解 .zip 密碼的,但這邊我的建議是

計算機
file
| |
checksec
| |
Decompile
main
| |
calcutator()
| |
goodbye()
| |
我 leak 出 libc 位置,串好 ROP 才發現有留後門(win)
win
| |
開 nx 沒 cannary,又看到 get(),差不多可以確定是 ROP,需要注意的是他需要一個參數,要記得pop 到 $rdi
exploit
| |
yummyyummy
file
| |
checksec
| |
Decompile
main
| |
heap 題,保護機制全開
16和55行只 free 不設 Null, Dangling pointer。存在 UAF,但16行才是重點,我原本的想法是把 *(ptr+1)設成 system,然後 v3 也就是第五次點餐的內容填成 /bin/sh,這樣就可以 RCE!
對,我又沒發現這題有 backdoor
這題挺麻煩的,因為 chunk 進到 tcatch bin 裡面的 data 會被 flush 掉,完全沒有頭緒,幸好後來學長跟我提醒說這題要用 ubuntu 16.04,為什麼?因為16.04沒有 tcatch
那這題其實就很簡單了
經典 fastbin UAF 問題,16行產生 Dangling pointer,chunk 會被 fastbin 管理,這時候我們只要去 malloc 一塊一樣大小的 chunk,然後透過 52行就可以蓋掉原本存在在 chunk 上面的
bye_func() address,填成 backdoor 的位置,57行在 call 的時候,就不會是 callbye_func(),而是 backdoor()
exploit
| |
因為有開 PIE 第一次 leak bye_func() address,然後去反組譯(看/算) process 的 memory base,加上 offset 算出 backdoor() address ,其實第三次之後的 send payload 可以去要更大塊的 memory,這樣就不會動到原本擺好的 payload
Backdoor
file
| |
checksec
| |
objdump -d
| |
三個 syscall,分別為印出(write)特定資訊,以及讀入(read) 0xdeadbeef 個 bytes
| |
注意到這邊 stack 並沒有被撐開(感謝你的注意,沒什麼用的資訊)
- ROPgadget –binary
–multibr
–multib Enable multiple branch gadgets
| |
objdump -d 有找到一組 syscall;ret 位置在
0x401165
這題是 SROP,核心概念是呼叫 syscall 前會把 $register 的值保存起來,然後進 kernal mode 執行,結束回到 user mode,把剛剛存起來的 $register pop 回去原本的位置,而我們就是透過修改保存那些 $register 的地方的 value 達到 ROP,那最簡單的方法就是 $rip 設成 system,然後 $rdi 設成 "/bin/sh" 但會遇到一些問題,一般不會有 "/bin/sh" 字串,沒有就自己送,送過去的 "/bin/sh 會在 stack 上,stack 在哪? 想辦法 leak stack address,觀察前面找出的 gadget 發現也沒辦法 leak 出 stack address
運氣很好的碰到熱心的學長,跟我說這題題如其名,他把我需要的東西藏在程式的某個地方
xxd 看一眼,就找到了
| |
exploit
| |
做簡報後發現自己當初解題時觀念一塌糊塗,當初能解出來真的全靠運氣
自己找不到 bin/sh,leak 不出 stack address
system call 回去 User space 會自動恢復上下文,然後自動恢復上下文這個操作又跟 sigreturn() 的操作一樣,所以我就以為是 system call 會使用 sigreturn() 恢復上下文
他都叫sigreturn()了,system call 怎麼會使用他,瞎貓碰上死耗子,亂串 gadget 剛好碰到 rax == 0xf 執行 sigreturn()
https://img-9gag-fun.9cache.com/photo/a4ROVQp_460sv.mp4
詳細的 SROP 在下面連結
https://docs.google.com/presentation/d/1_HD9hIJB6wXhQp-FjrdPy5sqB74DHabAkLG4jg46sLk/edit?usp=sharing