changed 6 months ago
Linked with GitHub

AlpacaHack Round 4 (Rev) Writeup

新幹線での移動中に参加でした。
1問解くことを目標に参加して何とか達成したものの、
後日残りの問題を見たらフルタイム参加しても厳しそうでした。
Rev頑張りたい。

Simple Flag Checker

問題

次のようなcheckerファイルが渡される。

# file checker
checker: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=c2dd390ecbb794a78e20db0151832e9dd5c6359d, for GNU/Linux 3.2.0, not stripped
[checksec]
Canary                                  : Enabled 
NX                                      : Enabled
PIE                                     : Enabled
RELRO                                   : Full RELRO
Fortify                                 : Found
# ./checker
flag? aaaaaaaa
Wrong...

解法

まずはGhidraで眺めてみる。
image
入力文字列は49byte。
1byte毎にupdateで16byteに変換され、tableの値と比較を行っている。
この入力を求めたい。
updateを見るが規則的な変換は多いものの追うにはしんどそうなので動的解析を試みる。

memcmpの結果が0になれば正解なので1byteずつ探索していく。
pythonからgdbのサブプロセスを作って回したが時間がかかったので
途中で並列化してゴリ押した。
gdbの処理内でやり直したりできるんだろうけど上手く書けなかったです。

solver

import subprocess from concurrent.futures import ThreadPoolExecutor binary_path = "./checker" partial_flag = "Alpaca{" candidate_chars = "ea0413256789_@?!riotnslcudpTAOISWmhgbfywkvxzjqCBPHFMDRELNGUKVYJQZX#%$*+-|~\"'(),./:;<=>[]\\^`{} " def check_byte(candidate_char, current_flag, binary_path): result = subprocess.run( ["gdb", "-q", "--nx", "--batch", "-x", "find_flag_byte.gdb", "--args", binary_path], input=f"{current_flag + candidate_char}\n", stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True ) if "byte found" in result.stdout: print("Found:", current_flag + candidate_char) return current_flag + candidate_char return None def find_next_byte_parallel(candidate_chars, current_flag, binary_path, max_workers=12): with ThreadPoolExecutor(max_workers=max_workers) as executor: futures = [ executor.submit(check_byte, char, current_flag, binary_path) for char in candidate_chars ] for future in futures: result = future.result() if result: return result while len(partial_flag) < 49: gdb_check_steps = ''.join(['c\nif ($eax == 0)\nprintf ""\nend\n' for _ in range(len(partial_flag))]) gdb_script = f""" b main r b *main+175 {gdb_check_steps} c if ($eax == 0) printf "byte found\\n" quit end quit """ with open("find_flag_byte.gdb", "w") as gdb_file: gdb_file.write(gdb_script) partial_flag = find_next_byte_parallel(candidate_chars, partial_flag, binary_path)

※補足
自分の普通のノートPCだと6分30秒くらい
candidate_charsは普通のprintableな文字ですがなんとなく出現高そうな順に並んでます
(ちゃんとフラグの統計取ったら面白そう)

Select a repo