前回 から、picoCTF 2025 にリアルタイムで参戦しています。
picoCTF 2025 が 3/7 から始まっていて、昨日(3/17)に終了しました。今回は、リアルタイムで参戦できました。終了するまでは、解法や、フラグを公開することは禁止されていましたので、順番にその内容を公開していきます。
今回は、Reverse Engineering です。
それでは、やっていきます。
はじめに
「セキュリティ」の記事一覧です。良かったら参考にしてください。
・第2回:Ghidraで始めるリバースエンジニアリング(使い方編)
・第3回:VirtualBoxにParrotOS(OVA)をインストールする
・第4回:tcpdumpを理解して出力を正しく見れるようにする
・第5回:nginx(エンジンエックス)を理解する
・第6回:Python+Flask(WSGI+Werkzeug+Jinja2)を動かしてみる
・第7回:Python+FlaskのファイルをCython化してみる
・第8回:shadowファイルを理解してパスワードを解読してみる
・第9回:安全なWebアプリケーションの作り方(徳丸本)の環境構築
・第10回:Vue.jsの2.xと3.xをVue CLIを使って動かしてみる(ビルドも行う)
・第11回:Vue.jsのソースコードを確認する(ビルド後のソースも見てみる)
・第12回:徳丸本:OWASP ZAPの自動脆弱性スキャンをやってみる
・第13回:徳丸本:セッション管理を理解してセッションID漏洩で成りすましを試す
・第14回:OWASP ZAPの自動スキャン結果の分析と対策:パストラバーサル
・第15回:OWASP ZAPの自動スキャン結果の分析と対策:クロスサイトスクリプティング(XSS)
・第16回:OWASP ZAPの自動スキャン結果の分析と対策:SQLインジェクション
・第17回:OWASP ZAPの自動スキャン結果の分析と対策:オープンリダイレクト
・第18回:OWASP ZAPの自動スキャン結果の分析と対策:リスク中すべて
・第19回:CTF初心者向けのCpawCTFをやってみた
・第20回:hashcatの使い方(GPU実行時間の見積りとパスワード付きZIPファイル)
・第21回:Scapyの環境構築とネットワークプログラミング
・第22回:CpawCTF2にチャレンジします(クリア状況は随時更新します)
・第23回:K&Rのmalloc関数とfree関数を理解する
・第24回:C言語、アセンブラでシェルを起動するプログラムを作る(ARM64)
・第25回:機械語でシェルを起動するプログラムを作る(ARM64)
・第26回:入門セキュリhttps://github.com/SECCON/SECCON2017_online_CTF.gitティコンテスト(CTFを解きながら学ぶ実践技術)を読んだ
・第27回:x86-64 ELF(Linux)のアセンブラをGDBでデバッグしながら理解する(GDBコマンド、関連ツールもまとめておく)
・第28回:入門セキュリティコンテスト(CTFを解きながら学ぶ実践技術)のPwnable問題をやってみる
・第29回:実行ファイルのセキュリティ機構を調べるツール「checksec」のまとめ
・第30回:setodaNote CTF Exhibitionにチャレンジします(クリア状況は随時更新します)
・第31回:常設CTFのksnctfにチャレンジします(クリア状況は随時更新します)
・第32回:セキュリティコンテストチャレンジブックの「Part2 pwn」を読んだ
・第33回:セキュリティコンテストチャレンジブックの「付録」を読んでx86とx64のシェルコードを作った
・第34回:TryHackMeを始めてみたけどハードルが高かった話
・第35回:picoCTFを始めてみた(Beginner picoMini 2022:全13問完了)
・第36回:picoCTF 2024:Binary Exploitationの全10問をやってみた(Hardの1問は後日やります)
・第37回:picoCTF 2024:Reverse Engineeringの全7問をやってみた(Windowsプログラムの3問は後日やります)
・第38回:picoCTF 2024:General Skillsの全10問をやってみた
・第39回:picoCTF 2024:Web Exploitationの全6問をやってみた(最後の2問は解けず)
・第40回:picoCTF 2024:Forensicsの全8問をやってみた(最後の2問は解けず)
・第41回:picoCTF 2024:Cryptographyの全5問をやってみた(最後の2問は手つかず)
・第42回:picoCTF 2023:General Skillsの全6問をやってみた
・第43回:picoCTF 2023:Reverse Engineeringの全9問をやってみた
・第44回:picoCTF 2023:Binary Exploitationの全7問をやってみた(最後の1問は後日やります)
・第45回:書籍「セキュリティコンテストのためのCTF問題集」を読んだ
・第46回:書籍「詳解セキュリティコンテスト」のReversingを読んだ
・第47回:書籍「詳解セキュリティコンテスト」のPwnableのシェルコードを読んだ
・第48回:書籍「バイナリファイル解析 実践ガイド」を読んだ
・第49回:書籍「詳解セキュリティコンテスト」Pwnableのスタックベースエクスプロイトを読んだ
・第50回:書籍「詳解セキュリティコンテスト」Pwnableの共有ライブラリと関数呼び出しを読んだ
・第51回:picoCTF 2025:General Skillsの全5問をやってみた
・第52回:picoCTF 2025:Reverse Engineeringの全7問をやってみた ← 今回
picoCTF の公式サイトは以下です。
3/7 から 3/17 までの 10日間で開催されています。

今回は、Reverse Engineering をやっていきます。
picoCTF 2025:Reverse Engineering
ポイントの低い順にやっていきます。
Flag Hunters(75 points)
Pythonファイル(lyric-reader.py)をダウンロードできます。また、netcat で接続する問題のようです。

まず、Pythonスクリプトです。結構長いですね。ところどころ文字化けしてます。全角の ' が良くないようです。
import re import time # Read in flag from file flag = open('flag.txt', 'r').read() secret_intro = \ '''Pico warriors rising, puzzles laid bare, Solving each challenge with precision and flair. With unity and skill, flags we deliver, The ether窶冱 ours to conquer, '''\ + flag + '\n' song_flag_hunters = secret_intro +\ ''' [REFRAIN] We窶决e flag hunters in the ether, lighting up the grid, No puzzle too dark, no challenge too hid. With every exploit we trigger, every byte we decrypt, We窶决e chasing that victory, and we窶冤l never quit. CROWD (Singalong here!); RETURN [VERSE1] Command line wizards, we窶决e starting it right, Spawning shells in the terminal, hacking all night. Scripts and searches, grep through the void, Every keystroke, we're a cypher's envoy. Brute force the lock or craft that regex, Flag on the horizon, what challenge is next? REFRAIN; Echoes in memory, packets in trace, Digging through the remnants to uncover with haste. Hex and headers, carving out clues, Resurrect the hidden, it's forensics we choose. Disk dumps and packet dumps, follow the trail, Buried deep in the noise, but we will prevail. REFRAIN; Binary sorcerers, let窶冱 tear it apart, Disassemble the code to reveal the dark heart. From opcode to logic, tracing each line, Emulate and break it, this key will be mine. Debugging the maze, and I see through the deceit, Patch it up right, and watch the lock release. REFRAIN; Ciphertext tumbling, breaking the spin, Feistel or AES, we窶决e destined to win. Frequency, padding, primes on the run, Vigenティre, RSA, cracking them for fun. Shift the letters, matrices fall, Decrypt that flag and hear the ether call. REFRAIN; SQL injection, XSS flow, Map the backend out, let the database show. Inspecting each cookie, fiddler in the fight, Capturing requests, push the payload just right. HTML's secrets, backdoors unlocked, In the world wide labyrinth, we窶决e never lost. REFRAIN; Stack's overflowing, breaking the chain, ROP gadget wizardry, ride it to fame. Heap spray in silence, memory's plight, Race the condition, crash it just right. Shellcode ready, smashing the frame, Control the instruction, flags call my name. REFRAIN; END; ''' MAX_LINES = 100 def reader(song, startLabel): lip = 0 start = 0 refrain = 0 refrain_return = 0 finished = False # Get list of lyric lines song_lines = song.splitlines() # Find startLabel, refrain and refrain return for i in range(0, len(song_lines)): if song_lines[i] == startLabel: start = i + 1 elif song_lines[i] == '[REFRAIN]': refrain = i + 1 elif song_lines[i] == 'RETURN': refrain_return = i # Print lyrics line_count = 0 lip = start while not finished and line_count < MAX_LINES: line_count += 1 for line in song_lines[lip].split(';'): if line == '' and song_lines[lip] != '': continue if line == 'REFRAIN': song_lines[refrain_return] = 'RETURN ' + str(lip + 1) lip = refrain elif re.match(r"CROWD.*", line): crowd = input('Crowd: ') song_lines[lip] = 'Crowd: ' + crowd lip += 1 elif re.match(r"RETURN [0-9]+", line): lip = int(line.split()[1]) elif line == 'END': finished = True else: print(line, flush=True) time.sleep(0.5) lip += 1 reader(song_flag_hunters, '[VERSE1]')
早速やってみます。最初の Crowd: のところで、1回だけ入力しただけで、あとは、そのまま終了しました。この入力から考えなければなりませんね。
$ nc verbal-sleep.picoctf.net 50331 Command line wizards, we’re starting it right, Spawning shells in the terminal, hacking all night. Scripts and searches, grep through the void, Every keystroke, we're a cypher's envoy. Brute force the lock or craft that regex, Flag on the horizon, what challenge is next? We’re flag hunters in the ether, lighting up the grid, No puzzle too dark, no challenge too hid. With every exploit we trigger, every byte we decrypt, We’re chasing that victory, and we’ll never quit. Crowd: 2 Echoes in memory, packets in trace, Digging through the remnants to uncover with haste. Hex and headers, carving out clues, Resurrect the hidden, it's forensics we choose. Disk dumps and packet dumps, follow the trail, Buried deep in the noise, but we will prevail. We’re flag hunters in the ether, lighting up the grid, No puzzle too dark, no challenge too hid. With every exploit we trigger, every byte we decrypt, We’re chasing that victory, and we’ll never quit. Crowd: 2 Binary sorcerers, let’s tear it apart, Disassemble the code to reveal the dark heart. From opcode to logic, tracing each line, Emulate and break it, this key will be mine. Debugging the maze, and I see through the deceit, Patch it up right, and watch the lock release. We’re flag hunters in the ether, lighting up the grid, No puzzle too dark, no challenge too hid. With every exploit we trigger, every byte we decrypt, We’re chasing that victory, and we’ll never quit. Crowd: 2 Ciphertext tumbling, breaking the spin, Feistel or AES, we’re destined to win. Frequency, padding, primes on the run, Vigenère, RSA, cracking them for fun. Shift the letters, matrices fall, Decrypt that flag and hear the ether call. We’re flag hunters in the ether, lighting up the grid, No puzzle too dark, no challenge too hid. With every exploit we trigger, every byte we decrypt, We’re chasing that victory, and we’ll never quit. Crowd: 2 SQL injection, XSS flow, Map the backend out, let the database show. Inspecting each cookie, fiddler in the fight, Capturing requests, push the payload just right. HTML's secrets, backdoors unlocked, In the world wide labyrinth, we’re never lost. We’re flag hunters in the ether, lighting up the grid, No puzzle too dark, no challenge too hid. With every exploit we trigger, every byte we decrypt, We’re chasing that victory, and we’ll never quit. Crowd: 2 Stack's overflowing, breaking the chain, ROP gadget wizardry, ride it to fame. Heap spray in silence, memory's plight, Race the condition, crash it just right. Shellcode ready, smashing the frame, Control the instruction, flags call my name. We’re flag hunters in the ether, lighting up the grid, No puzzle too dark, no challenge too hid. With every exploit we trigger, every byte we decrypt, We’re chasing that victory, and we’ll never quit. Crowd: 2
ダウンロードした Pythonスクリプトをデバッガで動かしてみると、最初の for文の結果は、start=15、refrain=7、refrain_return=12 でした。つまり、最初は 15行目から([VERSE1] の次の行から)始まります。その後、REFRAIN; の行まで進み、そこで、RETURN の行を書き換えて、[REFRAIN] の行(7行目)に飛びます。その後、CROWD (Singalong here!); で、最初の入力を行い、RETURN の行に進み、先ほど書き換えた値の行に飛びます。
だいたいの流れは分かりましたが、どうすればいいでしょうか。0行目に飛ばして、フラグが表示されれば、ゴールです。各行は、セミコロンで分割してるので、入力するときに、;RETURN 0 とすれば良さそうです。
とりあえず、やってみます。無事にフラグが表示されました。
$ nc verbal-sleep.picoctf.net 50331 Command line wizards, we’re starting it right, Spawning shells in the terminal, hacking all night. Scripts and searches, grep through the void, Every keystroke, we're a cypher's envoy. Brute force the lock or craft that regex, Flag on the horizon, what challenge is next? We’re flag hunters in the ether, lighting up the grid, No puzzle too dark, no challenge too hid. With every exploit we trigger, every byte we decrypt, We’re chasing that victory, and we’ll never quit. Crowd: ;RETURN 0 Echoes in memory, packets in trace, Digging through the remnants to uncover with haste. Hex and headers, carving out clues, Resurrect the hidden, it's forensics we choose. Disk dumps and packet dumps, follow the trail, Buried deep in the noise, but we will prevail. We’re flag hunters in the ether, lighting up the grid, No puzzle too dark, no challenge too hid. With every exploit we trigger, every byte we decrypt, We’re chasing that victory, and we’ll never quit. Crowd: Pico warriors rising, puzzles laid bare, Solving each challenge with precision and flair. With unity and skill, flags we deliver, The ether’s ours to conquer, picoCTF{70637h3r_f0r3v3r_6c145c84}
Binary Instrumentation 1(200 points)
1つの ZIPファイル(bininst1.zip)がダウンロードできますが、Windows がウィルスと検知してしまいます。仕方ないので、ParrotOS で wget を使ってダウンロードしました。

Windows の実行ファイルでした。Windows は後回しにします。
Tap into Hash(200 points)
1つの暗号化されたファイル(enc_flag)と、Pythonスクリプト(block_chain.py)がダウンロードできます。

暗号化ファイルはテキストファイルでした。
Key: b'\xa9\xcco`\xfa\xf9\xb5\xc0\xda\xf6*\xb3\xbe\xa9t\x0fi\xae\x13\x01q-\xae\x9ap\xb7\xa45\x1e{\xaa\xb4'
Encrypted Blockchain: b'\xf7Y\x8db\x8bS\xb2\x80q\xf2\xa0\x87\xd6(\xfc\xe6\xf2\\\x82`\x8c\\\xb4\xd4v\xf0\xf2\xd1\xde/\xfa\xb0\xfb]\xdfg\x8bV\xe2\xd1$\xa5\xa6\xd9\x8c+\xa8\xe7\xa6X\x82d\xda\x01\xb1\x85u\xa4\xa3\xd3\xda}\xff\xbc\xeeZ\x8am\x8d\x01\xb1\x84$\xa1\xf4\x85\x8c,\xfa\xe7\xf0S\x8f4\x8f\x02\xb1\x82w\xf1\xf6\x85\xd7/\xff\xb3\xa6]\xdf`\x8b\x00\xe3\xd1"\xf2\xf6\xd8\xda|\xfd\xb7\xf3Z\xd83\xdc\\\xbe\xd6!\xa2\xae\xd8\x8c{\xfa\xb0\xf2G\x8ae\x8a\x01\xe3\xd7q\xf4\xa3\xd9\x8aq\xfd\xbd\xfb\x08\x8ag\x8dQ\xe3\xd0"\xa4\xf2\x84\xdeq\xac\xb5\xf4\x0e\xca<\xdd\x0b\xc5\xe6U\xbc\xf5\x8d\x81*\xa6\xdb\xf09\xe8=\xe8\r\xd4\xd0G\xf6\xe6\x82\xb6\x16\x95\xd1\xa9\'\x8a\'\x8a]\xe5\xfaL\xb6\xd4\x9b\x83\x03\x97\xfe\x81!\xe5a\x87\\\xbf\xd4*\xa2\xf6\x9c\xdex\xf4\xe5\xf6R\x8em\x87W\xb1\x80 \xf5\xf4\xd6\x8a{\xfd\xe7\xf3\x08\x89d\xdfP\xb2\x82u\xf4\xa0\x80\xc3y\xfd\xb2\xa5[\x83m\x8dQ\xe5\x84+\xfe\xa5\x84\xd7p\xf4\xb6\xf1S\x89m\xddQ\xb0\xd7&\xf2\xf2\xd3\x8b,\xf9\xb5\xfa\x08\xd8f\x8c\\\xe5\xd7"\xf3\xf6\xd4\x8c|\xac\xe5\xa7\x08\x8ag\x8d\x02\xbe\xd1$\xa2\xa3\x80\x8ad\xfd\xb4\xa6]\xdc0\xd8W\xb1\x85q\xa5\xf6\x80\xdbq\xa9\xb5\xf0_\xdee\x8e\x00\xbe\xd3r\xf4\xa2\xd4\x88y\xf4\xb0\xa5_\xdc4\xda\x05\xb7\x80r\xf1\xa3\x84\x8ap\xfb\xb2\xfa\x08\x8c`\x8eR\xe3\x81q\xfe\xae\x84\x88x\xcf\x86'
Pythonスクリプトは以下です。VSCode でデバッグしながら見ていきます。
コマンドライン引数が必要なので、適当な文字列("aaa")を入力して進めていきます。ざっと見ましたが、だいぶ難しいです。
import time import base64 import hashlib import sys import secrets class Block: def __init__(self, index, previous_hash, timestamp, encoded_transactions, nonce): self.index = index self.previous_hash = previous_hash self.timestamp = timestamp self.encoded_transactions = encoded_transactions self.nonce = nonce def calculate_hash(self): block_string = f"{self.index}{self.previous_hash}{self.timestamp}{self.encoded_transactions}{self.nonce}" return hashlib.sha256(block_string.encode()).hexdigest() def proof_of_work(previous_block, encoded_transactions): index = previous_block.index + 1 timestamp = int(time.time()) nonce = 0 block = Block(index, previous_block.calculate_hash(), timestamp, encoded_transactions, nonce) while not is_valid_proof(block): nonce += 1 block.nonce = nonce return block def is_valid_proof(block): guess_hash = block.calculate_hash() return guess_hash[:2] == "00" def decode_transactions(encoded_transactions): return base64.b64decode(encoded_transactions).decode('utf-8') def get_all_blocks(blockchain): return blockchain def blockchain_to_string(blockchain): block_strings = [f"{block.calculate_hash()}" for block in blockchain] return '-'.join(block_strings) def encrypt(plaintext, inner_txt, key): midpoint = len(plaintext) // 2 first_part = plaintext[:midpoint] second_part = plaintext[midpoint:] modified_plaintext = first_part + inner_txt + second_part block_size = 16 plaintext = pad(modified_plaintext, block_size) key_hash = hashlib.sha256(key).digest() ciphertext = b'' for i in range(0, len(plaintext), block_size): block = plaintext[i:i + block_size] cipher_block = xor_bytes(block, key_hash) ciphertext += cipher_block return ciphertext def pad(data, block_size): padding_length = block_size - len(data) % block_size padding = bytes([padding_length] * padding_length) return data.encode() + padding def xor_bytes(a, b): return bytes(x ^ y for x, y in zip(a, b)) def generate_random_string(length): return secrets.token_hex(length // 2) random_string = generate_random_string(64) def main(token): key = bytes.fromhex(random_string) print("Key:", key) genesis_block = Block(0, "0", int(time.time()), "EncodedGenesisBlock", 0) blockchain = [genesis_block] for i in range(1, 5): encoded_transactions = base64.b64encode( f"Transaction_{i}".encode()).decode('utf-8') new_block = proof_of_work(blockchain[-1], encoded_transactions) blockchain.append(new_block) all_blocks = get_all_blocks(blockchain) blockchain_string = blockchain_to_string(all_blocks) encrypted_blockchain = encrypt(blockchain_string, token, key) print("Encrypted Blockchain:", encrypted_blockchain) if __name__ == "__main__": text = sys.argv[1] main(text)
encrypt関数がポイントな気がします。encrypt関数の引数は、そこまでに作った 5個のブロックを - で繋いだ文字列と、コマンドライン引数で指定した文字列と、ランダムな文字列です。
encrypt関数を詳細に見ます。midpoint は、常に 162 になるようです。plaintext(324文字)を、前半分を first_part に入れて、後半分を second_part に入れます。コマンドライン引数で指定した文字列を真ん中に入れて、これらを連結します。パディングして 16の倍数にします。ランダムな文字列を使って、sha256 で、32byte のダイジェストを作ります。for文では、16byteずつ取り出して、ダイジェストと XOR を取ったものを ciphertext として連結します。
これ以上、遡っていくのは難しいので、まずは、これを逆算するような Pythonスクリプトを作っていきます。
import hashlib import sys def xor_bytes(a, b): return bytes(x ^ y for x, y in zip(a, b)) key = b'\xa9\xcco`\xfa\xf9\xb5\xc0\xda\xf6*\xb3\xbe\xa9t\x0fi\xae\x13\x01q-\xae\x9ap\xb7\xa45\x1e{\xaa\xb4' ciphertext = b'\xf7Y\x8db\x8bS\xb2\x80q\xf2\xa0\x87\xd6(\xfc\xe6\xf2\\\x82`\x8c\\\xb4\xd4v\xf0\xf2\xd1\xde/\xfa\xb0\xfb]\xdfg\x8bV\xe2\xd1$\xa5\xa6\xd9\x8c+\xa8\xe7\xa6X\x82d\xda\x01\xb1\x85u\xa4\xa3\xd3\xda}\xff\xbc\xeeZ\x8am\x8d\x01\xb1\x84$\xa1\xf4\x85\x8c,\xfa\xe7\xf0S\x8f4\x8f\x02\xb1\x82w\xf1\xf6\x85\xd7/\xff\xb3\xa6]\xdf`\x8b\x00\xe3\xd1"\xf2\xf6\xd8\xda|\xfd\xb7\xf3Z\xd83\xdc\\\xbe\xd6!\xa2\xae\xd8\x8c{\xfa\xb0\xf2G\x8ae\x8a\x01\xe3\xd7q\xf4\xa3\xd9\x8aq\xfd\xbd\xfb\x08\x8ag\x8dQ\xe3\xd0"\xa4\xf2\x84\xdeq\xac\xb5\xf4\x0e\xca<\xdd\x0b\xc5\xe6U\xbc\xf5\x8d\x81*\xa6\xdb\xf09\xe8=\xe8\r\xd4\xd0G\xf6\xe6\x82\xb6\x16\x95\xd1\xa9\'\x8a\'\x8a]\xe5\xfaL\xb6\xd4\x9b\x83\x03\x97\xfe\x81!\xe5a\x87\\\xbf\xd4*\xa2\xf6\x9c\xdex\xf4\xe5\xf6R\x8em\x87W\xb1\x80 \xf5\xf4\xd6\x8a{\xfd\xe7\xf3\x08\x89d\xdfP\xb2\x82u\xf4\xa0\x80\xc3y\xfd\xb2\xa5[\x83m\x8dQ\xe5\x84+\xfe\xa5\x84\xd7p\xf4\xb6\xf1S\x89m\xddQ\xb0\xd7&\xf2\xf2\xd3\x8b,\xf9\xb5\xfa\x08\xd8f\x8c\\\xe5\xd7"\xf3\xf6\xd4\x8c|\xac\xe5\xa7\x08\x8ag\x8d\x02\xbe\xd1$\xa2\xa3\x80\x8ad\xfd\xb4\xa6]\xdc0\xd8W\xb1\x85q\xa5\xf6\x80\xdbq\xa9\xb5\xf0_\xdee\x8e\x00\xbe\xd3r\xf4\xa2\xd4\x88y\xf4\xb0\xa5_\xdc4\xda\x05\xb7\x80r\xf1\xa3\x84\x8ap\xfb\xb2\xfa\x08\x8c`\x8eR\xe3\x81q\xfe\xae\x84\x88x\xcf\x86' print( f"len(key)={len(key)}, len(ciphertext)={len(ciphertext)}" ) # 32 384 (162 + 60 + 162) key_hash = hashlib.sha256(key).digest() print( f"key_hash={key_hash}, len(key_hash)={len(key_hash)}" ) block_size = 16 plaintext = b'' for i in range(0, len(ciphertext), block_size): block = ciphertext[i:i + block_size] plain_block = xor_bytes(block, key_hash) plaintext += plain_block print( f"plaintext={plaintext}" )
とりあえず、実行してみます。
plaintext の途中にフラグのようなものが見えます。これを提出するとクリアでした。
$ python block_chain_decode.py len(key)=32, len(ciphertext)=384 key_hash=b'\xc3j\xbaU\xbed\x86\xb2\x13\xc7\x97\xe1\xeeI\xcd\x84\xdb\xfb\xaf2\x10k>\x8b\xb3\xfe\xcb3`a\xaa\x82', len(key_hash)=32 plaintext=b'43775742b57f8a1b1685282fe7e00f7487e252dc7b18bbece281de77fc424428-0083e767fcdbe7c395a1f70d6ad9f27e7e55dec15a9450300bfb88d2e99b2741-004eeeb348d8098b0235eb1cee08a17dpicoCTF{block_3SRhViRbT1qcX_XUjM0r49cH_qCzmJZzBK_4989f9ea}019a5848937232c7d20c0b31a440f37a-006f19835c6892e99922938c56e55e2ee419bb328ce14a5b5aadb023f8c7e4ad-00e7fef377bbaa58d135d00d8aa355f094f5fada12a64ed9669b6506e3b99ef1\x02\x02'
Chronohack(200 points)
1つの Pythonスクリプト(token_generator.py)をダウンロードできます。あと、サーバを起動して進めるようです。

Pythonスクリプトは以下です。
まず、20文字の文字列を取得して、トークンとします。ユーザから入力を得て、トークンと一致したらフラグが表示されるようです。普通に考えたら無理ですね。
import random import time def get_random(length): alphabet = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" random.seed(int(time.time() * 1000)) # seeding with current time s = "" for i in range(length): s += random.choice(alphabet) return s def flag(): with open('/flag.txt', 'r') as picoCTF: content = picoCTF.read() print(content) def main(): print("Welcome to the token generation challenge!") print("Can you guess the token?") token_length = 20 # the token length token = get_random(token_length) try: n=0 while n < 50: user_guess = input("\nEnter your guess for the token (or exit):").strip() n+=1 if user_guess == "exit": print("Exiting the program...") break if user_guess == token: print("Congratulations! You found the correct token.") flag() break else: print("Sorry, your token does not match. Try again!") if n == 50: print("\nYou exhausted your attempts, Bye!") except KeyboardInterrupt: print("\nKeyboard interrupt detected. Exiting the program...") if __name__ == "__main__": main()
乱数シードが、time.time() なので、現在時刻のようです。これは予測できるということでしょうか。同じ乱数シードで、50回のチャレンジが出来るので、なんとかなりそうです。
Python と pwntools を使って実装しました。
import random import time from pwn import * def get_random(length, seed): alphabet = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" random.seed(seed) # seeding with current time s = "" for i in range(length): s += random.choice(alphabet) return s def main(delay): adrs = "verbal-sleep.picoctf.net" port = 57465 #adrs = "localhost" #port = 4000 proc = remote( adrs, port ) now = time.time() try: n = 0 while n < 50: ret = proc.recvuntil( ':' ) seed = int((now) * 1000) + n + delay #seed = int((now) * 1000) info( f"recv ret={ret}', seed={seed}" ) ss = get_random( 20, seed ) proc.sendline( ss.encode("utf-8") ) ret = proc.recvline() info( f"send ss={ss}, recv ret={ret}" ) if 'Congratulations' in ret.decode("utf-8"): info( proc.recvline() ) info( proc.recvline() ) info( proc.recvline() ) info( proc.recvline() ) n += 1 except KeyboardInterrupt: print("\nKeyboard interrupt detected. Exiting the program...") except EOFError: print( f"********* ret={ret} *********" ) if __name__ == "__main__": for ii in range(1000): main( ii * 30 )
以下は、成功したところです。かなり苦労して、なんとか当てることが出来ました。実は何度も当ててたのですが、標準出力にフラグを表示できてなかったりなど、まだ pwntools を使いこなせてない感じでした。
$ python token_generator_pwntools.py > 20250311_2.log 2>&1 & $ tail -f 20250311_2.log (途中省略) [*] recv ret=b'\nEnter your guess for the token (or exit):'', seed=1741701063299 [*] send ss=QvkOjQzT1o4vdLkOBOcL, recv ret=b'Sorry, your token does not match. Try again!\n' [*] recv ret=b'\nEnter your guess for the token (or exit):'', seed=1741701063300 [*] send ss=4MDrmzpg2YJWNCisQgN0, recv ret=b'Sorry, your token does not match. Try again!\n' [*] recv ret=b'\nEnter your guess for the token (or exit):'', seed=1741701063301 [*] send ss=x9QriVKHlbbIBJ9TMm9t, recv ret=b'Sorry, your token does not match. Try again!\n' [*] recv ret=b'\nEnter your guess for the token (or exit):'', seed=1741701063302 [*] send ss=wTECACj6qoJqnmLulH8N, recv ret=b'Sorry, your token does not match. Try again!\n' [*] recv ret=b'\nEnter your guess for the token (or exit):'', seed=1741701063303 [*] send ss=j5xEiw8db900Toy3EjC1, recv ret=b'Sorry, your token does not match. Try again!\n' [*] recv ret=b'\nEnter your guess for the token (or exit):'', seed=1741701063304 [*] send ss=jt4XtryCEHxhxzohjbCO, recv ret=b'Sorry, your token does not match. Try again!\n' [*] recv ret=b'\nEnter your guess for the token (or exit):'', seed=1741701063305 [*] send ss=vq1qnqESQihBi9L8eEmN, recv ret=b'Sorry, your token does not match. Try again!\n' [*] recv ret=b'\nEnter your guess for the token (or exit):'', seed=1741701063306 [*] send ss=bfQDEvV8nbbxmVEp21Oa, recv ret=b'Sorry, your token does not match. Try again!\n' [*] recv ret=b'\nEnter your guess for the token (or exit):'', seed=1741701063307 [*] send ss=6T40hEAGtddkQ7bSjXzF, recv ret=b'Sorry, your token does not match. Try again!\n' [*] recv ret=b'\nEnter your guess for the token (or exit):'', seed=1741701063308 [*] send ss=K7gBgnZpJipAOCQoG4FO, recv ret=b'Sorry, your token does not match. Try again!\n' [*] recv ret=b'\nEnter your guess for the token (or exit):'', seed=1741701063309 [*] send ss=MgdeQQt5UnxZPIVv8bWZ, recv ret=b'Sorry, your token does not match. Try again!\n' [*] recv ret=b'\nEnter your guess for the token (or exit):'', seed=1741701063310 [*] send ss=XzZcLl3W5OCtbmaeAnfx, recv ret=b'Congratulations! You found the correct token.\n' /home/user/20240819/lib/python3.11/site-packages/pwnlib/log.py:396: BytesWarning: Bytes is not text; assuming ASCII, no guarantees. See https://docs.pwntools.com/#bytes self._log(logging.INFO, message, args, kwargs, 'info') [*] picoCTF{UseSecure#$_Random@j3n3r@T0rsde389b79} ********* ret=b'Congratulations! You found the correct token.\n' ********* [x] Opening connection to verbal-sleep.picoctf.net on port 64908 [x] Opening connection to verbal-sleep.picoctf.net on port 64908: Trying 3.138.217.147 [+] Opening connection to verbal-sleep.picoctf.net on port 64908: Done [*] recv ret=b'Welcome to the token generation challenge!\nCan you guess the token?\n\nEnter your guess for the token (or exit):'', seed=1741701074622 [*] send ss=iRxlltvft4QWUWdw11IL, recv ret=b'Sorry, your token does not match. Try again!\n' [*] recv ret=b'\nEnter your guess for the token (or exit):'', seed=1741701074623 [*] send ss=FMiKR1WRoC6zEh7XkDu0, recv ret=b'Sorry, your token does not match. Try again!\n'
Quantum Scrambler(200 points)
Pythonスクリプト(quantum_scrambler.py)をダウンロードできます。サーバに接続して進める問題のようです。

Pythonスクリプトは以下です。
まず、フラグを読み出し、1文字ずつ、数値に変換して、16進数文字列(例:"0x70")にして、それをリストにして、hex_flagリストに追加(例:[['0x70'], ['0x69'], ...])します。その hex_flagリストを引数にして、scramble関数を呼び出します。そこでは、hex_flagリストの中身をいろいろ移動してるようです。
import sys def exit(): sys.exit(0) def scramble(L): A = L i = 2 while (i < len(A)): A[i-2] += A.pop(i-1) A[i-1].append(A[:i-2]) i += 1 return L def get_flag(): flag = open('flag.txt', 'r').read() flag = flag.strip() hex_flag = [] for c in flag: hex_flag.append([str(hex(ord(c)))]) return hex_flag def main(): flag = get_flag() cypher = scramble(flag) print(cypher) if __name__ == '__main__': main()
サーバに接続してみます。すると、大量の cipher が表示されます。あまりに多いので最初の方だけ貼ります。
$ nc verbal-sleep.picoctf.net 63779 [['0x70', '0x69'], ['0x63', [], '0x6f'], ['0x43', [['0x70', '0x69']], '0x54'], ['0x46', [['0x70', '0x69'], ['0x63', [], '0x6f']], '0x7b'], ['0x70', [['0x70', '0x69'], ['0x63', [], '0x6f'], ['0x43', [['0x70', '0x69']], '0x54']], '0x79'], ['0x74', [['0x70', '0x69'], ['0x63', [], '0x6f'], ['0x43', [['0x70', '0x69']], '0x54'], ['0x46', [['0x70', '0x69'], ['0x63', [], '0x6f']], '0x7b']], '0x68'], ['0x6f', [['0x70', '0x69'], ['0x63', [], '0x6f'], ['0x43', [['0x70', '0x69']], '0x54'], ['0x46', [['0x70', '0x69'], ['0x63', [], '0x6f']], '0x7b'], ['0x70', [['0x70', '0x69'], ['0x63', [], '0x6f'], ['0x43', [['0x70', '0x69']], '0x54']], '0x79']], '0x6e'], ['0x5f', [['0x70', '0x69'], ['0x63', [], '0x6f'], ['0x43', [['0x70', '0x69']], '0x54'], ['0x46', [['0x70', '0x69'], ['0x63', [], '0x6f']], '0x7b'], ['0x70', [['0x70', '0x69'], ['0x63', [], '0x6f'], ['0x43', [['0x70', '0x69']], '0x54']], '0x79'], ['0x74', [['0x70', '0x69'], ['0x63', [], '0x6f'], ['0x43', [['0x70', '0x69']], '0x54'], ['0x46', [['0x70', '0x69'], ['0x63', [], '0x6f']], '0x7b']], '0x68']], '0x69'], ['0x73', [['0x70', '0x69'], ['0x63', [], '0x6f'], ['0x43', [['0x70', '0x69']], '0x54'], ['0x46', [['0x70', '0x69'], ['0x63', [], '0x6f']], '0x7b'], ['0x70', [['0x70', '0x69'], ['0x63', [], '0x6f'], ['0x43', [['0x70', '0x69']], '0x54']], '0x79'], ['0x74', [['0x70', '0x69'], ['0x63', [], '0x6f'], ['0x43', [['0x70', '0x69']], '0x54'], ['0x46', [['0x70', '0x69'], ['0x63', [], '0x6f']], '0x7b']], '0x68'], ['0x6f', [['0x70', '0x69'], ['0x63', [], '0x6f'], ['0x43', [['0x70', '0x69']], '0x54'], ['0x46', [['0x70', '0x69'], ['0x63', [], '0x6f']], '0x7b'], ['0x70', [['0x70', '0x69'], ['0x63', [], '0x6f'], ['0x43', [['0x70', '0x69']], '0x54']], '0x79']], '0x6e']], '0x5f'], ['0x77', [['0x70', '0x69'], ['0x63', [], '0x6f'], ['0x43', [['0x70', '0x69']], '0x54'], ['0x46', [['0x70', '0x69'], ['0x63', [], '0x6f']], '0x7b'], ['0x70', [['0x70', '0x69'], ['0x63', [], '0x6f'], ['0x43', [['0x70', '0x69']], '0x54']], '0x79'], ['0x74', [['0x70', '0x69'], ['0x63', [], '0x6f'], ['0x43', [['0x70', '0x69']], '0x54'], ['0x46', [['0x70', '0x69'], ['0x63', [], '0x6f']], '0x7b']], '0x68'], ['0x6f', [['0x70', '0x69'], ['0x63', [], '0x6f'], ['0x43', [['0x70', '0x69']], '0x54'], ['0x46', [['0x70', '0x69'], ['0x63', [], '0x6f']], '0x7b'], ['0x70', [['0x70', '0x69'], ['0x63', [], '0x6f'], ['0x43', [['0x70', '0x69']], '0x54']], '0x79']], '0x6e'], ['0x5f', [['0x70', '0x69'], ['0x63', [], '0x6f'], ['0x43', [['0x70', '0x69']], '0x54'], ['0x46', [['0x70', '0x69'], ['0x63', [], '0x6f']], '0x7b'], ['0x70', [['0x70', '0x69'], ['0x63', [], '0x6f'], ['0x43', [['0x70', '0x69']], '0x54']], '0x79'], ['0x74', [['0x70', '0x69'], ['0x63', [], '0x6f'], ['0x43', [['0x70', '0x69']], '0x54'], ['0x46', [['0x70', '0x69'], ['0x63', [], '0x6f']], '0x7b']], '0x68']], '0x69']], '0x65'], ['0x69', [['0x70', '0x69'], ['0x63', [], '0x6f'], ['0x43', [['0x70', '0x69']], '0x54'], ['0x46', [['0x70', '0x69'], ['0x63', [], '0x6f']], '0x7b'], ['0x70', [['0x70', '0x69'], ['0x63', [], '0x6f'], ['0x43', [['0x70', '0x69']], '0x54']], '0x79'], ['0x74', [['0x70', '0x69'], ['0x63', [], '0x6f'], ['0x43', [['0x70', '0x69']], '0x54'], ['0x46', [['0x70', '0x69'], ['0x63', [], '0x6f']], '0x7b']], '0x68'], ['0x6f', [['0x70', '0x69'], ['0x63', [], '0x6f'], ['0x43', [['0x70', '0x69']], '0x54'], ['0x46', [['0x70', '0x69'], ['0x63', [], '0x6f']], '0x7b'], ['0x70', [['0x70', '0x69'], ['0x63', [], '0x6f'], ['0x43', [['0x70', '0x69']], '0x54']], '0x79']], '0x6e'], ['0x5f', [['0x70', '0x69'], ['0x63', [], '0x6f'], ['0x43', [['0x70', '0x69']], '0x54'], ['0x46', [['0x70', '0x69'], ['0x63', [], '0x6f']], '0x7b'], ['0x70', [['0x70', '0x69'], ['0x63', [], '0x6f'], ['0x43', [['0x70', '0x69']], '0x54']], '0x79'], ['0x74', [['0x70', '0x69'], ['0x63', [], '0x6f'], ['0x43', [['0x70', '0x69']], '0x54'], ['0x46', [['0x70', '0x69'], ['0x63', [], '0x6f']], '0x7b']], '0x68']], '0x69'], ['0x73', [['0x70', '0x69'], ['0x63', [], '0x6f'], ['0x43', [['0x70', '0x69']], '0x54'], ['0x46', [['0x70', '0x69'], ['0x63', [], '0x6f']], '0x7b'], ['0x70', [['0x70', '0x69'], ['0x63', [], '0x6f'], ['0x43', [['0x70', '0x69']], '0x54']], '0x79'], ['0x74', [['0x70', '0x69'], ['0x63', [], '0x6f'], ['0x43', [['0x70', '0x69']], '0x54'], ['0x46', [['0x70', '0x69'], ['0x63', [], '0x6f']], '0x7b']], '0x68'], ['0x6f', [['0x70', '0x69'], ['0x63', [], '0x6f'], ['0x43', [['0x70', '0x69']], '0x54'], ['0x46', [['0x70', '0x69'], ['0x63', [], '0x6f']], '0x7b'], ['0x70', [['0x70', '0x69'], ['0x63', [], '0x6f'], ['0x43', [['0x70', '0x69']], '0x54']], '0x79']], '0x6e']], '0x5f']], '0x72'], ['0x64', [['0x70', '0x69'], ['0x63', [], '0x6f'], ['0x43', [['0x70', '0x69']], '0x54'], ['0x46', [['0x70', '0x69'], ['0x63', [], '0x6f']], '0x7b'], ['0x70', [['0x70', '0x69'], ['0x63', [], '0x6f'], ['0x43', [['0x70', '0x69']], '0x54']], '0x79'], ['0x74', [['0x70', '0x69'], ['0x63', [], '0x6f'], ['0x43', [['0x70', '0x69']], '0x54'], ['0x46', [['0x70', '0x
結局、どうすればいいのでしょうか。この大きな cipher を復号して、フラグを抽出するということだと思います。では、逆算するような Pythonスクリプトを作ってみます。
その前に、ちょっとした Pythonスクリプトを作りました。ただし、お題の cipher が大きいので、そこは省略して貼りました。仮のフラグを用意して、i と len(L) がどのように推移していくのかを見えるようにしました。
import sys import copy cipher = [['0x70', '0x69'], ['0x63', [], '0x6f'], ...] def scramble(L): A = L i = 2 print( f"i={i}, len(L)={len(L)}" ) while (i < len(A)): A[i-2] += A.pop(i-1) A[i-1].append(A[:i-2]) i += 1 print( f"i={i}, len(L)={len(L)}" ) return L def main(): print( f"len(cipher)={len(cipher)}" ) flag = [['0x70'], ['0x69'], ['0x63'], ['0x6f'], ['0x43'], ['0x54'], ['0x46'], ['0x7b'], ['0x64'], ['0x65'], ['0x61'], ['0x64'], ['0x62'], ['0x65'], ['0x65'], ['0x66'], ['0x7d'], ['0x70'], ['0x69'], ['0x63'], ['0x6f'], ['0x43'], ['0x54'], ['0x46'], ['0x7b'], ['0x64'], ['0x65'], ['0x61'], ['0x64'], ['0x62'], ['0x65'], ['0x65'], ['0x66'], ['0x7d']] print( f"len(flag)={len(flag)}" ) scramble( copy.deepcopy(flag) ) flag.pop( 0 ) print( f"len(flag)={len(flag)}" ) scramble( copy.deepcopy(flag) ) flag.pop( 0 ) print( f"len(flag)={len(flag)}" ) scramble( copy.deepcopy(flag) ) flag.pop( 0 ) print( f"len(flag)={len(flag)}" ) scramble( copy.deepcopy(flag) ) if __name__ == '__main__': main()
実行します。お題の cipher は、要素数が 17 のようです。scramble関数では、L は、要素数が 1つずつ減っていきます。一方、i は 2 から始まって、1つずつ増えていきます。cipher の要素数が 17 になるパターンは 2つあります。2通り試せばいいですね。
$ python quantum_scrambler_pre.py len(cipher)=17 len(flag)=34 i=2, len(L)=34 i=3, len(L)=33 i=4, len(L)=32 i=5, len(L)=31 i=6, len(L)=30 i=7, len(L)=29 i=8, len(L)=28 i=9, len(L)=27 i=10, len(L)=26 i=11, len(L)=25 i=12, len(L)=24 i=13, len(L)=23 i=14, len(L)=22 i=15, len(L)=21 i=16, len(L)=20 i=17, len(L)=19 i=18, len(L)=18 len(flag)=33 i=2, len(L)=33 i=3, len(L)=32 i=4, len(L)=31 i=5, len(L)=30 i=6, len(L)=29 i=7, len(L)=28 i=8, len(L)=27 i=9, len(L)=26 i=10, len(L)=25 i=11, len(L)=24 i=12, len(L)=23 i=13, len(L)=22 i=14, len(L)=21 i=15, len(L)=20 i=16, len(L)=19 i=17, len(L)=18 i=18, len(L)=17 len(flag)=32 i=2, len(L)=32 i=3, len(L)=31 i=4, len(L)=30 i=5, len(L)=29 i=6, len(L)=28 i=7, len(L)=27 i=8, len(L)=26 i=9, len(L)=25 i=10, len(L)=24 i=11, len(L)=23 i=12, len(L)=22 i=13, len(L)=21 i=14, len(L)=20 i=15, len(L)=19 i=16, len(L)=18 i=17, len(L)=17 len(flag)=31 i=2, len(L)=31 i=3, len(L)=30 i=4, len(L)=29 i=5, len(L)=28 i=6, len(L)=27 i=7, len(L)=26 i=8, len(L)=25 i=9, len(L)=24 i=10, len(L)=23 i=11, len(L)=22 i=12, len(L)=21 i=13, len(L)=20 i=14, len(L)=19 i=15, len(L)=18 i=16, len(L)=17 i=17, len(L)=16
逆に操作する Pythonスクリプトを実装しました。
import sys cipher = [['0x70', '0x69'], ['0x63', [], '0x6f'], ...] def main(): global cipher print( f"len(cipher)={len(cipher)}" ) # whileループを抜けるときは、i と len(A) は同じ値?のはず # i=18, len(L)=17 → i=17, len(L)=17 #ii = 17 ii = 16 while( ii >= 2 ): # A[16].append(A[:15]):A[:15] を A[16] の末尾に追加 → A[16] の末尾の要素を削除する print( f"ii={ii}, len(cipher[ii-1])={len(cipher[ii-1])}" ) cipher[ii - 1].pop( -1 ) # A[15] += A.pop(16): → A[15] の末尾の要素を抽出して、A の末尾に追加する cipher.insert( ii - 1, [cipher[ii - 2].pop(-1)] ) ii -= 1 print( f"ii={ii}, len(cipher)={len(cipher)}" ) print( cipher ) ss = "" for cc in cipher: ss += chr( int(cc[0], 16) ) print( ss ) if __name__ == '__main__': main()
実行します。フラグが表示されました。
$ python quantum_scrambler_decrypt.py len(cipher)=17 ii=16, len(cipher[ii-1])=2 ii=15, len(cipher)=18 ii=15, len(cipher[ii-1])=2 ii=14, len(cipher)=19 ii=14, len(cipher[ii-1])=2 ii=13, len(cipher)=20 ii=13, len(cipher[ii-1])=2 ii=12, len(cipher)=21 ii=12, len(cipher[ii-1])=2 ii=11, len(cipher)=22 ii=11, len(cipher[ii-1])=2 ii=10, len(cipher)=23 ii=10, len(cipher[ii-1])=2 ii=9, len(cipher)=24 ii=9, len(cipher[ii-1])=2 ii=8, len(cipher)=25 ii=8, len(cipher[ii-1])=2 ii=7, len(cipher)=26 ii=7, len(cipher[ii-1])=2 ii=6, len(cipher)=27 ii=6, len(cipher[ii-1])=2 ii=5, len(cipher)=28 ii=5, len(cipher[ii-1])=2 ii=4, len(cipher)=29 ii=4, len(cipher[ii-1])=2 ii=3, len(cipher)=30 ii=3, len(cipher[ii-1])=2 ii=2, len(cipher)=31 ii=2, len(cipher[ii-1])=2 ii=1, len(cipher)=32 [['0x70'], ['0x69'], ['0x63'], ['0x6f'], ['0x43'], ['0x54'], ['0x46'], ['0x7b'], ['0x70'], ['0x79'], ['0x74'], ['0x68'], ['0x6f'], ['0x6e'], ['0x5f'], ['0x69'], ['0x73'], ['0x5f'], ['0x77'], ['0x65'], ['0x69'], ['0x72'], ['0x64'], ['0x66'], ['0x39'], ['0x64'], ['0x65'], ['0x34'], ['0x34'], ['0x39'], ['0x61'], ['0x7d']] picoCTF{python_is_weirdf9de449a}
Binary Instrumentation 2(300 points)
1つの ZIPファイル(bininst2.zip)がダウンロードできます。今度は、ウィルスとは検知されませんでした。と思ったら、解凍したらウィルスと検知しました。

確認できませんでしたが、Windows の実行ファイルだと思います。Windows は後回しにします。
perplexed(400 points)
1つの実行ファイル(perplexed)がダウンロードできます。問題文には何も書かれていません。

とりあえず、表層解析します。
$ file perplexed perplexed: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=85480b12e666f376909d57d282a1ef0f30e93db4, for GNU/Linux 3.2.0, not stripped $ ~/bin/checksec --file=perplexed RELRO STACK CANARY NX PIE RPATH RUNPATH Symbols FORTIFY Fortified Fortifiable FILE Partial RELRO No canary found NX enabled No PIE No RPATH No RUNPATH 39 Symbols No 0 2 perplexed $ pwn checksec --file=perplexed [*] '/home/user/svn/experiment/picoCTF/picoCTF2025_ReverseEngineering/perplexed' Arch: amd64-64-little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x400000) Stripped: No
実行してみます。パスワードを入力させるプログラムのようです。
$ ./perplexed
Enter the password: xxxxxxxx
Wrong :(
Ghidra で見ていきます。
main関数は、ユーザにパスワードを入力させて、check関数を実行して、その戻り値が 1以外なら成功ということのようです。
bool main(void) { bool bVar1; undefined8 local_118; undefined8 local_110; undefined8 local_108; undefined8 local_100; undefined8 local_f8; undefined8 local_f0; undefined8 local_e8; undefined8 local_e0; undefined8 local_d8; undefined8 local_d0; undefined8 local_c8; undefined8 local_c0; undefined8 local_b8; undefined8 local_b0; undefined8 local_a8; undefined8 local_a0; undefined8 local_98; undefined8 local_90; undefined8 local_88; undefined8 local_80; undefined8 local_78; undefined8 local_70; undefined8 local_68; undefined8 local_60; undefined8 local_58; undefined8 local_50; undefined8 local_48; undefined8 local_40; undefined8 local_38; undefined8 local_30; undefined8 local_28; undefined8 local_20; int local_c; local_118 = 0; local_110 = 0; local_108 = 0; local_100 = 0; local_f8 = 0; local_f0 = 0; local_e8 = 0; local_e0 = 0; local_d8 = 0; local_d0 = 0; local_c8 = 0; local_c0 = 0; local_b8 = 0; local_b0 = 0; local_a8 = 0; local_a0 = 0; local_98 = 0; local_90 = 0; local_88 = 0; local_80 = 0; local_78 = 0; local_70 = 0; local_68 = 0; local_60 = 0; local_58 = 0; local_50 = 0; local_48 = 0; local_40 = 0; local_38 = 0; local_30 = 0; local_28 = 0; local_20 = 0; printf("Enter the password: "); fgets((char *)&local_118,0x100,stdin); local_c = check(&local_118); bVar1 = local_c != 1; if (bVar1) { puts("Correct!! :D"); } else { puts("Wrong :("); } return !bVar1; }
次は、check関数を見てみます。パスワード長は 27文字でなければなりません。実際は改行が含まれるようなので、26文字でした。
ちょっと分かりにくいですが、スタックの rbp - 0x50 から、23byte のデータがあります。ざっくり言うと、この 23byte のデータと、パスワードのデータをビット単位で比較して、XOR して、0 になるビットであれば、それが正しいパスワードということになります。ただし、23byte のデータで使うビット位置とパスワードのビット位置は異なる動きをするようなので、そこの見極めが難しそうです。
undefined8 check(char *param_1) { size_t sVar1; undefined8 uVar2; size_t sVar3; undefined8 local_58; undefined7 local_50; undefined uStack_49; undefined7 uStack_48; uint local_34; uint local_30; undefined4 local_2c; int local_28; uint local_24; int local_20; int local_1c; sVar1 = strlen(param_1); if (sVar1 == 0x1b) { local_58 = 0x617b2375f81ea7e1; local_50 = 0x69df5b5afc9db9; uStack_49 = 0xd2; uStack_48 = 0xf467edf4ed1bfe; local_1c = 0; local_20 = 0; local_2c = 0; for (local_24 = 0; local_24 < 0x17; local_24 = local_24 + 1) { for (local_28 = 0; local_28 < 8; local_28 = local_28 + 1) { if (local_20 == 0) { local_20 = 1; } local_30 = 1 << (7U - (char)local_28 & 0x1f); local_34 = 1 << (7U - (char)local_20 & 0x1f); if (0 < (int)((int)param_1[local_1c] & local_34) != 0 < (int)((int)*(char *)((long)&local_58 + (long)(int)local_24) & local_30)) { return 1; } local_20 = local_20 + 1; if (local_20 == 8) { local_20 = 0; local_1c = local_1c + 1; } sVar3 = (size_t)local_1c; sVar1 = strlen(param_1); if (sVar3 == sVar1) { return 0; } } } uVar2 = 0; } else { uVar2 = 1; } return uVar2; }
スタックを可視化します。
| アドレス | サイズ | 内容 |
|---|---|---|
| rbp | ||
| rbp - 0x14 | 4 | local_1c |
| rbp - 0x18 | 4 | local_20(1→2→...→7→1→...) |
| rbp - 0x1C | 4 | local_24 |
| rbp - 0x20 | 4 | local_28 |
| rbp - 0x24 | 4 | 空き |
| rbp - 0x28 | 4 | local_30(0x80→0x40→...→0x00→0x80→...) |
| rbp - 0x2C | 4 | local_34(0x40→0x20→...→0x00→0x40→...) |
| rbp - 0x40 | 8 | fe, 1b, ed, f4, ed, 67, f4, 00(0x00f467edf4ed1bfe) |
| rbp - 0x48 | 8 | b9, 9d, fc, 5a, 5b, df, 69, d2(0xd269df5b5afc9db9) |
| rbp - 0x50 | 8 | e1, a7, 1e, f8, 75, 23, 7b, 61(0x617b2375f81ea7e1) |
| rbp - 0x58 | 8 | passwordのアドレス |
GDB で表示したアセンブラも貼っておきます。
pwndbg> disassemble Dump of assembler code for function check: 0x0000000000401156 <+0>: push rbp 0x0000000000401157 <+1>: mov rbp,rsp 0x000000000040115a <+4>: push rbx 0x000000000040115b <+5>: sub rsp,0x58 0x000000000040115f <+9>: mov QWORD PTR [rbp-0x58],rdi 0x0000000000401163 <+13>: mov rax,QWORD PTR [rbp-0x58] 0x0000000000401167 <+17>: mov rdi,rax 0x000000000040116a <+20>: call 0x401040 <strlen@plt> 0x000000000040116f <+25>: cmp rax,0x1b 0x0000000000401173 <+29>: je 0x40117f <check+41> 0x0000000000401175 <+31>: mov eax,0x1 0x000000000040117a <+36>: jmp 0x40129f <check+329> 0x000000000040117f <+41>: movabs rax,0x617b2375f81ea7e1 0x0000000000401189 <+51>: movabs rdx,0xd269df5b5afc9db9 0x0000000000401193 <+61>: mov QWORD PTR [rbp-0x50],rax 0x0000000000401197 <+65>: mov QWORD PTR [rbp-0x48],rdx 0x000000000040119b <+69>: movabs rax,0xf467edf4ed1bfed2 0x00000000004011a5 <+79>: mov QWORD PTR [rbp-0x41],rax 0x00000000004011a9 <+83>: mov DWORD PTR [rbp-0x14],0x0 0x00000000004011b0 <+90>: mov DWORD PTR [rbp-0x18],0x0 0x00000000004011b7 <+97>: mov DWORD PTR [rbp-0x24],0x0 0x00000000004011be <+104>: mov DWORD PTR [rbp-0x1c],0x0 0x00000000004011c5 <+111>: jmp 0x40128e <check+312> 0x00000000004011ca <+116>: mov DWORD PTR [rbp-0x20],0x0 0x00000000004011d1 <+123>: jmp 0x401280 <check+298> 0x00000000004011d6 <+128>: cmp DWORD PTR [rbp-0x18],0x0 0x00000000004011da <+132>: jne 0x4011e0 <check+138> 0x00000000004011dc <+134>: add DWORD PTR [rbp-0x18],0x1 0x00000000004011e0 <+138>: mov eax,0x7 0x00000000004011e5 <+143>: sub eax,DWORD PTR [rbp-0x20] 0x00000000004011e8 <+146>: mov edx,0x1 0x00000000004011ed <+151>: mov ecx,eax 0x00000000004011ef <+153>: shl edx,cl 0x00000000004011f1 <+155>: mov eax,edx 0x00000000004011f3 <+157>: mov DWORD PTR [rbp-0x28],eax 0x00000000004011f6 <+160>: mov eax,0x7 0x00000000004011fb <+165>: sub eax,DWORD PTR [rbp-0x18] 0x00000000004011fe <+168>: mov edx,0x1 0x0000000000401203 <+173>: mov ecx,eax 0x0000000000401205 <+175>: shl edx,cl 0x0000000000401207 <+177>: mov eax,edx 0x0000000000401209 <+179>: mov DWORD PTR [rbp-0x2c],eax 0x000000000040120c <+182>: mov eax,DWORD PTR [rbp-0x1c] 0x000000000040120f <+185>: cdqe 0x0000000000401211 <+187>: movzx eax,BYTE PTR [rbp+rax*1-0x50] 0x0000000000401216 <+192>: movsx eax,al 0x0000000000401219 <+195>: and eax,DWORD PTR [rbp-0x28] 0x000000000040121c <+198>: test eax,eax 0x000000000040121e <+200>: setg cl 0x0000000000401221 <+203>: mov eax,DWORD PTR [rbp-0x14] 0x0000000000401224 <+206>: movsxd rdx,eax 0x0000000000401227 <+209>: mov rax,QWORD PTR [rbp-0x58] 0x000000000040122b <+213>: add rax,rdx 0x000000000040122e <+216>: movzx eax,BYTE PTR [rax] 0x0000000000401231 <+219>: movsx eax,al 0x0000000000401234 <+222>: and eax,DWORD PTR [rbp-0x2c] 0x0000000000401237 <+225>: test eax,eax 0x0000000000401239 <+227>: setg al 0x000000000040123c <+230>: xor eax,ecx 0x000000000040123e <+232>: test al,al => 0x0000000000401240 <+234>: je 0x401249 <check+243> 0x0000000000401242 <+236>: mov eax,0x1 0x0000000000401247 <+241>: jmp 0x40129f <check+329> 0x0000000000401249 <+243>: add DWORD PTR [rbp-0x18],0x1 0x000000000040124d <+247>: cmp DWORD PTR [rbp-0x18],0x8 0x0000000000401251 <+251>: jne 0x40125e <check+264> 0x0000000000401253 <+253>: mov DWORD PTR [rbp-0x18],0x0 0x000000000040125a <+260>: add DWORD PTR [rbp-0x14],0x1 0x000000000040125e <+264>: mov eax,DWORD PTR [rbp-0x14] 0x0000000000401261 <+267>: movsxd rbx,eax 0x0000000000401264 <+270>: mov rax,QWORD PTR [rbp-0x58] 0x0000000000401268 <+274>: mov rdi,rax 0x000000000040126b <+277>: call 0x401040 <strlen@plt> 0x0000000000401270 <+282>: cmp rbx,rax 0x0000000000401273 <+285>: jne 0x40127c <check+294> 0x0000000000401275 <+287>: mov eax,0x0 0x000000000040127a <+292>: jmp 0x40129f <check+329> 0x000000000040127c <+294>: add DWORD PTR [rbp-0x20],0x1 0x0000000000401280 <+298>: cmp DWORD PTR [rbp-0x20],0x7 0x0000000000401284 <+302>: jle 0x4011d6 <check+128> 0x000000000040128a <+308>: add DWORD PTR [rbp-0x1c],0x1 0x000000000040128e <+312>: mov eax,DWORD PTR [rbp-0x1c] 0x0000000000401291 <+315>: cmp eax,0x16 0x0000000000401294 <+318>: jbe 0x4011ca <check+116> 0x000000000040129a <+324>: mov eax,0x0 0x000000000040129f <+329>: mov rbx,QWORD PTR [rbp-0x8] 0x00000000004012a3 <+333>: leave 0x00000000004012a4 <+334>: ret End of assembler dump.
同じ動きをする Pythonスクリプトを作ります。条件を満たすビットを論理和で重ねていけば、求められているパスワードが作られていく、みたいな感じにしてみます。
def main(): password = "abcdefghijklmnopqrstuvwxyz" lst = [ 0xe1, 0xa7, 0x1e, 0xf8, 0x75, 0x23, 0x7b, 0x61, 0xb9, 0x9d, 0xfc, 0x5a, 0x5b, 0xdf, 0x69, 0xd2, 0xfe, 0x1b, 0xed, 0xf4, 0xed, 0x67, 0xf4, 0x00, 0x00, 0x00, 0x00, 0x00 ] ret = [ 0 ] * 27 l_1c = 0 l_20 = 0 for l_24 in range( 23 ): for l_28 in range( 8 ): if l_20 == 0: l_20 = 1 l_30 = 1 << ( (7 - l_28) & 0x1F ) l_34 = 1 << ( (7 - l_20) & 0x1F ) #if (0 < ord(password[l_1c]) & l_34) != (0 < (lst[l_24] & l_30)): # print( "failure" ) if (lst[l_24] & l_30) != 0: ret[l_1c] |= l_34 print( f"ii={l_24:2d}, jj={l_28}: l_30=0x{l_30:02X}, l_34=0x{l_34:02X}, l_20={l_20}, l_1c={l_1c}" ) l_20 += 1 if l_20 == 8: l_20 = 0 l_1c += 1 if l_1c == 27: print( "success" ) ret = [ chr(ii) for ii in ret ] ret = "".join( ret ) print( ret ) if __name__ == "__main__": main()
実行してみます。出ました!
$ python perplexed.py ii= 0, jj=0: l_30=0x80, l_34=0x40, l_20=1, l_1c=0 ii= 0, jj=1: l_30=0x40, l_34=0x20, l_20=2, l_1c=0 ii= 0, jj=2: l_30=0x20, l_34=0x10, l_20=3, l_1c=0 ii= 0, jj=3: l_30=0x10, l_34=0x08, l_20=4, l_1c=0 ii= 0, jj=4: l_30=0x08, l_34=0x04, l_20=5, l_1c=0 ii= 0, jj=5: l_30=0x04, l_34=0x02, l_20=6, l_1c=0 ii= 0, jj=6: l_30=0x02, l_34=0x01, l_20=7, l_1c=0 ii= 0, jj=7: l_30=0x01, l_34=0x40, l_20=1, l_1c=1 ii= 1, jj=0: l_30=0x80, l_34=0x20, l_20=2, l_1c=1 ii= 1, jj=1: l_30=0x40, l_34=0x10, l_20=3, l_1c=1 ii= 1, jj=2: l_30=0x20, l_34=0x08, l_20=4, l_1c=1 ii= 1, jj=3: l_30=0x10, l_34=0x04, l_20=5, l_1c=1 ii= 1, jj=4: l_30=0x08, l_34=0x02, l_20=6, l_1c=1 ii= 1, jj=5: l_30=0x04, l_34=0x01, l_20=7, l_1c=1 ii= 1, jj=6: l_30=0x02, l_34=0x40, l_20=1, l_1c=2 ii= 1, jj=7: l_30=0x01, l_34=0x20, l_20=2, l_1c=2 ii= 2, jj=0: l_30=0x80, l_34=0x10, l_20=3, l_1c=2 ii= 2, jj=1: l_30=0x40, l_34=0x08, l_20=4, l_1c=2 ii= 2, jj=2: l_30=0x20, l_34=0x04, l_20=5, l_1c=2 ii= 2, jj=3: l_30=0x10, l_34=0x02, l_20=6, l_1c=2 ii= 2, jj=4: l_30=0x08, l_34=0x01, l_20=7, l_1c=2 ii= 2, jj=5: l_30=0x04, l_34=0x40, l_20=1, l_1c=3 ii= 2, jj=6: l_30=0x02, l_34=0x20, l_20=2, l_1c=3 ii= 2, jj=7: l_30=0x01, l_34=0x10, l_20=3, l_1c=3 ii= 3, jj=0: l_30=0x80, l_34=0x08, l_20=4, l_1c=3 ii= 3, jj=1: l_30=0x40, l_34=0x04, l_20=5, l_1c=3 ii= 3, jj=2: l_30=0x20, l_34=0x02, l_20=6, l_1c=3 ii= 3, jj=3: l_30=0x10, l_34=0x01, l_20=7, l_1c=3 ii= 3, jj=4: l_30=0x08, l_34=0x40, l_20=1, l_1c=4 ii= 3, jj=5: l_30=0x04, l_34=0x20, l_20=2, l_1c=4 ii= 3, jj=6: l_30=0x02, l_34=0x10, l_20=3, l_1c=4 ii= 3, jj=7: l_30=0x01, l_34=0x08, l_20=4, l_1c=4 ii= 4, jj=0: l_30=0x80, l_34=0x04, l_20=5, l_1c=4 ii= 4, jj=1: l_30=0x40, l_34=0x02, l_20=6, l_1c=4 ii= 4, jj=2: l_30=0x20, l_34=0x01, l_20=7, l_1c=4 ii= 4, jj=3: l_30=0x10, l_34=0x40, l_20=1, l_1c=5 ii= 4, jj=4: l_30=0x08, l_34=0x20, l_20=2, l_1c=5 ii= 4, jj=5: l_30=0x04, l_34=0x10, l_20=3, l_1c=5 ii= 4, jj=6: l_30=0x02, l_34=0x08, l_20=4, l_1c=5 ii= 4, jj=7: l_30=0x01, l_34=0x04, l_20=5, l_1c=5 ii= 5, jj=0: l_30=0x80, l_34=0x02, l_20=6, l_1c=5 ii= 5, jj=1: l_30=0x40, l_34=0x01, l_20=7, l_1c=5 ii= 5, jj=2: l_30=0x20, l_34=0x40, l_20=1, l_1c=6 ii= 5, jj=3: l_30=0x10, l_34=0x20, l_20=2, l_1c=6 ii= 5, jj=4: l_30=0x08, l_34=0x10, l_20=3, l_1c=6 ii= 5, jj=5: l_30=0x04, l_34=0x08, l_20=4, l_1c=6 ii= 5, jj=6: l_30=0x02, l_34=0x04, l_20=5, l_1c=6 ii= 5, jj=7: l_30=0x01, l_34=0x02, l_20=6, l_1c=6 ii= 6, jj=0: l_30=0x80, l_34=0x01, l_20=7, l_1c=6 ii= 6, jj=1: l_30=0x40, l_34=0x40, l_20=1, l_1c=7 ii= 6, jj=2: l_30=0x20, l_34=0x20, l_20=2, l_1c=7 ii= 6, jj=3: l_30=0x10, l_34=0x10, l_20=3, l_1c=7 ii= 6, jj=4: l_30=0x08, l_34=0x08, l_20=4, l_1c=7 ii= 6, jj=5: l_30=0x04, l_34=0x04, l_20=5, l_1c=7 ii= 6, jj=6: l_30=0x02, l_34=0x02, l_20=6, l_1c=7 ii= 6, jj=7: l_30=0x01, l_34=0x01, l_20=7, l_1c=7 ii= 7, jj=0: l_30=0x80, l_34=0x40, l_20=1, l_1c=8 ii= 7, jj=1: l_30=0x40, l_34=0x20, l_20=2, l_1c=8 ii= 7, jj=2: l_30=0x20, l_34=0x10, l_20=3, l_1c=8 ii= 7, jj=3: l_30=0x10, l_34=0x08, l_20=4, l_1c=8 ii= 7, jj=4: l_30=0x08, l_34=0x04, l_20=5, l_1c=8 ii= 7, jj=5: l_30=0x04, l_34=0x02, l_20=6, l_1c=8 ii= 7, jj=6: l_30=0x02, l_34=0x01, l_20=7, l_1c=8 ii= 7, jj=7: l_30=0x01, l_34=0x40, l_20=1, l_1c=9 ii= 8, jj=0: l_30=0x80, l_34=0x20, l_20=2, l_1c=9 ii= 8, jj=1: l_30=0x40, l_34=0x10, l_20=3, l_1c=9 ii= 8, jj=2: l_30=0x20, l_34=0x08, l_20=4, l_1c=9 ii= 8, jj=3: l_30=0x10, l_34=0x04, l_20=5, l_1c=9 ii= 8, jj=4: l_30=0x08, l_34=0x02, l_20=6, l_1c=9 ii= 8, jj=5: l_30=0x04, l_34=0x01, l_20=7, l_1c=9 ii= 8, jj=6: l_30=0x02, l_34=0x40, l_20=1, l_1c=10 ii= 8, jj=7: l_30=0x01, l_34=0x20, l_20=2, l_1c=10 ii= 9, jj=0: l_30=0x80, l_34=0x10, l_20=3, l_1c=10 ii= 9, jj=1: l_30=0x40, l_34=0x08, l_20=4, l_1c=10 ii= 9, jj=2: l_30=0x20, l_34=0x04, l_20=5, l_1c=10 ii= 9, jj=3: l_30=0x10, l_34=0x02, l_20=6, l_1c=10 ii= 9, jj=4: l_30=0x08, l_34=0x01, l_20=7, l_1c=10 ii= 9, jj=5: l_30=0x04, l_34=0x40, l_20=1, l_1c=11 ii= 9, jj=6: l_30=0x02, l_34=0x20, l_20=2, l_1c=11 ii= 9, jj=7: l_30=0x01, l_34=0x10, l_20=3, l_1c=11 ii=10, jj=0: l_30=0x80, l_34=0x08, l_20=4, l_1c=11 ii=10, jj=1: l_30=0x40, l_34=0x04, l_20=5, l_1c=11 ii=10, jj=2: l_30=0x20, l_34=0x02, l_20=6, l_1c=11 ii=10, jj=3: l_30=0x10, l_34=0x01, l_20=7, l_1c=11 ii=10, jj=4: l_30=0x08, l_34=0x40, l_20=1, l_1c=12 ii=10, jj=5: l_30=0x04, l_34=0x20, l_20=2, l_1c=12 ii=10, jj=6: l_30=0x02, l_34=0x10, l_20=3, l_1c=12 ii=10, jj=7: l_30=0x01, l_34=0x08, l_20=4, l_1c=12 ii=11, jj=0: l_30=0x80, l_34=0x04, l_20=5, l_1c=12 ii=11, jj=1: l_30=0x40, l_34=0x02, l_20=6, l_1c=12 ii=11, jj=2: l_30=0x20, l_34=0x01, l_20=7, l_1c=12 ii=11, jj=3: l_30=0x10, l_34=0x40, l_20=1, l_1c=13 ii=11, jj=4: l_30=0x08, l_34=0x20, l_20=2, l_1c=13 ii=11, jj=5: l_30=0x04, l_34=0x10, l_20=3, l_1c=13 ii=11, jj=6: l_30=0x02, l_34=0x08, l_20=4, l_1c=13 ii=11, jj=7: l_30=0x01, l_34=0x04, l_20=5, l_1c=13 ii=12, jj=0: l_30=0x80, l_34=0x02, l_20=6, l_1c=13 ii=12, jj=1: l_30=0x40, l_34=0x01, l_20=7, l_1c=13 ii=12, jj=2: l_30=0x20, l_34=0x40, l_20=1, l_1c=14 ii=12, jj=3: l_30=0x10, l_34=0x20, l_20=2, l_1c=14 ii=12, jj=4: l_30=0x08, l_34=0x10, l_20=3, l_1c=14 ii=12, jj=5: l_30=0x04, l_34=0x08, l_20=4, l_1c=14 ii=12, jj=6: l_30=0x02, l_34=0x04, l_20=5, l_1c=14 ii=12, jj=7: l_30=0x01, l_34=0x02, l_20=6, l_1c=14 ii=13, jj=0: l_30=0x80, l_34=0x01, l_20=7, l_1c=14 ii=13, jj=1: l_30=0x40, l_34=0x40, l_20=1, l_1c=15 ii=13, jj=2: l_30=0x20, l_34=0x20, l_20=2, l_1c=15 ii=13, jj=3: l_30=0x10, l_34=0x10, l_20=3, l_1c=15 ii=13, jj=4: l_30=0x08, l_34=0x08, l_20=4, l_1c=15 ii=13, jj=5: l_30=0x04, l_34=0x04, l_20=5, l_1c=15 ii=13, jj=6: l_30=0x02, l_34=0x02, l_20=6, l_1c=15 ii=13, jj=7: l_30=0x01, l_34=0x01, l_20=7, l_1c=15 ii=14, jj=0: l_30=0x80, l_34=0x40, l_20=1, l_1c=16 ii=14, jj=1: l_30=0x40, l_34=0x20, l_20=2, l_1c=16 ii=14, jj=2: l_30=0x20, l_34=0x10, l_20=3, l_1c=16 ii=14, jj=3: l_30=0x10, l_34=0x08, l_20=4, l_1c=16 ii=14, jj=4: l_30=0x08, l_34=0x04, l_20=5, l_1c=16 ii=14, jj=5: l_30=0x04, l_34=0x02, l_20=6, l_1c=16 ii=14, jj=6: l_30=0x02, l_34=0x01, l_20=7, l_1c=16 ii=14, jj=7: l_30=0x01, l_34=0x40, l_20=1, l_1c=17 ii=15, jj=0: l_30=0x80, l_34=0x20, l_20=2, l_1c=17 ii=15, jj=1: l_30=0x40, l_34=0x10, l_20=3, l_1c=17 ii=15, jj=2: l_30=0x20, l_34=0x08, l_20=4, l_1c=17 ii=15, jj=3: l_30=0x10, l_34=0x04, l_20=5, l_1c=17 ii=15, jj=4: l_30=0x08, l_34=0x02, l_20=6, l_1c=17 ii=15, jj=5: l_30=0x04, l_34=0x01, l_20=7, l_1c=17 ii=15, jj=6: l_30=0x02, l_34=0x40, l_20=1, l_1c=18 ii=15, jj=7: l_30=0x01, l_34=0x20, l_20=2, l_1c=18 ii=16, jj=0: l_30=0x80, l_34=0x10, l_20=3, l_1c=18 ii=16, jj=1: l_30=0x40, l_34=0x08, l_20=4, l_1c=18 ii=16, jj=2: l_30=0x20, l_34=0x04, l_20=5, l_1c=18 ii=16, jj=3: l_30=0x10, l_34=0x02, l_20=6, l_1c=18 ii=16, jj=4: l_30=0x08, l_34=0x01, l_20=7, l_1c=18 ii=16, jj=5: l_30=0x04, l_34=0x40, l_20=1, l_1c=19 ii=16, jj=6: l_30=0x02, l_34=0x20, l_20=2, l_1c=19 ii=16, jj=7: l_30=0x01, l_34=0x10, l_20=3, l_1c=19 ii=17, jj=0: l_30=0x80, l_34=0x08, l_20=4, l_1c=19 ii=17, jj=1: l_30=0x40, l_34=0x04, l_20=5, l_1c=19 ii=17, jj=2: l_30=0x20, l_34=0x02, l_20=6, l_1c=19 ii=17, jj=3: l_30=0x10, l_34=0x01, l_20=7, l_1c=19 ii=17, jj=4: l_30=0x08, l_34=0x40, l_20=1, l_1c=20 ii=17, jj=5: l_30=0x04, l_34=0x20, l_20=2, l_1c=20 ii=17, jj=6: l_30=0x02, l_34=0x10, l_20=3, l_1c=20 ii=17, jj=7: l_30=0x01, l_34=0x08, l_20=4, l_1c=20 ii=18, jj=0: l_30=0x80, l_34=0x04, l_20=5, l_1c=20 ii=18, jj=1: l_30=0x40, l_34=0x02, l_20=6, l_1c=20 ii=18, jj=2: l_30=0x20, l_34=0x01, l_20=7, l_1c=20 ii=18, jj=3: l_30=0x10, l_34=0x40, l_20=1, l_1c=21 ii=18, jj=4: l_30=0x08, l_34=0x20, l_20=2, l_1c=21 ii=18, jj=5: l_30=0x04, l_34=0x10, l_20=3, l_1c=21 ii=18, jj=6: l_30=0x02, l_34=0x08, l_20=4, l_1c=21 ii=18, jj=7: l_30=0x01, l_34=0x04, l_20=5, l_1c=21 ii=19, jj=0: l_30=0x80, l_34=0x02, l_20=6, l_1c=21 ii=19, jj=1: l_30=0x40, l_34=0x01, l_20=7, l_1c=21 ii=19, jj=2: l_30=0x20, l_34=0x40, l_20=1, l_1c=22 ii=19, jj=3: l_30=0x10, l_34=0x20, l_20=2, l_1c=22 ii=19, jj=4: l_30=0x08, l_34=0x10, l_20=3, l_1c=22 ii=19, jj=5: l_30=0x04, l_34=0x08, l_20=4, l_1c=22 ii=19, jj=6: l_30=0x02, l_34=0x04, l_20=5, l_1c=22 ii=19, jj=7: l_30=0x01, l_34=0x02, l_20=6, l_1c=22 ii=20, jj=0: l_30=0x80, l_34=0x01, l_20=7, l_1c=22 ii=20, jj=1: l_30=0x40, l_34=0x40, l_20=1, l_1c=23 ii=20, jj=2: l_30=0x20, l_34=0x20, l_20=2, l_1c=23 ii=20, jj=3: l_30=0x10, l_34=0x10, l_20=3, l_1c=23 ii=20, jj=4: l_30=0x08, l_34=0x08, l_20=4, l_1c=23 ii=20, jj=5: l_30=0x04, l_34=0x04, l_20=5, l_1c=23 ii=20, jj=6: l_30=0x02, l_34=0x02, l_20=6, l_1c=23 ii=20, jj=7: l_30=0x01, l_34=0x01, l_20=7, l_1c=23 ii=21, jj=0: l_30=0x80, l_34=0x40, l_20=1, l_1c=24 ii=21, jj=1: l_30=0x40, l_34=0x20, l_20=2, l_1c=24 ii=21, jj=2: l_30=0x20, l_34=0x10, l_20=3, l_1c=24 ii=21, jj=3: l_30=0x10, l_34=0x08, l_20=4, l_1c=24 ii=21, jj=4: l_30=0x08, l_34=0x04, l_20=5, l_1c=24 ii=21, jj=5: l_30=0x04, l_34=0x02, l_20=6, l_1c=24 ii=21, jj=6: l_30=0x02, l_34=0x01, l_20=7, l_1c=24 ii=21, jj=7: l_30=0x01, l_34=0x40, l_20=1, l_1c=25 ii=22, jj=0: l_30=0x80, l_34=0x20, l_20=2, l_1c=25 ii=22, jj=1: l_30=0x40, l_34=0x10, l_20=3, l_1c=25 ii=22, jj=2: l_30=0x20, l_34=0x08, l_20=4, l_1c=25 ii=22, jj=3: l_30=0x10, l_34=0x04, l_20=5, l_1c=25 ii=22, jj=4: l_30=0x08, l_34=0x02, l_20=6, l_1c=25 ii=22, jj=5: l_30=0x04, l_34=0x01, l_20=7, l_1c=25 ii=22, jj=6: l_30=0x02, l_34=0x40, l_20=1, l_1c=26 ii=22, jj=7: l_30=0x01, l_34=0x20, l_20=2, l_1c=26 picoCTF{0n3_bi7_4t_a_7im3}
おわりに
今回、picoCTF 2025 にリアルタイムで参戦しています。今回は、Reverse Engineering の全7問のうち、Windowsプログラムの 2問を除いて、全部解けました!(Windowsプログラムもいずれ勉強します。。)
次回は、Binary Exploitation をやっていきます。
最後になりましたが、エンジニアグループのランキングに参加中です。
気楽にポチッとよろしくお願いいたします🙇
今回は以上です!
最後までお読みいただき、ありがとうございました。