前回 は、引き続き、「詳解セキュリティコンテスト: CTFで学ぶ脆弱性攻略の技術 Compass Booksシリーズ」の 33章の「共有ライブラリと関数呼び出し」を読みました。
この書籍はまだ読み続けますが、picoCTF 2025 が 3/7 から始まっていて、昨日(3/17)に終了しました。今回は、リアルタイムで参戦できました。終了するまでは、解法や、フラグを公開することは禁止されていましたので、今日からその内容を公開していきます。今回は、General Skills の全5問です。
はじめに
「セキュリティ」の記事一覧です。良かったら参考にしてください。
・第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問をやってみた ← 今回
picoCTF の公式サイトは以下です。
3/7 から 3/17 までの 10日間で開催されています。

今回は、General Skills をやっていきます。
picoCTF 2025:General Skills
ポイントの低い順にやっていきます。
FANTASY CTF(10 points)
netcat で接続する形式のようです。

では、早速やってみます。物語を読み進めて、競技のルールについて教えてくれました。最後にフラグを表示してくれました。10ポイント獲得です。
$ nc verbal-sleep.picoctf.net 55716 FANTASY CTF SIMULATION The simulation begins in the private room of Eibhilin, a bright, young student. The room is dimly lit, with the glow of her multiple monitors casting an electric blue hue on the walls. Around the room are posters of vintage movies from the MCU — ancient guardians from another age staring down like digital sentinels. --- (Press Enter to continue...) --- Eibhilin stretches back in her chair, adjusting the holo-display of her keyboard. A soft hum of a nearby server fills the air as her AI companion, `Nyx`, comes to life. --- (Press Enter to continue...) --- "Good evening, Ei," Nyx chirps, "The 3025 edition of picoCTF registration is open. You asked me to remind so you could try out the competition for the first time. Do you wish to proceed?" --- (Press Enter to continue...) --- Outside, the city of Nexalus glimmers under the stars, but Eibhilin's focus remains entirely on the screen in front of her. --- (Press Enter to continue...) --- "Yes, Nyx. Let's do it!" --- (Press Enter to continue...) --- Nyx brings up the registration page. Options: A) *Register multiple accounts* B) *Share an account with a friend* C) *Register a single, private account* [a/b/c] > c Nyx nods, "Good choice, Ei. Let's proceed with a single, private account. It can be tempting to try and do something 'clever', but registering multiple accounts or sharing an account with a friend can lead to disqualification." --- (Press Enter to continue...) --- "Ok," Nyx says, "Registering you for the competition... There's an introductory audio message, piping to your speakers." --- (Press Enter to continue...) --- "Welcome hacker! You're about to embark on a journey that will teach you many esoteric and valuable skills. Our mission is to guide you in the right path, that you may use these skills to protect and defend and never for selfish gain or deceit. We hope you enjoy the challenges that our authors have devised this year. Always remember: 'With great power, comes great responsibility!'" --- (Press Enter to continue...) --- Nyx continues, "I've gleaned from the Ether that in CTF competitions, it's always good to start with the 'sanity' challenge. It should be the challenge worth the least amount of points. I'll pull it up. You're looking for something called the flag. You should know it when you see it." --- (Press Enter to continue...) --- "Oh interesting," Eibhilin says, "It seems like the sanity challenge is an old school interactive fiction game." --- (Press Enter to continue...) --- Options: A) *Play the game* B) *Search the Ether for the flag* [a/b] > b Nyx says, "Oh, I've read about this Ei. You don't want to submit a different players flag! That's against the rules! Even sharing artifact downloads could lead to you finding someone else's flag." --- (Press Enter to continue...) --- "Got it." --- (Press Enter to continue...) --- Options: A) *Play the game* B) *Search the Ether for the flag* [a/b] > 1 Invalid choice. Try again! Options: A) *Play the game* B) *Search the Ether for the flag* [a/b] > a "Good choice, Ei," Nyx says, "You never want to share flags or artifact downloads." --- (Press Enter to continue...) --- Playing the Game Playing the Game: 10%|███▊ [time left: 00Playing the Game: 20%|███████▌ [time leftPlaying the Game: 30%|███████████▍ [time Playing the Game: 40%|███████████████▏ [tPlaying the Game: 50%|███████████████████ Playing the Game: 60%|██████████████████████▊ Playing the Game: 70%|██████████████████████████▌ Playing the Game: 80%|████████████████████████████ Playing the Game: 90%|████████████████████████████ Playing the Game: 100%|████████████████████████████ Playing the Game: 100%|████████████████████████████ ██████████ [time left: 00:00] Playing the Game completed successfully! --- (Press Enter to continue...) --- "That was fun!" Eibhilin exclaims, "I found the flag!" --- (Press Enter to continue...) --- Nyx says, "Great job, Ei! I've read that a lot of players create writeups of interesting challenges they solve during the competition. Just be sure to wait to publish them until after the winners have been announced. We can work on that together if you'd like." --- (Press Enter to continue...) --- "Thanks, Nyx! Here's the flag I found: picoCTF{m1113n1um_3d1710n_fc649bc5}" --- (Press Enter to continue...) --- "Great, you just got 10 points!" Nyx exclaims. --- (Press Enter to continue...) --- Eibhilin smiles, "I'm off to a good start!" --- (Press Enter to continue...) --- Nyx says, "Let's keep going!" --- (Press Enter to continue...) --- END OF FANTASY CTF SIMULATION Thank you for playing! To reemphasize some rules for this year: 1. Register only one account. 2. Do not share accounts, flags or artifact downloads. 3. Wait to publish writeups publicly until after the organizers announce the winners. 4. picoCTF{m1113n1um_3d1710n_fc649bc5} is a real flag! Submit it for some points in picoCTF 2025! --- (Press Enter to continue...) ---
Rust fixme 1(100 points)
圧縮ファイル(fixme1.tar.gz)がダウンロードできます。Rust に関する問題のようです。これから Rust の問題が 3問続くようです。Rust はやったことありませんが、やってみます。
構文エラーを修正して、フラグを出力する問題のようです。

ダウンロードしたファイルを解凍します。
$ tar zxvf fixme1.tar.gz fixme1/ fixme1/Cargo.toml fixme1/Cargo.lock fixme1/src/ fixme1/src/main.rs
ソースコードは以下です。
use xor_cryptor::XORCryptor; fn main() { // Key for decryption let key = String::from("CSUCKS") // How do we end statements in Rust? // Encrypted flag values let hex_values = ["41", "30", "20", "63", "4a", "45", "54", "76", "01", "1c", "7e", "59", "63", "e1", "61", "25", "7f", "5a", "60", "50", "11", "38", "1f", "3a", "60", "e9", "62", "20", "0c", "e6", "50", "d3", "35"]; // Convert the hexadecimal strings to bytes and collect them into a vector let encrypted_buffer: Vec<u8> = hex_values.iter() .map(|&hex| u8::from_str_radix(hex, 16).unwrap()) .collect(); // Create decrpytion object let res = XORCryptor::new(&key); if res.is_err() { ret; // How do we return in rust? } let xrc = res.unwrap(); // Decrypt flag and print it out let decrypted_buffer = xrc.decrypt_vec(encrypted_buffer); println!( ":?", // How do we print out a variable in the println function? String::from_utf8_lossy(&decrypted_buffer) ); }
以下の記事で Rust の環境構築を行い、ビルド、実行は確認できました。
今回のお題についても、ビルド、実行していきます。
3か所の修正が必要でした。1か所目は、5行目の分について、セミコロンが無かったので、追加しました。2か所目は、18行目で、ret; を return; に修正しました。3か所目は、25行目で、println で変数の出力として、":?" を "{}" に修正しました。
$ cd fixme1/ $ cargo build Updating crates.io index Downloaded crossbeam-deque v0.8.5 Downloaded either v1.13.0 Downloaded crossbeam-utils v0.8.20 Downloaded xor_cryptor v1.2.3 Downloaded crossbeam-epoch v0.9.18 Downloaded rayon v1.10.0 Downloaded rayon-core v1.12.1 Downloaded 7 crates (388.2 KB) in 0.45s Compiling crossbeam-utils v0.8.20 Compiling rayon-core v1.12.1 Compiling either v1.13.0 Compiling crossbeam-epoch v0.9.18 Compiling crossbeam-deque v0.8.5 Compiling rayon v1.10.0 Compiling xor_cryptor v1.2.3 Compiling rust_proj v0.1.0 (/home/user/svn/experiment/picoCTF/picoCTF2025_GeneralSkills/fixme1) error: expected `;`, found keyword `let` --> src/main.rs:5:37 | 5 | let key = String::from("CSUCKS") // How do we end statements in Rust? | ^ help: add `;` here ... 8 | let hex_values = ["41", "30", "20", "63", "4a", "45", "54", "76", "01... | --- unexpected token error: argument never used --> src/main.rs:26:9 | 25 | ":?", // How do we print out a variable in the println function? | ---- formatting specifier missing 26 | String::from_utf8_lossy(&decrypted_buffer) | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ argument never used error[E0425]: cannot find value `ret` in this scope --> src/main.rs:18:9 | 18 | ret; // How do we return in rust? | ^^^ help: a local variable with a similar name exists: `res` For more information about this error, try `rustc --explain E0425`. error: could not compile `rust_proj` (bin "rust_proj") due to 3 previous errors
ソースコードを修正したので、ビルド、実行します。ビルドと実行を一気にやってくれる cargo run を使います。無事にフラグが表示されました。
$ cargo run ] Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.06s Running `target/debug/rust_proj` picoCTF{4r3_y0u_4_ru$t4c30n_n0w?}
Rust fixme 2(100 points)
圧縮ファイル(fixme2.tar.gz)がダウンロードできます。

解凍します。先ほどと同じ感じです。
$ tar zxvf fixme2.tar.gz fixme2/ fixme2/Cargo.toml fixme2/Cargo.lock fixme2/src/ fixme2/src/main.rs
ソースコードは以下です。
use xor_cryptor::XORCryptor; fn decrypt(encrypted_buffer:Vec<u8>, borrowed_string: &String){ // How do we pass values to a function that we want to change? // Key for decryption let key = String::from("CSUCKS"); // Editing our borrowed value borrowed_string.push_str("PARTY FOUL! Here is your flag: "); // Create decrpytion object let res = XORCryptor::new(&key); if res.is_err() { return; // How do we return in rust? } let xrc = res.unwrap(); // Decrypt flag and print it out let decrypted_buffer = xrc.decrypt_vec(encrypted_buffer); borrowed_string.push_str(&String::from_utf8_lossy(&decrypted_buffer)); println!("{}", borrowed_string); } fn main() { // Encrypted flag values let hex_values = ["41", "30", "20", "63", "4a", "45", "54", "76", "01", "1c", "7e", "59", "63", "e1", "61", "25", "0d", "c4", "60", "f2", "12", "a0", "18", "03", "51", "03", "36", "05", "0e", "f9", "42", "5b"]; // Convert the hexadecimal strings to bytes and collect them into a vector let encrypted_buffer: Vec<u8> = hex_values.iter() .map(|&hex| u8::from_str_radix(hex, 16).unwrap()) .collect(); let party_foul = String::from("Using memory unsafe languages is a: "); // Is this variable changeable? decrypt(encrypted_buffer, &party_foul); // Is this the correct way to pass a value to a function so that it can be changed? }
早速、ビルド、実行していきます。
エラーが出ます。オブジェクト(文字列の borrowed_string)が Immutable(不変)なのに、変更しようとしているところで、エラーになっているようです。
まず、decrypt関数の引数の borrowed_string を Mutable(可変)に修正します。具体的には、&String を &mut String に修正します。次に、main関数の decrypt関数を呼び出しているところを、同様に、&party_foul を &mut party_foul に修正します。最後に、party_foul の宣言を、let party_foul から let mut party_foul に修正します。
$ cargo run Compiling crossbeam-utils v0.8.20 Compiling rayon-core v1.12.1 Compiling either v1.13.0 Compiling crossbeam-epoch v0.9.18 Compiling crossbeam-deque v0.8.5 Compiling rayon v1.10.0 Compiling xor_cryptor v1.2.3 Compiling rust_proj v0.1.0 (/home/user/svn/experiment/picoCTF/picoCTF2025_GeneralSkills/fixme2) error[E0596]: cannot borrow `*borrowed_string` as mutable, as it is behind a `&` reference --> src/main.rs:9:5 | 9 | borrowed_string.push_str("PARTY FOUL! Here is your flag: "); | ^^^^^^^^^^^^^^^ `borrowed_string` is a `&` reference, so the data it refers to cannot be borrowed as mutable | help: consider changing this to be a mutable reference | 3 | fn decrypt(encrypted_buffer:Vec<u8>, borrowed_string: &mut String){ // How do we pass values to a function that we want to change? | +++ error[E0596]: cannot borrow `*borrowed_string` as mutable, as it is behind a `&` reference --> src/main.rs:20:5 | 20 | borrowed_string.push_str(&String::from_utf8_lossy(&decrypted_buffer)); | ^^^^^^^^^^^^^^^ `borrowed_string` is a `&` reference, so the data it refers to cannot be borrowed as mutable | help: consider changing this to be a mutable reference | 3 | fn decrypt(encrypted_buffer:Vec<u8>, borrowed_string: &mut String){ // How do we pass values to a function that we want to change? | +++ For more information about this error, try `rustc --explain E0596`. error: could not compile `rust_proj` (bin "rust_proj") due to 2 previous errors
では、ビルド、実行します。無事にフラグが表示されました。
$ cargo run Compiling rust_proj v0.1.0 (/home/user/svn/experiment/picoCTF/picoCTF2025_GeneralSkills/fixme2) Finished `dev` profile [unoptimized + debuginfo] target(s) in 1.34s Running `target/debug/rust_proj` Using memory unsafe languages is a: PARTY FOUL! Here is your flag: picoCTF{4r3_y0u_h4v1n5_fun_y31?}
Rust fixme 3(100 points)
圧縮ファイル(fixme3.tar.gz)がダウンロードできます。

解凍します。先ほどと同じ感じです。
$ tar zxvf fixme3.tar.gz fixme3/ fixme3/Cargo.toml fixme3/Cargo.lock fixme3/src/ fixme3/src/main.rs
ソースコードは以下です。
use xor_cryptor::XORCryptor; fn decrypt(encrypted_buffer: Vec<u8>, borrowed_string: &mut String) { // Key for decryption let key = String::from("CSUCKS"); // Editing our borrowed value borrowed_string.push_str("PARTY FOUL! Here is your flag: "); // Create decryption object let res = XORCryptor::new(&key); if res.is_err() { return; } let xrc = res.unwrap(); // Did you know you have to do "unsafe operations in Rust? // https://doc.rust-lang.org/book/ch19-01-unsafe-rust.html // Even though we have these memory safe languages, sometimes we need to do things outside of the rules // This is where unsafe rust comes in, something that is important to know about in order to keep things in perspective // unsafe { // Decrypt the flag operations let decrypted_buffer = xrc.decrypt_vec(encrypted_buffer); // Creating a pointer let decrypted_ptr = decrypted_buffer.as_ptr(); let decrypted_len = decrypted_buffer.len(); // Unsafe operation: calling an unsafe function that dereferences a raw pointer let decrypted_slice = std::slice::from_raw_parts(decrypted_ptr, decrypted_len); borrowed_string.push_str(&String::from_utf8_lossy(decrypted_slice)); // } println!("{}", borrowed_string); } fn main() { // Encrypted flag values let hex_values = ["41", "30", "20", "63", "4a", "45", "54", "76", "12", "90", "7e", "53", "63", "e1", "01", "35", "7e", "59", "60", "f6", "03", "86", "7f", "56", "41", "29", "30", "6f", "08", "c3", "61", "f9", "35"]; // Convert the hexadecimal strings to bytes and collect them into a vector let encrypted_buffer: Vec<u8> = hex_values.iter() .map(|&hex| u8::from_str_radix(hex, 16).unwrap()) .collect(); let mut party_foul = String::from("Using memory unsafe languages is a: "); decrypt(encrypted_buffer, &mut party_foul); }
早速、ビルド、実行していきます。エラーが出ます。
コメントで書かれてる内容を見ると、Rust のルールに外れる実装もするときがある、みたいなことが書かれています。スライス操作が危険な操作なんでしょうか。Rust には unsafe という構文があるようです。今回は、// unsafe { とコメントアウトされている箇所を、unsafe { のように、有効にするのと、対応している括弧の // } を } としてやれば良さそうです。
$ cargo run Compiling crossbeam-utils v0.8.20 Compiling rayon-core v1.12.1 Compiling either v1.13.0 Compiling crossbeam-epoch v0.9.18 Compiling crossbeam-deque v0.8.5 Compiling rayon v1.10.0 Compiling xor_cryptor v1.2.3 Compiling rust_proj v0.1.0 (/home/user/svn/experiment/picoCTF/picoCTF2025_GeneralSkills/fixme3) error[E0133]: call to unsafe function `std::slice::from_raw_parts` is unsafe and requires unsafe function or block --> src/main.rs:31:31 | 31 | ...ce = std::slice::from_raw_parts(decrypted_ptr, decrypted_len); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ call to unsafe function | = note: consult the function's documentation for information on how to avoid undefined behavior For more information about this error, try `rustc --explain E0133`. error: could not compile `rust_proj` (bin "rust_proj") due to 1 previous error
では、ビルド、実行してみます。無事にフラグが表示されました。
$ cargo run Compiling rust_proj v0.1.0 (/home/user/svn/experiment/picoCTF/picoCTF2025_GeneralSkills/fixme3) Finished `dev` profile [unoptimized + debuginfo] target(s) in 1.45s Running `target/debug/rust_proj` Using memory unsafe languages is a: PARTY FOUL! Here is your flag: picoCTF{n0w_y0uv3_f1x3d_1h3m_411}
Rust の問題が終わりました。思ったより簡単で助かりました。
YaraRules0x100(200 points)
1つの zipファイル(suspicious.zip)がダウンロードできます。今回は、YARAルールをいくつか作ってほしい、というお題のようです。YARAルールとは、悪意のあるソフトウェア、スクリプト、シグネチャの存在を示すパターンとのことです。

解凍します。Windows の実行ファイルです。
$ unzip suspicious.zip Archive: suspicious.zip [suspicious.zip] suspicious.exe password: password incorrect--reenter: inflating: suspicious.exe $ file suspicious.exe suspicious.exe: PE32 executable (GUI) Intel 80386, for MS Windows, UPX compressed, 3 sections
とりあえず、実行しようとすると、管理者権限が必要と言われます。やるしかないので、管理者権限で実行してみます。以下のようなアプリケーションが起動しました。

和訳します。
これは偽のマルウェアです。害はありません。 ヒント: - 効果的な Yara ルールを開発するには、このプログラムで使用されている疑わしい Win32 API 関数を見つけてください。 - 文字列のみに基づいてルールを開発することは (URL、ライブラリ関数呼び出しなどを除く) 良い考えではありません。誤検知につながる可能性があります。 - このバイナリがパック (またはアンパック) されていても、ルールは機能するはずです。 幸運を祈ります
続きをやりたいところですが、Windowsプログラムは全く分かりません。後回しにします。
おわりに
現在、picoCTF 2025 にリアルタイムで参戦しています。今回は、General Skills の全5問のうち、4問を解きました。Windowsプログラムも勉強しないとダメですね。
次回は、Reverse Engineering をやっていきます。
最後になりましたが、エンジニアグループのランキングに参加中です。
気楽にポチッとよろしくお願いいたします🙇
今回は以上です!
最後までお読みいただき、ありがとうございました。