æ¬è¨äºã¯2024å¹´10æ5æ¥ï¼åï¼ã«AlpacaHackä¸ã§éå¬ããããAlpacaHack Round 4(Rev)ä¸ã§åºé¡ããã"Simple Flag Checker"ã¨ããåé¡ã®writeupã§ãã
AlpacaHackã¯å人æ¦ã®çæéCTFãç¶ç¶ãã¦éå¬ãã¤ã¤ããããã®åé¡ã常è¨ãããã¨ãç®æ¨ã¨ããæ°ããCTFãã©ãããã©ã¼ã ã§ããAlpacaHackã§éå¬ãããåã©ã¦ã³ãã¯1ã¸ã£ã³ã«ããã¼ãã¨ãã¦ãã¦ãä»åã®AlpacaHack Round 4(Rev)ã¯Reversingããã¼ãã¨ããã©ã¦ã³ãã§ãããç§ã¯æ¬Roundã«2åã®åé¡ãæä¾ããä»å解説ããåé¡ã§ãã"Simple Flag Checker"ã¯ãã®ãã¡ã®ä¸ã¤ã§ãã
ãã®åé¡ã¯ãã®åã®éã*1ãã¤ããªèªä½ã¯ã·ã³ãã«ãªåé¡ã§ãããããã¾ã åé¡ãè¦ã¦ããªãæ¹ã¯ããã®å ãèªãåã«10åã»ã©ã§ãããã®ã§ãã¤ããªãçºãããã¨ããããããã¾ããæ¬writeupã¯åé¡ãè¦ã¦ããªã人ã§ããããããã«æ¸ããã¤ããã§ã¯ããã¾ããããèªåã§å¤å°ã§ã解æ³ãèããã¨ãã®å ã®è©±ãé ã«å ¥ãããããªãã¯ãã§ãã
ä»åã®ã©ã¦ã³ãã¯Reversingã®ååã¨ãããã¨ããããæ§ã ãªReversingã®ã¨ãã»ã³ã¹ãè©°ãè¾¼ãã ã»ããã«ãããã¨èãã¦ãã¾ããããã®åé¡ã¯ããã³ã³ãã¤ã«ãéã¢ã»ã³ããªãèªãã ããReversingã§ã¯ãªããã¨ããã¡ãã»ã¼ã¸ãããã¦ãã¦ããã¤ããªã®åç解æã®ææ³ãå¦ã¶è¶³æããã«ãªããããªåé¡ã¨ãã¦ä½åãã¾ãããæ¬writeupã§ã¯ãåç解æã®ã¢ããã¼ããããã«ç¨ããéå ·ã®å¤æ§ããæãåã£ã¦ããã ããããã·ã³ãã«ãªè§£æ³ããå°ãæ»ã£ã解æ³ã¾ã§æ§ã ãªããªã¨ã¼ã·ã§ã³ã®è§£æ³ãç´¹ä»ãã¾ãã
ããã§ç´¹ä»ãã解æ³ãå®è£ ããã½ã«ãåã³åé¡èªä½ã®ã½ã¼ã¹ã³ã¼ãã¯ã以ä¸ã®GitHubãªãã¸ããªã§å ¬éãã¦ãã¾ãã
- åé¡æ¦è¦
- 解æ³
- ltraceãç¨ãã¦é¢æ°å¼ã³åºããããã¯ããè¿ãå¤ãå¾ã
- LD_PRELOADãç¨ãã¦memcmpãå·®ãæ¿ãã
- memcmpãputsã«ç½®ãæããåã«ã¼ãã§ã®hashãå¾ã
- gdbã§memcmpã®è¿ãå¤ãå¾ã
- updateãèªç±ã«å®è¡ã§ãããã¤ããªãä½ã
- ctypes.CDLLãç¨ãã¦Pythonããupdateãå®è¡ãã
- angrã¨Unicornãç¨ãã¦ãã¤ããªãã¨ãã¥ã¬ã¼ããã
- ãããã«
åé¡æ¦è¦
åé¡ã®æ¦è¦ã¨è§£æ³ã®å ±éããé¨åã«ã¤ãã¦ã軽ã触ãã¦ããã¾ãããã®åé¡ã¯x86-64ã®ELFå®è¡ãã¡ã¤ã«ãflag checkerã¨ãã¦ä¸ãããããããåçããflagãæ±ããåé¡ã§ããmainé¢æ°ã¯Ghidraãªã©ãç¨ããã¨ç°¡åã«ãã³ã³ãã¤ã«ãããã¨ãã§ãã以ä¸ã®ãããªå¦çã¨ãªã£ã¦ãããã¨ã確èªã§ããã¨æãã¾ãã
#define FLAG_LEN 49 #define HASH_LEN 36 char table[FLAG_LEN][16] = { FLAG_DATA }; int main() { char buf[FLAG_LEN + 1]; printf("flag? "); fgets(buf, FLAG_LEN + 1, stdin); char hash[HASH_LEN] = {0}; int correct = 1; for (int i = 0; i < FLAG_LEN; i++) { update(hash, buf[i]); correct &= memcmp(hash, table[i], 16) == 0; } if (correct) { printf("Correct! Your flag is: %s\n", buf); return 0; } else { puts("Wrong..."); return 1; } }
è¦ã¦åããã¨ããããã®ããã°ã©ã ã¯ããã·ã¥ãä¸æåæ¯ã«updateããªãããã®ç¶æ
ãtableã«æ ¼ç´ããããã¤ãåã¨æ¯è¼ãããã¹ã¦ã®æåã«ã¤ãã¦åã£ã¦ãããªãã°å
¥åãåçããã¨ããåä½ããã¦ãã¾ããããããmainé¢æ°ããå¼ã°ãã¦ããupdateé¢æ°ã¯ç¬èªã®hashé¢æ°ãå®è£
ãã¦ãããæé©åãããã£ã¦ãããã¨ãè¬ã®SIMDå½ä»¤ããããã¨ãç¸ã¾ã£ã¦ã¨ã¦ãéçã«åä½ã解æãããè¦ãç®ã¯ãã¦ãã¾ããããããã¨ãã¦ã36byteããç¶æ
ã®å
é 16byteããæ¯è¼ãã¦ããªãããã«table[i]
ã¨table[i+1]
ããupdateã«ç¨ããããcharãä¸æã«å®ãããã¨ã¯ã§ãã¾ããã
âæãããã¿ã«ãªã£ã¦ãããã§ãããæãããã¿ã®ãã¿ã³ãåºãªãã¦å°ã£ã¦ãã¾ã ãããã«ããã¦ããã¾ãã
ⶠä¸æã«å®ã¾ããªãç´æçãªçç±
ç¾å¨è§£ãããåé¡ã¯ããupdateé¢æ°ã«æ¸¡ãããå¼æ°ã¨æ´æ°ãããç¶æ ã®å é 16 byteãç¹å®ã®å¤ã¨ãªã(å¼æ°, æ´æ°ãããç¶æ , c)ã®çµãè¦ã¤ãããã§ããupdateãhashé¢æ°ã®è«¸æ§è³ªï¼æ´æ°ãããç¶æ ãåæå¤éææ§ããããhashã¯ã©ã³ãã ã«åå¸ããï¼ãæºãããªãã°*2ããã®åé¡ã®è§£ã¯ä¸æã«å®ãããã¨ãã§ããªãå¯è½æ§ãé常ã«é«ãã§ãã
ã¾ããcãä»»æã®å¤ã«åºå®ããã¨ãã«æ¡ä»¶ãæºãããããªå¤ã®çµãåå¨ããããèãã¾ããcããæ£è§£ãã®å¤ã«åºå®ããå ´åã¯åå¨ãããã¨ã¯æããã§ãããããã§ãªãå ´åã¯ã©ãã§ããããï¼
ç´æçã«ã¯ãå¼æ°ã®å¾å20 byteãèªç±ã«èª¿æ´ãã¦ãããªãã°æ´æ°ãããç¶æ
ã®å
é 16 byteãä»»æã®å¤ã«ãããã¨ã¯ã»ã¨ãã©ã®å ´åã«ã§ãããã§ããããå°ãç ããè¨ãæ¹ãããã¨ããã°ãã256²â°åã¬ãã£ãåãããªãã1/256¹â¶ç¨åº¦ã®å½ããã¯ã»ã¼ç¢ºå®ã«å¼ããã ãããã¨ãã£ãã¨ããã§ããããã
éã«è¨ãã°ãhashèªä½ã20 byteç¨åº¦ã§èªç±ã«èª¿æ´ã§ããç®æã4 byteç¨åº¦ã ã£ãå ´åã¯ãï¼å¹ççã«ã§ãããã¯å¥åé¡ã§ããï¼é«ã確çã§ä¸æã«å®ãããã¨ãå¯è½ããã§ãã
ã§ã¯è§£ããªãâ¦â¦ãã¨ããã¨ãããªãã¨ã¯ãªããflagãä¸æåãã¤ç¹å®ãã¦ãããã¨ãå¯è½ã§ãããã®åé¡ã§ã¯memcmpãä¸æåãã¤è¡ããã¦ãã¦ããã©ã°ãä¸è´ãã¦ããæåã¾ã§ã®memcmpå¼ã³åºãã¯ã¼ããè¿ãããã以éã¯éã¼ããè¿ãã¾ãããã®ããããã®åé¡ã解ãããã«ã¯memcmpã®æ»ãå¤ãmemcmpã«æ¸¡ãhashãåå¾ã§ããã°ããã§ãã
解æ³
ltraceãç¨ãã¦é¢æ°å¼ã³åºããããã¯ããè¿ãå¤ãå¾ã
ltraceã¯ãã¤ããªãè¡ãé¢æ°å¼ã³åºããããã¯ãã¦è¿½è·¡ããå¼ã°ããé¢æ°ãå¼æ°ãè¿ãå¤ã¨å ±ã«stderrã«åãåºãã¦ããã便å©ãªããã°ã©ã ã§ãã以ä¸ã¯ãltraceãç¨ãã¦checkerãã¤ããªã«é©å½ãªãã©ã°ãä¸ãã¤ã¤åããããã®ã§ãã
$ echo Alpaca{fakeflag} | ltrace ./checker > /dev/null __printf_chk(1, 0x610210a31004, 0x7fff58bfecc8, 0x610210a32da0) = 6 fgets("Alpaca{fakeflag}\n", 50, 0x76120a81aaa0) = 0x7fff58bfeb40 memcmp(0x7fff58bfeb10, 0x610210a33020, 16, 9) = 0 memcmp(0x7fff58bfeb10, 0x610210a33030, 16, 9) = 0 memcmp(0x7fff58bfeb10, 0x610210a33040, 16, 9) = 0 memcmp(0x7fff58bfeb10, 0x610210a33050, 16, 9) = 0 memcmp(0x7fff58bfeb10, 0x610210a33060, 16, 9) = 0 memcmp(0x7fff58bfeb10, 0x610210a33070, 16, 9) = 0 memcmp(0x7fff58bfeb10, 0x610210a33080, 16, 9) = 0 memcmp(0x7fff58bfeb10, 0x610210a33090, 16, 9) = 105 memcmp(0x7fff58bfeb10, 0x610210a330a0, 16, 9) = 8 ... memcmp(0x7fff58bfeb10, 0x610210a33310, 16, 9) = 0xfffffff3 memcmp(0x7fff58bfeb10, 0x610210a33320, 16, 9) = 57 puts("Wrong...") = 9 +++ exited (status 1) +++
mainé¢æ°å ã§ã®memcmpã®å¼æ°ã¨è¿ãå¤ãåãã¦ãããä»åã®å ´åã¯Alpaca{ã«è©²å½ããé¨åã¾ã§0ãè¿ã£ã¦ãã¦ããã®ãåããã¯ãã§ãããããç¨ãã¦ãã©ã°ãä¸æåãã¤ç¢ºå®ããã¦ããã¹ã¯ãªããã以ä¸ã«ç¤ºãã¾ãã
import subprocess from tqdm import tqdm from extract_data import FLAG_LEN def check(flag): res = subprocess.run(["ltrace", "./checker"], input=flag + "\n", capture_output=True, text=True).stderr iter = 0 for l in res.splitlines(): if not l.startswith("memcmp"): continue _, retval = l.split("=") if retval.strip() != "0": return iter iter += 1 return iter flag = "" for i in tqdm(range(FLAG_LEN)): candidates = [] for c in range(0x20, 0xff): if check(flag + chr(c)) == i + 1: candidates.append(chr(c)) assert len(candidates) == 1, candidates flag += candidates[0] print(flag)
ä»åã®åé¡ã§ã¯ããããæ³å®è§£ã§ãã*3ãltraceã¯ããã®ããã«ç´æ¥çã«åé¡ã解ããã以å¤ã«ãããããã®ãã¤ããªã®åä½ãææ¡ããããã«ãç¨ãããã¨ãã§ãã¾ãã-l
ãªãã·ã§ã³ã§å¼ã°ããé¢æ°ã®ãã£ã«ã¿ã¼ãå¯è½ã§ãããæå¤ã¨ç¥ããã¦ãã¾ããã-x
ãªãã·ã§ã³ãç¨ããã°ãã¤ããªå
ã®é¢æ°å¼ã³åºããããã¯ã§ããããã«ãªãã¾ããä½ããã£ã¦ãããä¸ç®ã§ã¯ããããªããã¤ããªã§ã¯ãã·ã¹ãã ã³ã¼ã«ãããã¯ããstraceãltraceãåã¾ãã¦åä½ãæ¦è¦³ãããã¨ãé¸æè¢ã¨ãã¦æã£ã¦ããã¨ããããããã¾ããã
LD_PRELOAD
ãç¨ãã¦memcmpãå·®ãæ¿ãã
å
ç¨ã¯memcmpã®å¼ã³åºããããã¯ãã¾ããããããã«ä¸æ©é²ãã§memcmpãèªåã§ç¨æããé¢æ°ã«ãã¦ãã¾ãã¾ããããä»åã®ææ³ã§ã¯ãmemcmpãåçãªã³ã¯ããã¦ãããã¨ãç¨ãã¾ããåçãªã³ã¯ããã³åçãªã³ã«ã«ã¤ãã¦ã¯ããã§ã¯è©³è¿°ãã¾ãããã端çã«è¨ããªãã°å®è¡ãã¡ã¤ã«ã®å¤ã«ããé¢æ°ãé¢æ°åããåçã«ãã¼ãããä»çµã¿ãåçãªã³ã¯ã§ããããå®ç¾ãã¦ãããã¤ããªãåçãªã³ã«ã§ããLinuxã§ç¨ãããã¦ããåçãªã³ã«ã§ããld.soã«ã¯ãªã³ã¯ã®è©³ç´°ãå¶å¾¡ããæ§ã
ãªãªãã·ã§ã³ãåå¨ãã¦ãã¦ããã®ãã¡ã®ä¸ã¤ã§ããLD_PRELOAD
ç°å¢å¤æ°ãä»åç¨ãã¾ãã
LD_PRELOAD
ç°å¢å¤æ°ã«shared objectãã¡ã¤ã«ã¸ã®ãã¹ã渡ãã¨ãåçãªã³ã¯æã«shared objectå
ã«å«ã¾ããé¢æ°ãä»ã®ã©ã®é¢æ°ãããåªå
ãã¦ãªã³ã¯ãããããã«ãªãã¾ããã¤ã¾ãããã¤ããªããå¼ãã§ããå¤é¨ã®é¢æ°ï¼ããã§ã¯memcmpï¼ãèªä½ã®é¢æ°ã§ä¸æ¸ãã§ããã¨ãããã¨ã§ãã
shared objectèªä½ã¯ãã ã®ELFãªã®ã§ãé常ã®Cã®ããã°ã©ã ã®ããã«æ¸ããã¨ãã§ãã¾ããä»åæ¸ããmemcmpã¯ã2ã¤ã®å¼æ°ãä¸è´ãã¦ããå ´åã«puts("ok")
ãå®è¡ããããã«ãã¦ãã¾ãã
#include <stdio.h> int memcmp(char* buf1, char* buf2, int len) { for (int i = 0; i < len; i++) { if (buf1[i] < buf2[i]) return -1; if (buf1[i] > buf2[i]) return 1; } puts("ok"); return 0; }
ãããshared objectã¨ãã¦ã³ã³ãã¤ã«ã«ã¯ã-shared
ãªãã·ã§ã³ãä»ããã ãã§åé¡ããã¾ããã-fPIC
ãªãã·ã§ã³*4ãã¤ãã¦ããæç®ãããã¾ããã2017å¹´ç¨ããããã©ã«ãã§ãã®ãªãã·ã§ã³ãæå¹ã«ãªã£ã¦ããããã2024å¹´ç¾å¨ã§ã¯ä¸è¦ã ã¨èªèãã¦ãã¾ã*5ã
ãã®ããã«ãã¦ä½æããshared objectãã¡ã¤ã«ãLD_PRELOAD
ã«æå®ããã¨ãå®è£
ããmemcmpé¢æ°ãå¼ã³åºããã¦ãããã¨ãåããã¾ãã
$ gcc -shared -o hook.so hook.c $ echo Alpaca{fakeflag} | LD_PRELOAD=./hook.so ./checker flag? ok ok ok ok ok ok ok Wrong...
å¾ã¯ãltraceã®å ´åã¨åæ§ã«è§£ããã¨ãã§ãã¾ãã
import subprocess from tqdm import tqdm from extract_data import FLAG_LEN def check(flag): res = subprocess.run(["./checker"], env={"LD_PRELOAD": "./hook.so"}, input=flag + "\n", capture_output=True, text=True).stdout return res.count("ok") flag = "" for i in tqdm(range(FLAG_LEN)): for c in range(0x20, 0xff): if check(flag + chr(c)) == i + 1: flag += chr(c) break print(flag)
ltraceãç¨ãã解æ³ã¯ä½éã§ãããããã®è§£æ³ã¯åæ§ã®ãã¨ãããã¤ã¤é«éã«åä½ãã¾ããããã¯ltraceããããã¬ã®ããã«ãã¬ã¼ã¯ãã¤ã³ãå¼µããã¨ã§æ©è½ãã¦ããä¸æ¹ãããã¯ãããã£ããã¨ã¯è¡ã£ã¦ããªãããã§ã*6ãã¾ããLD_PRELOAD
ã¯ãããã°ãããããã»ã¹ãæ¢ã«å¥ããã»ã¹ã«ãã¬ã¼ã¹ããã¦ããã¨ãã£ãç¶æ³ã§ã使ç¨ã§ãããã¨ãé
åã§ãã
$ time python solver_ld_preload.py python solver_ld_preload.py 1.83s user 2.05s system 111% cpu 3.472 total $ time python solver_ltrace.py python solver_ltrace.py 15.22s user 38.00s system 102% cpu 51.907 total
memcmpãputsã«ç½®ãæããåã«ã¼ãã§ã®hashãå¾ã
åé
ã§ã¯ãåçãªã³ã«ã¯é¢æ°åããã¨ã«ãªã³ã¯ãã¦ããã¨æ¸ãã¾ãããããã¯ã¤ã¾ãããªã³ã¯ã«ç¨ãããã¦ããæååãä»ã®æååã«ç½®ãæããã°ãå¥ã®é¢æ°ããªã³ã¯ãããããã«ãªãã¨ãããã¨ã§ãããã®ãã¤ããªã§ã¯ãmemcmpã¯memcmp(hash, table[i], 16)
ã¨ãã¦å¼ã°ãã¦ãã¾ããããã£ã¦ãmemcmpãputsã«ç½®ãæããã°puts(hash)
ãå¼ã°ãã¦ãããã¨ã«ãªãããåã«ã¼ãã§ã®hashãå¾ããã¨ãã§ãããã§ãã
ä»åã¯hashãæ½åºãããããä¸è´å¤å®ãã½ã«ãã§è¡ãå¿
è¦ãããã¾ãããã®ãããtable
ã®ãã¼ã¿ãæ½åºããªãã¨ããã¾ãããç§ã¯æ®æ®µã¯Ghidraã«åå¨ããCopy as æ©è½ã§æ½åºãããã¨ãå¤ãã§ãããä»åã¯æ¢ãã¦pwntoolsãç¨ãã¦æ½åºãã¦ã¿ããã¨ã«ãã¾ãã
from more_itertools import chunked from pwn import ELF FLAG_LEN = 49 DIGEST_LEN = 16 def get_table(): elf = ELF("./checker") res = elf.read(elf.symbols["table"], FLAG_LEN * DIGEST_LEN) return list(map(bytes, chunked(res, DIGEST_LEN)))
次ã«ãcheckerã®memcmpãputsã«ç½®ãæãããã¤ããªãä½æãã¾ããé«åº¦ãªãã¼ã«ã使ããã«ãã¤ããªããããããéã®ååã§ããããªãã»ããããµã¤ãºãå¤æ´ããã¨ããã«å¯¾å¿ããå ´æãç·¨éããªãã¨ãããªããªãããããã¼ã¿ã®ç½®ãæãã¯ä¸æ¸ãã§è¡ããã¨ãæã¾ããã§ãããªããå¾ã»ã©ç´¹ä»ããLIEFã®ãããªãã¼ã«ã¯ãªãã»ããã®ç·¨éãªã©ãèªåã§è¡ã£ã¦ããã¾ãã
import subprocess res = open("checker", "rb").read() open("checker_puts", "wb").write(res.replace(b'memcmp', b'puts\x00\x00')) subprocess.run("chmod +x checker_puts", shell=True)
ãã¨ã¯ãåºåãåºã«ä¸è´å¤å®ããã¦ããã°ããã§ããhashã®ãã¡NULLãã¤ãã®å ãåºåãããªãããã«è¤æ°åè£ãåºãå¯è½æ§ãèæ ®ããããã¯ãã©ãã¯ãè¡ããããã«DFSã¨ãã¦å®è£ ãã¾ããã
import subprocess table = get_table() def check(flag: bytes): output = subprocess.run("./checker_puts", input=flag+b'\n', capture_output=True).stdout expected_hash = table[len(flag) - 1].split(b'\x00')[0] return expected_hash in output def solve(flag): if len(flag) == FLAG_LEN: return flag for c in range(0x20, 0xff): next_flag = flag+bytes([c]) if check(next_flag): res = solve(next_flag) if res is not None: return res return None print(solve(b''))
ä»åã¯åçã©ã¤ãã©ãªã®è§£æ±ºå ãå¤æ´ããã¨ããä»ã®ææ³ã§ã代æ¿ã§ãããã¨ãè¡ãã¾ãããããã®ããã«ç解ãåã¶é¨åãããããã¦åã°å¼·å¼ã«ãã¤ããªã®åä½ãé½åã®è¯ãããã«å¤æ´ãããã¨ã¯é«é£æ度ã®åé¡ã§ããã¾ã«ã¨ãããææ³ã§ãã
gdbã§memcmpã®è¿ãå¤ãå¾ã
ä»ã¾ã§ã®è§£æ³ã¯ãã¤ããªããmemcmpé¢æ°ãå¼ã°ãã¦ãããã¨ã«ä¾åãã¦ãã¾ãããããããå¼ã°ãã¦ããªãã£ããã©ã®ããã«è§£ãã°ããã§ãããããä¸ã¤ã®çãã¨ãã¦ããããã¬ãç¨ããå é¨ç¶æ ã®åå¾ãããã¾ããä»åã¯ãgdbãç¨ãããããã°ãèªååãã¦ã¿ã¾ãã
ã¾ããããã°ã©ã ãä¸æåæ¢ããããã¬ã¼ã¯ãã¤ã³ããå¼µãå ´æãæ¢ãã¾ããä»åã¯memcmpã®è¿ãå¤ãã»ããã®ã§ãmemcmpãå¼ã°ããç´å¾ã«å¼µãã¨ããããã§ããgdbãç¨ãã¦ã¢ã»ã³ããªãè¦ã¦ã¿ãã¨ãä»åã¯main+175ã該å½ãããã§ãã
$ gdb ./checker ... pwndbg> start ... pwndbg> x/80xi $rip => 0x555555555980 <main>: endbr64 0x555555555984 <main+4>: push r13 0x555555555986 <main+6>: push r12 0x555555555988 <main+8>: push rbp ... 0x555555555a1f <main+159>: add rsi,r13 0x555555555a22 <main+162>: mov edx,0x10 0x555555555a27 <main+167>: mov rdi,rbp 0x555555555a2a <main+170>: call 0x5555555550b0 <memcmp@plt> 0x555555555a2f <main+175>: test eax,eax 0x555555555a31 <main+177>: sete al 0x555555555a34 <main+180>: movzx eax,al ...
ãã¨ã¯ãããã«ãã¬ã¼ã¯ãã¤ã³ããè²¼ã£ãå¾ã«eaxãåå¾ããcontinueããæµããèªååããã°ããã§ããèªååã¯ãgdbãæä¾ãã¦ããPythonのAPIãç¨ãã¾ãããã®APIã¯ãgdbã§ã®ãããã°ã«æ
£ãã¦ãããªãã°é常ã«ç°¡åã«ä½¿ãã¾ããä»å使ãã¹ãã¡ã½ããã¯gdb.execute
ã§ã®ã³ãã³ãå®è¡ã¨ãgdb.parse_and_eval
ã§ã®å¤åå¾ã®2ã¤ã®ã¿ã§ãããªããã¡ã¢ãªå
容ãã¬ã¸ã¹ã¿ã®å
容ãåå¾ããå°ç¨ã®é¢æ°ã¯ãããã®ã®ãè²ã
ã¨ä½¿ãã«ããã®ã§ããã§ã¯ä½¿ç¨ãã¦ãã¾ããã
以ä¸ã¯ãããããç¨ãã¦æ¸ããã¹ã¯ãªããã§ãããã®ã¹ã¯ãªããã¯ãgdbã®-x
ãªãã·ã§ã³ã«æ¸¡ãã¦gdb -q -x solver.py
ã®ããã«ãããã¨ã§å®è¡ãããã¨ãã§ãã¾ãã
import gdb from tqdm import tqdm FLAG_LEN = 49 gdb.execute("file ./checker") gdb.execute("start") gdb.execute("b *main+175") gdb.execute("det") def check(flag): open("input.txt", "w").write(flag) gdb.execute("r < input.txt") # run res = 0 for i in range(FLAG_LEN): eax = gdb.parse_and_eval("$eax") if eax == 0: res += 1 gdb.execute("c") # continue return res flag = "" for i in tqdm(range(FLAG_LEN)): for c in range(0x20, 0xff): if check(flag + chr(c)) == i + 1: flag += chr(c) break print(flag)
gdbãèªååãã¦ã½ã«ããæ¸ããã¨ã¯é »ç¹ã«ã¯ãªãã§ãããåµã¾ãå ´é¢ã§ã¯ã¨ã¦ã便å©ãªå°è±¡ãããã¾ããããããæ±ç¨ã®ãããã°ãã¼ã«ãç¨ãã¦ããé½åä¸ãå®è¡é度ãããªãé ããã¨ãæ¬ ç¹ã¨ãã¦æãããã¾ããå®éãä»åã®ã¹ã¯ãªããã¯å®è¡ã«30åç¨åº¦ãããã¾ããã
updateãèªç±ã«å®è¡ã§ãããã¤ããªãä½ã
ä»ã¾ã§ã¯ãã¤ããªãå®è¡ããçµæãä½ããã®å½¢ã§åå¾ãã¦ãã¾ããããå ã®ãã¤ããªãä»ããªãæ¹æ³ã¯ãªãã®ã§ããããï¼ããããã¯ãupdateé¢æ°ãç´æ¥å¼ã³åºããã¨ãç®æ¨ã¨ãã¦ã¿ã¾ãã
æå§ãã«ãupdateé¢æ°ãå®è¡ããããã ãã®ãã¤ããªãä½æãã¦ã¿ã¾ããããå
ç¨ã®ããã«mainé¢æ°ãæ¸ãæãã¦ãããã®ã§ãããããã§ã¯ããå°ãã·ã³ãã«ãªæ¹æ³ãã¨ããã¨æãã¾ããä»åãupdateé¢æ°å
ã«åä½ã«å¿
è¦ãªé¢æ°å¼ã³åºãã¯åå¨ãã¾ãã*7ããã®ãããupdateé¢æ°ã¯ç´æ¥å¥ã®ã¨ããã«æã£ã¦ãã£ãã¨ãã¦ãåãããã§ããä»åã¯ãå®è¡å¯è½ã«ããstackã«ã·ã§ã«ã³ã¼ãã®ãããªå½¢ã§é¢æ°ã®ãã¼ã¿ãç½®ãããããå¼ã³åºãã¨ããææ³ãã¨ãã¾ãã以ä¸ã¯ããããè¡ãPythonã¹ã¯ãªããã¨ã³ã³ãã¤ã«ãããCã®ã³ã¼ãã§ããupdateé¢æ°ã®å
容ã¯ãã¯ãã¨ãã¦ã³ã³ãã¤ã«æã«æ³¨å
¥ãã¦ããã-z stack
ãªãã·ã§ã³ãã¤ãããã¨ã§stackãå®è¡å¯è½ã«ãã¦ãã¾ãã
import subprocess from pwn import ELF elf = ELF("./checker") update = elf.read(elf.symbols["update"], 0x7a9) subprocess.run(f"gcc -z execstack -DFUNC={','.join(map(str, update))} -o executor executor.c 2>/dev/null", shell=True)
#include<stdio.h> int main(){ char update[] = { FUNC }; char buf[36]; read(0, buf, 36); char c; read(0, &c, 1); ((void(*)(char*, char))update)(buf, c); write(1, buf, 36); }
ãã®ããã«ãã¦ä½ããããã¤ããªãç¨ããã°ãmainé¢æ°ã§è¡ã£ã¦ããhashã®æ´æ°ã¨memcmpã®å¦çãå®å ¨ã«Pythonå´ã§è¡ããã¨ãã§ããããã«ãªãã¾ãããããè¡ãã¹ã¯ãªããã¯ä»¥ä¸ã®éãã§ãã
import subprocess from tqdm import tqdm from extract_data import FLAG_LEN, get_table def compute(state: bytes, c: int): res = subprocess.run("./executor", input=state + bytes([c]), capture_output=True).stdout return res table = get_table() flag = "" state = b"\x00" * 36 for i in tqdm(range(FLAG_LEN)): for c in range(0x20, 0xff): next_state = compute(state,c) if next_state[:16] == table[i]: flag += chr(c) state = next_state break print(flag)
ctypes.CDLLãç¨ãã¦Pythonããupdateãå®è¡ãã
Pythonã«ã¯å ±æã©ã¤ãã©ãªãèªã¿è¾¼ãã§ããã®é¢æ°ãPythonããå®è¡ããããã®ctypesã¨ããã©ã¤ãã©ãªãåå¨ãã¾ãããããç¨ããã°ãåé ã®ãããªãã¨ãããã¨ãPythonããç´æ¥updateé¢æ°ãå¼ã³åºããã¨ãå¯è½ããã«æãã¾ããããããupdateé¢æ°ãåå¨ããã®ã¯å ±æã©ã¤ãã©ãªã§ã¯ãªãå®è¡ãã¡ã¤ã«ã§ããããããã®ã¾ã¾ã§ã¯èªã¿è¾¼ããã¨ãä¸å¯è½ã§ã*8ãããã¯ãLIEFã¨ãããã¤ããªãããã®ããã®ã©ã¤ãã©ãªãç¨ãããã¨ã§è§£æ±ºã§ãã¾ãããªããããã¯å ¬å¼ããã¥ã¡ã³ãã§ããã¥ã¼ããªã¢ã«ã®ä¸è²«ã¨ãã¦ãç´¹ä»ããã¦ãã¦ããã¦ã¼ã¹ã±ã¼ã¹ã§ãã
import lief bin = lief.parse("./checker") bin.add_exported_function(bin.get_function_address("update"), "update") bin[lief.ELF.DynamicEntry.TAG.FLAGS_1].remove(lief.ELF.DynamicEntryFlags.FLAG.PIE) bin.write("checker.so")
ãã®ããã«ãã¦å¤æããã©ã¤ãã©ãªãç¨ããã°ãPythonå ã§updateé¢æ°ãç´æ¥å¼ã³åºããã¨ãã§ãã¾ããctypesã§ã®é¢æ°ã®å¼ã³åºãã¯å¼æ°ã®ã©ããã¼ã¯ã©ã¹ãå°ã è¤éãªã®ã§ãããã¥ã¡ã³ããå 人ã®ã³ã¼ããªã©ãè¦ãªããæ¸ããã¨ããããããã¾ãã
import subprocess from tqdm import tqdm from extract_data import FLAG_LEN, get_table import ctypes HASH_LEN = 32 lib = ctypes.CDLL('checker.so') def update(state: bytes, c: int): state_array = ctypes.create_string_buffer(state) lib.update(state_array, ctypes.c_char(bytes([c]))) return bytes(state_array) table = get_table() flag = "" state = b"\x00" * 36 for i in tqdm(range(FLAG_LEN)): for c in range(0x20, 0xff): next_state = update(state, c) if next_state[:16] == table[i]: flag += chr(c) state = next_state break print(flag)
ctypesã¯Pythonã®ããã»ã¹å ã«ç´æ¥updateé¢æ°ããã¼ããã¦ããã®ã§ããªãã¨ãã£ã¦ãå§åçãªéããç¹å¾´ã§ããããã¾ã§ã®ã½ã«ãã§ã¯æéã®ãã®ã§ã1ç§ç¨åº¦ã¯ããã£ã¦ãããã®ã®ããã®ã½ã«ãã¯ãã¤ããªãå¼ã³åºããªã¼ãã¼ããããåå¨ããªãããã«ä¸ç¬ã§ãã©ã°ãæ±ã¾ãã¾ããã¾ããctypesã¯æ¨æºã©ã¤ãã©ãªã®é¢æ°ãç°¡åã«å¼ã³åºããã¨ãã§ããã®ã§ãsrandåã³randomãPythonããç¨ããã®ã«ããã使ç¨ããã¾ãã
from ctypes import CDLL libc = CDLL("libc.so.6") libc.srand(0) assert libc.rand() == 1804289383
ã¾ããä»åã¯LIEFãã©ã¤ãã©ãªã¸ã®å¤æã®ããã®ç¨éã«ã®ã¿ç¨ãã¾ããããLIEFã¯ELFã®æ å ±ã®åå¾ãã©ã¤ãã©ãªé¢æ°ã®å¦çã®å·®ãæ¿ãã¨ãã£ãä»ã®ç¨éã§ãç¨ãããã¨ãã§ãã¾ãã
angrã¨Unicornãç¨ãã¦ãã¤ããªãã¨ãã¥ã¬ã¼ããã
åå°½ãã¦ããã®ã§å段ã¯è»½ãã«æ¸ãã¾ãããã¤ãæ¸ãç´ãããâ¦â¦
angrã¨ã¯ãã·ã³ããªãã¯å®è¡ãè¡ãã©ã¤ãã©ãªã§ããã·ã³ããªãã¯å®è¡ã¨ã¯ãå ¥åå¤ãå¤æ°ï¼ã·ã³ãã«ï¼ã¨ãã¦æã£ã¦ãããã¡ã¢ãªãã¬ã¸ã¹ã¿ã®ç¶æ ãå¤æ´ããªããæ¡ä»¶åå²ã®ãã³ã«ç¶æ ãåå²ãããåç¶æ ã«è¾¿ãçãããã®æ¡ä»¶ãéããå®è¡æ¹å¼ã®ãã¨ã§ããä¾ãã°ã以ä¸ã®ãããªã½ã¼ã¹ã³ã¼ãããã³ã³ãã¤ã«ããããã¤ããªããã£ãã¨ãã¾ãã
int main() { int val=0; scanf("%d", &val); if (val * 0xdeadbeef == 0xcafebabe) { puts("OK!"); } }
ããã«angrãç¨ããã¨ãæå®ããå ´æã«å°éããã®ã«å¿ è¦ãªæ¡ä»¶ãèªåã§åéãããã®ä¸ã§å¿ è¦ãªå ¥åãèªåã§è¨ç®ãã¦ããã¾ãã
import subprocess import angr proj = angr.Project("angr_example") state = proj.factory.entry_state() simgr = proj.factory.simulation_manager(state) simgr.explore(find=lambda state: b'OK!' in state.posix.dumps(1)) # simgr.explore(find=0x4011da) # can also be address state = simgr.found[0] print(state.posix.dumps(0))
ä¸è¦ããã¨å ¨ã¦ã®reversingã解ããã¨ãã§ããæå¼·ã®ãã¼ã«ã®ããã«æãã¾ãããããã§ã¯ããã¾ãããangrã¯å¿ è¦ãªå ¥åãè¨ç®ããããã®ããã¯ã¨ã³ãã¨ãã¦SMTã½ã«ããç¨ãã¦ãããããæ±ãããã¨ãã§ããå¤ã¯ã½ã«ãã®è½åã«ç¸ããã¾ããã¾ããæ¡ä»¶åå²ã¨ã«ã¼ããçµã¿åããããªã©ããå ´åã¯ç¶æ ãåå²ãããã¦ãã¾ããangrã§ã¯å¦çããããªããªã£ã¦ãã¾ããã¨ãããã¾ããä»åã®updateé¢æ°ã¯æ¡ä»¶åå²ããã³éç·å½¢ãªå¦çãå«ãã§ãããSMTã½ã«ãåã³angrãé常ã«è¦æã¨ãã¦ããã¿ã¤ãã®ãã¤ããªã§ãã
ãã ãangrã¯ãã¤ããªã®ã¨ãã¥ã¬ã¼ãããã®ç¶æ ã®è¤è£½ã容æã«è¡ãããã®ã¤ã³ã¿ã¼ãã§ã¤ã¹ãæ´ãããã¦ãã¾ããã¾ããã·ã³ãã«ããªãç¶æ ã§ããã°Unicornã¨ããCPUã®ã¨ãã¥ã¬ã¼ã¿ãç¨ãã¦é«éãªã¨ãã¥ã¬ã¼ã·ã§ã³ãè¡ããã¨ãå¯è½ã§ããããã«æ³¨ç®ããä»åã¯angrãUnicornã®ããã³ãã¨ã³ãã¨ãã¦ç¨ãã¦ã¿ããã¨ã«ãã¾ããå ç¨ãè¿°ã¹ãããã«ãangrã¯è¤æ°ã®ç¶æ ã並è¡ãã¦ç®¡çãããã¨ãã§ãã¾ããå ç¨ã¯ç¶æ ã®åå²ã¯æ¡ä»¶åå²æã«è¡ãããã¨æ¸ãã¾ããããç¶æ ã®åå²ããã¡ãã§è¡ãã°ä¸æåãã¤ã®æ¢ç´¢ãè¡ãããã§ããã¾ããæ¡ä»¶ã«å°éããè¦è¾¼ã¿ããªããªã£ãã¹ãã¼ããå¤å®ãã¦æç©ããããã¨ã§ãä¸è¦ãªæ¢ç´¢ãé²ããã¨ãã§ãã¾ãããããè¡ãã¹ã¯ãªããã¯ä»¥ä¸ã®ã¨ããã§ãã
import angr import claripy proj = angr.Project("checker") AT_CHR_LOAD = 0x401a08 AFTER_MEMCMP = 0x401a2f def step_func(lsm: angr.SimulationManager): def remove_non_zero(state: angr.SimState): if state.addr != AFTER_MEMCMP: return False return state.regs.rax.concrete_value != 0 def populate_next(state: angr.SimState): if state.addr != AT_CHR_LOAD: return False print(state.globals["flag"]) states = [] for c in range(0x20, 0x80): new_state = state.copy() new_state.globals["flag"] += chr(c) next_flag = new_state.globals["flag"].encode() new_state.memory.store(state.regs.sp + 0x30, next_flag, len(next_flag)) states.append(new_state) return states lsm.drop(filter_func=remove_non_zero) lsm.apply(populate_next) return lsm state = proj.factory.entry_state( stdin=claripy.BVV(b''), add_options={ *angr.options.unicorn, "ZERO_FILL_UNCONSTRAINED_MEMORY", "ZERO_FILL_UNCONSTRAINED_REGISTERS", } ) state.globals["flag"] = "" # unicornã§ã®å®è¡ã¯ã¯ã¼ã«ãã¦ã³ãããã®ã§ããããç¡å¹åãã state.unicorn.cooldown_nonunicorn_blocks = 0 simgr = proj.factory.simulation_manager(state) simgr.explore( step_func=step_func, find=lambda state: b'Correct!' in state.posix.dumps(1), extra_stop_points={ AT_CHR_LOAD, AFTER_MEMCMP } ) print(simgr.found[0].globals["flag"])
æ£ç´ã«è¨ãã°angrãããã¾ã§çªã£è¾¼ãã 使ãæ¹ãããã®ã¯åãã¦ã ã£ãã®ã§ãããæã£ããããç²ãã¾ããããã®ãããªåé¡ãangrã§è§£ããã¨ã¯ãå§ããã¾ãããã¨ã¯ããã特定のアドレスの動作をフックããããç¹å®ã®é¢æ°ã ãå®è¡ãããã¨ãã æ®´ã以ä¸ã®ä½¿ãéããããã¼ã«ã§ãããã¾ããããã使ãããªããã°ãå¼·åãªéå ·ã¨ãªãã§ãããã
ãããã«
Reversingã¯ãã ã®è
åã¸ã£ã³ã«ã ã¨æãããã¡ã§ãããç§ã¯ç®ã®åã®èª²é¡ã«å¯¾ãã¦ã©ã®å´é¢ããæ»ããããé©åã«é¸æãããã¨ã大åãªã¸ã£ã³ã«ã ã¨èãã¦ãã¾ããæ¬Writeupã§ããåãç®æ¨ã«å¯¾ããæ§ã
ãªææ³ãç´¹ä»ãã¦ãã¾ãããä»åã®åé¡ã§ã¯é åããªãã®ã§ããä»ã®åé¡ã§ã¯éã«ã¯ãªãã£ã«ã«ã«ç¨ãããã¨ãã§ãããã®ãåå¨ãã¦ãã¾ããä»ã«ãããé¢åãªpycã®åé¡ãgdbãç¨ãã¦attachp python
ã¨search flag{
ããããã¨ã§è§£ãããããç¬èªã¢ã¼ããã¯ãã£ã®ã¨ãã¥ã¬ã¼ã¿ã«å
é¨ç¶æ
ããã³ãããã³ã¼ããå ãã¦èªåã§ã³ã³ãã¤ã«ãããè¦éããè¯ããªã£ããã¨ãã£ãã±ã¼ã¹ãåãããããä¸ä¾ã§ããããã
ã¨ã¯ãããä»åã®ããã«åç解æããããã¨ãæ確ã«ããå ´åãããã°ãå¦çãèªã¿ãããªãã®ã§åãã«åã£ãåç解æãããçµæã解ãã¯ãããã®ã®çµå±éçã«å¦çãèªãã æ¹ãæ©ãã£ãâ¦â¦ã¨ãã£ããã¨ããã°ãã°ããã¾ããããã®ããå´é¢ãè¦åããå è¦*9ã¯ãã¯ãçµé¨ã§éãããã¦ãããã®ã ããã¨æãã¦ãã¾ãã
ä¸æ¹ã§ãå´é¢ãæ»ãæ¹ã®å¼ãåºãã¯æ°ãããªãã¦ãå¢ãã¦ããã¾ãããã¨ãã¥ã¬ã¼ã¿ãã¤ã³ã¿ããªã¿ã®ã³ã¼ããªã¼ãã£ã³ã°ãOSã¨ãã£ãä¸ã¤ã¬ã¤ã¤ã®ä½ãé¨åã®ä»çµã¿ã«ã¤ãã¦é è©£ãæ·±ãããã¨ã§ãå¢ãã¦ããã¾ããæè¿åè¡ãããBinary Hacks Rebootedããã³åä½(?)ã®Binary Hacksã¯ããããã£ãç¥èãå¢ãããã人ã«ã¯ãå§ãã®ä¸åãªã®ã§ã¯ãªãã§ãããããä»åç´¹ä»ãã解æ³ã«ç¨ãããã¦ããltraceãLD_PRELOAD
ã«ã¤ãã¦ã¯Binary Hacksã«æ²è¼ããã¦ãã¾ãããgdbãangrãLIEFã«ã¤ãã¦ã¯Binary Hacks Rebootedã«æ²è¼ããã¦ãã¾ã*10ã
ãªãã ãæå¾ã¯ã¹ãããã¿ã¦ãã¾ãã¾ããã*11ãããã¾ã§èªãã§ããã ããããã¨ããããã¾ããï¼ãã®è¨äºãããCTFã©ã¤ãããéããããã ãä¸å©ã«ãªãã°å¹¸ãã§ãã
*1:åé¡åã¯Round 2ã§åºé¡ãããSimple Loginã®ã»ã«ããªãã¼ã¸ã¥ã§ãã
*2:è²ã æ¤è¨¼ãã¾ããããå®éã®ã¨ããã¨ãã¦æºããã¦ãããã¯æªããã§ãã
*3:mainé¢æ°ã ãæé©åã¬ãã«ãä¸ãããã¨ã§memcmpãé¢æ°ã®ã¾ã¾æ®ãããã®ææ³ãé©ç¨ã§ããããã«ãã¦ãã¾ãã
*4:ãã®ãªãã·ã§ã³ã¯ãé ç½®ãããã¢ãã¬ã¹ãä¸å®ã§ãåä½ããä½ç½®ç¬ç«ã³ã¼ãã¨å¼ã°ãããã¤ããªãçæãããªãã·ã§ã³ã§ãã
*5:32bitã¢ã¼ããã¯ãã£ãã¿ã¼ã²ããã«ããå ´åãæå¹ã«ãªã£ã¦ããããªã©ã¯åãããªãã§ãã
*6:ã¨ã¯ãããã©ããã¦ltraceãããã¾ã§ä½éãªã®ãã¯è¬ã§ããããå°ãæ©ãã¦ãããæ°ããã¾ããâ¦â¦
*7:__stack_chk_failã¯åå¨ãã¾ãããããã¯æ£å¸¸ç³»ãªãã°å¼ã°ãããã¨ã®ãªãé¢æ°ãªã®ã§ç¡è¦ãã¦ããã§ã
*8:shared objectãéãããã®é¢æ°ã§ããdlopenã§ã¯å®è¡ãã¡ã¤ã«ãéããã¨ãã§ãã¾ãããã¾ããupdateé¢æ°ã¯exportããã¦ããªããããéããã¨ãã¦ãé¢æ°ãåå¾ãããã¨ãã§ãã¾ãã
*9:ç§ã¯A*ã¢ã«ã´ãªãºã ã®è©ä¾¡é¢æ°ã®ãããªã¤ã¡ã¼ã¸ãæã£ã¦ãã¾ã
*10:å½ç¶ã§ããããã®åé¡ã¯ãããã®æ¬ãèªãã§ä½åããã¨ããããã§ã¯ããã¾ãããããã ãåºãé©ç¨ã§ãããã¯ããã¯ãã«ãã¼ãã¦ããã¨ãããã¨ã§ããã
*11:æ¬å½ã«éãã¾ã