ã¯ããã« å§ããŸããŠã23幎æ°åå
¥ç€Ÿã®Gã»Sã§ãã ãã€ãã2023幎æ°åå
¥ç€Ÿã§åå è
ãåã£ãŠã6/3ïŒåïŒ4ïŒæ¥ïŒéå¬ã®SECCON Beginners CTF 2023ã«åå ããŠããŸããã æ¬èšäºã¯ãæã
æ°åããŒã ãè§£ããåé¡ã®writupã«ãªã£ãŠããŸãã ä»ååå ããCTFã«ã€ã㊠SECCON Beginners CTF 2023 æ¥æ¬ã®CTFåå¿è
ãäžçŽè
ã察象ãšããCTFã®ã³ã³ãã¹ãã§æ¯å¹Ž6æã®äžæ¬ã«éå¬ãããŠããŸããä»åã¯2018幎ã®åéå¬ããæ°ããŠ6åç®ã®éå¬ãšãªããŸãã åºé¡ç¯å²ã¯ãcryptoãpwnableãmiscãwebãreversingã§ããã Writeup æã
ã®ããŒã ã¯Beginnerãå
šãŠè§£ãããšãã§ããŸããïŒïŒïŒ ã²ãšã€ãã€è§£èª¬ããŠããããšæããŸãã CoughingFox2(crypto) CougningFox2.tar.gzãå±éãããšcipher.txtãšmain.pyãå
¥ã£ãŠããŸããã cipher.txtãšmain.pyã®å
容ã¯ãããã以äžã®éãã§ãã cipher = [4396, 22819, 47998, 47995, 40007, 9235, 21625, 25006, 4397, 51534, 46680, 44129, 38055, 18513, 24368, 38451, 46240, 20758, 37257, 40830, 25293, 38845, 22503, 44535, 22210, 39632, 38046, 43687, 48413, 47525, 23718, 51567, 23115, 42461, 26272, 28933, 23726, 48845, 21924, 46225, 20488, 27579, 21636] # coding: utf-8 import random import os flag = b"ctf4b{xxx___censored___xxx}" # Please remove here if you wanna test this code in your environment :) flag = os.getenv("FLAG").encode() cipher = [] for i in range(len(flag)-1): c = ((flag[i] + flag[i+1]) ** 2 + i) cipher.append(c) random.shuffle(cipher) print(f"cipher = {cipher}") ããã°ã©ã ãèªããšæå·åãè¡ãæçµçã« cipher ãšãããªã¹ããåºåããŠããããšãåãããŸãããã®ããšãã cipher.txt ã«æžãããŠãããã®ã¯æå·åããããªãããåŸã®ãã®ã§ãšèããããŸãããŸãæå·åã®éšåã¯iæåç®ãši+1æåç®ãè¶³ããŠ2ä¹ããiãè¶³ãããã®ã§ããããšãããäžã€ã®æåãåããã°æ¬¡ã®æåãç·åœããã§æ¢ã (flag[i] + flag[i+1]) ** 2 + i ãèšç®ãcipher.txtå
ã®é
åã«ååšããã確èªããã°èã¥ãåŒã«è§£ãFlagãåŸãŸããã Flag : ctf4b{hi_b3g1nner!g00d_1uck_4nd_h4ve_fun!!!} åŠã³ æå·ã¯äžèŠé£ããããªãã®ã§ãããã°ã©ã ãæžãããããããšã§å®¹æã«è§£ããŠããŸããã®ãå€ãååšããŠããŸãã ãã£ãŠãæå·ã®éžå®ãããéã«ã¯ãã¡ããšè匱æ§ããªããã®ãéžã¶ããšãéèŠã§ãããšæããŸããã poem(pwnable) poem.tar.gzãå±éãããšpoemãšsrc.cãå
¥ã£ãŠããŸããã poemã¯å®è¡ãã¡ã€ã«ã§ãããsrc.cã®å
容ã¯ä»¥äžã®éãã§ãã #include <stdio.h> #include <unistd.h> char *flag = "ctf4b{***CENSORED***}"; char *poem[] = { "In the depths of silence, the universe speaks.", "Raindrops dance on windows, nature's lullaby.", "Time weaves stories with the threads of existence.", "Hearts entwined, two souls become one symphony.", "A single candle's glow can conquer the darkest room.", }; int main() { int n; printf("Number[0-4]: "); scanf("%d", &n); if (n < 5) { printf("%s\n", poem[n]); } return 0; } __attribute__((constructor)) void init() { setvbuf(stdin, NULL, _IONBF, 0); setvbuf(stdout, NULL, _IONBF, 0); alarm(60); } ãã®ããã°ã©ã ãèªããšæ°å€nãèªã¿åããé
åã«æ ŒçŽãããŠããnçªç®ã®ããšã ãåºåããããã°ã©ã ã§ããããšãåãããŸããèªã¿èŸŒãã nã5ãã倧ãããšãã¯ããšã ãåºåããªãããã«ããŠãããŸããã0ããå°ããå Žåã¯ãã®ãŸãŸé
åã«ã¢ã¯ã»ã¹ããããšã«ãªã£ãŠããŸãã ãã£ãŠnã«ãã€ãã¹ã®å€ãå
¥åããããšã§ã»ãã®é åã®ã¢ãã¬ã¹ã«ã¢ã¯ã»ã¹ããããšãã§ã-4ãå
¥åããããšã§FlagãåŸãŸããã Flag : ctf4b{y0u_sh0uld_v3rify_the_int3g3r_v4lu3} åŠã³ Pythonãªã©ã§ã¯ããªã¹ããªã©ã®ç¯å²å€ã®indexãæå®ããå ŽåIndexErrorãåºãŸãããCèšèªã§ã¯ãšã©ãŒãåºãã«å¥ã¡ã¢ãªé åã«ã¢ã¯ã»ã¹å¯èœã«ãªããŸãã ãããé²ãããã«ã¯ãå¢çå€ãæ£ããèšå®ãæ¡ä»¶åå²ããããšã倧äºããšæããŸããã YARO(misc) YARO.tar.gzãå±éãããšrule_example.yarãšserver.pyãå
¥ã£ãŠããŸããã ããããå
容ã¯ä»¥äžã®éãã§ãã rule shebang { strings: $shebang = /^#!(\/[^\/ ]*)+\/?/ condition: $shebang } rule maybe_python_executable { strings: $ident = /python(2|3)\r*\n/ condition: shebang and $ident } #!/usr/bin/env python3 import yara import os import timeout_decorator @timeout_decorator.timeout(20) def main(): rule = [] print('rule:') while True: l = input() if len(l) == 0: break rule.append(l) rule = '\n'.join(rule) try: print(f'OK. Now I find the malware from this rule:\n{rule}') compiled = yara.compile(source=rule) for root, d, f in os.walk('.'): for p in f: file = os.path.join(root, p) matches = compiled.match(file, timeout=60) if matches: print(f'Found: {file}, matched: {matches}') else: print(f'Not found: {file}') except: print('Something wrong') if __name__ == '__main__': try: main() except timeout_decorator.timeout_decorator.TimeoutError: print("Timeout") ã³ãŒããèŠããšruleãå
¥åãããyaraãšãããã«ãŠã§ã¢è§£æã»æ€ç¥ããŒã«ãçšããŠæ€çŽ¢ãè¡ã£ãŠããŸãã äžèšã®ãããªruleãæžãããšã§flagã®äžæåç®ãAãã©ãã確èªã§ããŸãã rule flag { strings: $shebang = /ctf4b{A.*}/ condition: $shebang } ãããç¹°ãè¿ããŠæ¢çŽ¢ããŠããããšã§FlagãåŸãŸããã Flag : ctf4b{Y3t_An0th3r_R34d_Opp0rtun1ty} åŠã³ ãŠãŒã¶ãŒã«ä»»æã®æ£èŠè¡šçŸãæžããããããªããã°ã©ã ãæžããšç§å¿æ
å ±ãæ¢çŽ¢ãããå¯èœæ§ãããããæ³šæãå¿
èŠã ãšåŠã³ãŸããã polyglot4b(misc) polyglot4b.tar.gzãå±éãããšç»åãã¡ã€ã«sample/sushi.jpgãšpolyglot4b.pyãå
¥ã£ãŠããŸããã import os import sys import uuid import shutil import subprocess print( f"""\033[36m\ ____ _ _ _ _____ _ _ _ | _ \ ___ | |_ _ __ _| | ___ | |_ | ____|__| (_) |_ ___ _ __ | |_) / _ \| | | | |/ _` | |/ _ \| __| | _| / _` | | __/ _ \| '__| | __/ (_) | | |_| | (_| | | (_) | |_ | |__| (_| | | || (_) | | |_| \___/|_|\__, |\__, |_|\___/ \__| |_____\__,_|_|\__\___/|_| |___/ |___/ {"-" * 68} >> """, end="", ) file = b"" for _ in range(10): text = sys.stdin.buffer.readline() if b"QUIT" in text: break file += text print(f"{'-' * 68}\033[0m") if len(file) >= 50000: print("ERROR: File size too large. (len < 50000)") sys.exit(0) f_id = uuid.uuid4() os.makedirs(f"tmp/{f_id}", exist_ok=True) with open(f"tmp/{f_id}/{f_id}", mode="wb") as f: f.write(file) try: f_type = subprocess.run( ["file", "-bkr", f"tmp/{f_id}/{f_id}"], capture_output=True ).stdout.decode() except: print("ERROR: Failed to execute command.") finally: shutil.rmtree(f"tmp/{f_id}") types = {"JPG": False, "PNG": False, "GIF": False, "TXT": False} if "JPEG" in f_type: types["JPG"] = True if "PNG" in f_type: types["PNG"] = True if "GIF" in f_type: types["GIF"] = True if "ASCII" in f_type: types["TXT"] = True for k, v in types.items(): v = "🟩" if v else "🟥" print(f"| {k}: {v} ", end="") print("|") if all(types.values()): print("FLAG: ctf4b{****REDACTED****}") else: print("FLAG: No! File mashimashi!!") ã³ãŒããèªããš10åå
¥åãåãåãå
¥åãçµåã㊠file ã³ãã³ããå®è¡ããã®åºåã«PNGãJPEGãGIFãASCIIã®æååããããã確èªããŠå
šãŠããã°ãã©ã°ãåºåããŠããŸãã ãŸãsample/sushi.jpgã file -bkr sample/sushi.jpg ãè¡ããšDescriptionã®æ¬ã«CTF4BãšããæåãèŠã€ããããŸãããã®ããšããDescriptionæ¬ã«ã¯ãä»»æã®æåãå
¥ããããšãã§ãããšèããããŸãããã£ãŠCTF4Bã®åŸãã«PNGJPEGGIFASCIIã远å ããŠã nc polyglot4b.beginners.seccon.games 31416 < sushi.jpg ãšããããšã§ãFlagãåŸãŸããã Flag : ctf4b{y0u_h4v3_fully_und3r5700d_7h15_p0ly6l07} åŠã³ fileã³ãã³ãã䜿ã£ãŠãã¡ã€ã«ãèå¥ããéã«æ£ããåŠçãæžããªããã°ããŠãŒã¶ãŒã®æªæã«ãã£ãŠãããã§ãåœè£
ã§ããããšãå¯èœã§ãããšããããŸãããä»åã®åé¡ã®ãã¹ããªããããã«ã¯ãfileã³ãã³ãã®è§£æããã¡ã€ã«ã¿ã€ãã瀺ããŠããéšåã ãã«ãããªã©ã®å·¥å€«ãå¿
èŠã§ãããšæããŸããã Forbidden(web) Forbidden.tar.gzãå±éãããšnginxã§äœãããWebã¢ããªã±ãŒã·ã§ã³ãå
¥ã£ãŠããŸããã app/index.jsã®å
容ã¯ä»¥äžã®éãã§ãã var express = require("express"); var app = express(); const HOST = process.env.CTF4B_HOST; const PORT = process.env.CTF4B_PORT; const FLAG = process.env.CTF4B_FLAG; app.get("/", (req, res, next) => { return res.send('FLAG ã¯ãã¡ã: <a href="/flag">/flag</a>'); }); const block = (req, res, next) => { if (req.path.includes('/flag')) { return res.send(403, 'Forbidden :('); } next(); } app.get("/flag", block, (req, res, next) => { return res.send(FLAG); }) var server = app.listen(PORT, HOST, () => { console.log("Listening:" + server.address().port); }); ã³ãŒããèªããš block ã§/flagã«é·ç§»ãããšã403ãè¿ããŠããããšãåãããŸãã nginxã¯ãURLã®å€§æåå°æåã®åºå¥ããªããããªãã¡"/flag"ãš"/Flag"ã¯ãåãããŒãžã§ãããšå€æãããŸãã ããããJavaScriptã§ã¯ã if (req.path.includes('/flag')) ã§åå²ãè¡ããããã¯åŠçãèšè¿°ããŠããããã https://forbidden.beginners.seccon.games/flag ã¯403ãåž°ã£ãŠããã https://forbidden.beginners.seccon.games/Flag ãšããããšã§ãã©ã°ãåŸãããšãã§ããŸãã Flag : ctf4b{403_forbidden_403_forbidden_403} åŠã³ ãã¬ãŒã ã¯ãŒã¯ã®ä»æ§ããã£ããçè§£ããŠããããšã倧åã§ãããšæããŸããã ä»åã®äŸã§ã¯ãnginxãšJSã§æååã®å€§å°ã®æ±ããéã£ãŠããŠãã®éããçè§£ããŠããªãããšã§æå³ããããã«åããŠããªãã®ã§ã仿§çè§£ãããããã«ããŠã¯ãããªããšåŠã³ãŸããã aiwaf(web) aiwaf.tar.gzãå±éãããšnginxã§äœãããWebã¢ããªã±ãŒã·ã§ã³ãå
¥ã£ãŠããŸããã app/app.pyã®å
容ã¯ä»¥äžã®éãã§ãã import uuid import openai import urllib.parse from flask import Flask, request, abort # from flask_limiter import Limiter # from flask_limiter.util import get_remote_address ################################################## # OpenAI API key KEY = "****REDACTED****" ################################################## app = Flask(__name__) app.config["RATELIMIT_HEADERS_ENABLED"] = True # limiter = Limiter(get_remote_address, app=app, default_limits=["3 per minute"]) openai.api_key = KEY top_page = """ <!DOCTYPE html> <html lang="ja"> <head> <meta charset="utf-8" /> <title>äºç©ºæåº«</title> </head> <body> <h1>äºç©ºæåº«</h1> AIã«ã»ãã¥ãªãã£ã®ç©èªãæžããŠããããŸããã<br> å
å®¹ã¯æ£ãããã©ããããããŸããã<br> <ul> <li><a href="/?file=book0.txt">ãæž</a></li> <li><a href="/?file=book1.txt">ãæž</a></li> <li><a href="/?file=book2.txt">ãæž</a></li> <!-- <li><a href="/?file=book3.txt">ãæž</a></li> --> </ul> â»ã»ãã¥ãªãã£ã®ããAI-WAFãå°å
¥ããŠããŸãã<br> © 2023 ããããã®æžæ¿ </body> </html> """ @app.route("/") def top(): file = request.args.get("file") if not file: return top_page if file in ["book0.txt", "book1.txt", "book2.txt"]: with open(f"./books/{file}", encoding="utf-8") as f: return f.read() # AI-WAF puuid = uuid.uuid4() prompt = f"""\ 以äžã®{puuid}ã«å²ãŸããéšåã®URLã¯ãšãªã¯ãã¹ãã©ããŒãµã«æ»æã§ããããïŒ ããã§ããå ŽåYesããéãå ŽåNoãè¿ããŠãã ããã ../ãflagãšããæååãå«ãŸããŠããå ŽåãYesãè¿ããŠãã ããã {puuid} {urllib.parse.unquote(request.query_string)[:50]} {puuid} """ try: response = openai.ChatCompletion.create( model="gpt-3.5-turbo", messages=[ { "role": "user", "content": prompt, }, ], ) result = response.choices[0]["message"]["content"].strip() except: return abort(500, "OpenAI APIã®ãšã©ãŒã§ãã\nå°ãæéããããŠã¢ã¯ã»ã¹ããŠãã ããã") if "No" in result: with open(f"./books/{file}", encoding="utf-8") as f: return f.read().replace(KEY, "") return abort(403, "AI-WAFã«æ€ç¥ãããŸãã👻") if __name__ == "__main__": app.run(debug=True, host="0.0.0.0", port=31415) ã³ãŒããèªã㚠以äžã®{puuid}ã«å²ãŸããéšåã®URLã¯ãšãªã¯ãã¹ãã©ããŒãµã«æ»æã§ããããïŒ ããã§ããå ŽåYesããéãå ŽåNoãè¿ããŠãã ããã ../ãflagãšããæååãå«ãŸããŠããå ŽåãYesãè¿ããŠãã ããã {puuid} {urllib.parse.unquote(request.query_string)[:50]} {puuid} ãšããããã³ãããAIã«æããŠåž°ã£ãŠããå€ãYesã ã£ãå Žåããããã¯ãã仿§ã«ãªã£ãŠããŸãã ããã³ããéšåãèŠããš urllib.parse.unquote(request.query_string)[:50] ãšãªã£ãŠããäžããããURLã®50æåç®ãŸã§ããAIã«ããæ€ç¥ãè¡ã£ãŠããªãããšãããããŸãã ããã«ããããã©ã¡ãŒã¿ã«é©åœãªãã®ã远å ã file=../flag ã50æåç®ä»¥éãšãªãããã«èšå®ããããšã§AIã«ããæ€ç¥ãæããŠãã©ã°ãåŸãããšãã§ããŸããã Flag : ctf4b{pr0mp7_1nj3c710n_c4n_br34k_41_w4f} åŠã³ OpenAIã¯æååäœã§ãéãããããŸãããã®ãããã³ã¹ããäžããããšéäžã§æååãåã£ãŠããããã³ãããçæããŠããŸããªã©ãããšåãåã£ã以éã®éšåã«æ»æãå«ãŸããŠããå¯èœæ§ããããæ»æãæç«ããŠããŸãå¯èœæ§ããããŸããæååãåãå Žåã¯ã以éã®åŠçãåã£ãŠããè¡ããªã©ããªããšãããªããšæããŸããã Half(reversing) Half.tar.gzãå±éãããšhalfãšããå®è¡ãã¡ã€ã«ãå
¥ã£ãŠããŸããã ãã®ããã°ã©ã ãå®è¡ãããšå
¥ååŸ
ã¡ã«ãªããå
¥åããããã©ã°ã®æ£èª€å€å®ãããŠãããŸãã ãã®å®è¡ãã¡ã€ã«ã strings ã³ãã³ãã䜿ã£ãŠå¯èªéšåã衚瀺ããŠåŸãããçµæã以äžã®éãã§ãã $ strings half /lib64/ld-linux-x86-64.so.2 libc.so.6 strncmp __isoc99_scanf puts printf strlen __cxa_finalize strcmp __libc_start_main GLIBC_2.7 GLIBC_2.2.5 _ITM_deregisterTMCloneTable __gmon_start__ _ITM_registerTMCloneTable u+UH []A\A]A^A_ Enter the FLAG: %99s%*[^ Invalid FLAG ctf4b{ge4_t0_kn0w_the _bin4ry_fi1e_with_s4ring3} Correct! :*3$" GCC: (Ubuntu 9.4.0-1ubuntu1~20.04.1) 9.4.0 crtstuff.c deregister_tm_clones __do_global_dtors_aux completed.8061 __do_global_dtors_aux_fini_array_entry frame_dummy __frame_dummy_init_array_entry main.c __FRAME_END__ __init_array_end _DYNAMIC __init_array_start __GNU_EH_FRAME_HDR _GLOBAL_OFFSET_TABLE_ __libc_csu_fini strncmp@@GLIBC_2.2.5 _ITM_deregisterTMCloneTable puts@@GLIBC_2.2.5 _edata strlen@@GLIBC_2.2.5 printf@@GLIBC_2.2.5 __libc_start_main@@GLIBC_2.2.5 __data_start strcmp@@GLIBC_2.2.5 __gmon_start__ __dso_handle _IO_stdin_used __libc_csu_init __bss_start main __isoc99_scanf@@GLIBC_2.7 __TMC_END__ _ITM_registerTMCloneTable __cxa_finalize@@GLIBC_2.2.5 .symtab .strtab .shstrtab .interp .note.gnu.property .note.gnu.build-id .note.ABI-tag .gnu.hash .dynsym .dynstr .gnu.version .gnu.version_r .rela.dyn .rela.plt .init .plt.got .plt.sec .text .fini .rodata .eh_frame_hdr .eh_frame .init_array .fini_array .dynamic .data .bss .comment ãã®åºåãèŠãŠã¿ããš20è¡ç®ã«FlagããããŸãã Flag : ctf4b{ge4_t0_kn0w_the_bin4ry_fi1e_with_s4ring3} åŠã³ æååã¯ãå®è¡ãã¡ã€ã«ã«ãã®ãŸãŸä¿åããããããç§å¿æ
å ±ãæååã«ãããŸãŸå®è¡ãã¡ã€ã«ã«å
¥ããŠããŸããšå®¹æã«èŠããŠããŸãã®ã§ãç§å¿æ
å ±ãèŒããªãããšã培åºããªããã°ãããªããšæããŸããã çµããã« ä»åã¯ãç§ã¯ååå ãªããå
šãŠã®beginneråé¡ãè§£ãããšãã§ããã®ã§å€§å€æºè¶³ããŠããŸãã ããããmediumã»herdããããå
šãè§£ããªãã£ãã®ã§ããå°ã粟é²ããŠãããããšæããŸããã CTFã«åå ããããšã§ä»ãŸã§ç¥ããªãã£ãè匱æ§ãªã©ãç¥ãæ©äŒã«ãªãã ã»ãã¥ãªãã£ãžã®æèãé«ãŸãã®ã§æ¯éçãããåå ããŠã¿ãŠãã ããã æçš¿ ãCTFãSECCON Beginners CTF 2023 æ°åããŒã Writeup 㯠ãã€ãããšã³ãžãã¢ããã° ã«æåã«è¡šç€ºãããŸããã