CTFd を動かしてみる

インストール

λ git clone https://github.com/CTFd/CTFd.git
λ cd CTFd/
λ python3 -c "import os; f=open('.ctfd_secret_key', 'a+b'); f.write(os.urandom(64)); f.close()"
λ sudo docker-compose up

これで http://localhost:8000 にアクセス!

結構色々できてすごい。CTF の問題は Docker とかで動かせば良いっぽいね。

参考リソース

Runes (ångstromCTF 2019)

振り返り

問題文の Paillier が問題を解くためのヒントだということに気づけなかった。

Paillier 暗号というのも初めてなので、ちゃんと勉強しよう。

Paillier 暗号

ペイエ暗号というらしい。

RSAに似てるけど加法準同型性を満たす珍しい暗号みたいで、存在を知れてよかった。

今回の問題の解き方

runes.txt には以下の内容だけが含まれています。

n: 99157116611790833573985267443453374677300242114595736901854871276546481648883
g: 99157116611790833573985267443453374677300242114595736901854871276546481648884
c: 2433283484328067719826123652791700922735828879195114568755579061061723786565164234075183183699826399799223318790711772573290060335232568738641793425546869

まずは msievepq の値を求めます。

λ msieve -q -v -e 99157116611790833573985267443453374677300242114595736901854871276546481648883
...
factoring 99157116611790833573985267443453374677300242114595736901854871276546481648883 (77 digits)
...
p39 factor: 310013024566643256138761337388255591613
p39 factor: 319848228152346890121384041219876391791
elapsed time 00:02:03

77桁なので2分程度で求まりました。

あとはこんな感じのプログラムで復号します。

import Crypto.Number.ModArithmetic
import Crypto.Number.Serialize

import Data.Text
import Data.Text.Encoding

p, q, n, g, c :: Integer
p = 310013024566643256138761337388255591613
q = 319848228152346890121384041219876391791
n = 99157116611790833573985267443453374677300242114595736901854871276546481648883
g = 99157116611790833573985267443453374677300242114595736901854871276546481648884
c = 2433283484328067719826123652791700922735828879195114568755579061061723786565164234075183183699826399799223318790711772573290060335232568738641793425546869

l :: Integer -> Integer
l x = (x - 1) `div` n

solve :: Integer -> Integer
solve c = (l (expSafe c lambda (n^2)) * mu) `mod` n
where
lambda = lcm (p-1) (q-1)
mu = inverseCoprimes (l (expSafe g lambda (n^2))) n

toDisplay :: Integer -> Text
toDisplay = decodeUtf8 . i2osp

実行結果

*Main> solve c
8483734412270322850839331621532480687141757

*Main> toDisplay $ solve c
"actf{crypto_lives}"

フラグ

"actf{crypto_lives}"

参考リソース

ångstromCTF 2019 の結果

結果

ジャンル 解いた数 / 問題数
MISC 8 / 13
REV 4 / 6
WEB 2 / 9
CRYPTO 3 / 12
BINARY 1 / 8

最終的に 720 点の 205 位でした。

振り返り

開催期間が1週間ほどの大会だったので、問題数が結構ありました。

問題もクイズ形式で簡単なものから難しいものまで幅広く、非常に勉強になりました。

サーバーがめっちゃ重たかったり、シェルにログインできなかったりということもありましたが、初心者の僕でもとても楽しむことができました。

WEBCRYPTO をもっと解けるようにしないといけないかなと思います。

また来年も出てみようと思います。

日本語で読める writeup まとめ

Control You (ångstromCTF 2019)

解説

リンク先に飛ぶと目が痛いページが・・・。

ソースコードにフラグが書いてあった。

λ curl https://controlyou.2019.chall.actf.co/ | grep actf
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 991 100 991 0 0 1256 0 --:--:-- --:--:-- --:--:-- 1257
if (flag.value === "actf{control_u_so_we_can't_control_you}") {

フラグ

actf{control_u_so_we_can't_control_you}

The Mueller Report (ångstromCTF 2019)

解説

問題のリンク先から full-mueller-report.pdf (138.8 MB) をダウンロードします。(結構時間かかる)

あとは strings コマンドを叩くだけでフラグが出てきます。

λ strings full-mueller-report.pdf | grep actf
actf{no0o0o0_col1l1l1luuuusiioooon}

フラグ

actf{no0o0o0_col1l1l1luuuusiioooon}

Paper Trail (ångstromCTF 2019)

解説

paper_trail.pcapng ということでネットワークのパケット問題。

Wireshark の TCP Stream で終わり

せっかくなので tshark を使ってワンライナーで抜き出す例。

λ tshark -r paper_trail.pcapng -z follow,tcp,ascii,0 | grep "^PRIVMSG" | sed 1,2d | awk -F: '{printf "%c",$2}'
actf{fake_math_papers}

フラグ

actf{fake_math_papers}

参考リソース

Scratch It Out (ångstromCTF 2019)

解説

project.json をダウンロードして中身を見てみる。

json isStage のようなキーワードで検索するとプログラミング言語の Scratch の情報が出てくるので、たぶんこれ。

project.jsonproject.sb2 のように名前を変更する。

λ mv project.json project.sb2

このファイルをオンラインエディタで開くとこんな感じ。

動かしてみると猫がフラグを教えてくれる。

フラグ

actf{Th5_0pT1maL_LANgUaG3}

参考リソース

Just Letters (ångstromCTF 2019)

解説

結構面白かった。

とりあえず接続して適当なコマンドを打ってみる。

λ nc misc.2019.chall.actf.co 19600
Welcome to the AlphaBeta interpreter! The flag is at the start of memory. You get one line:
> a

λ nc misc.2019.chall.actf.co 19600
Welcome to the AlphaBeta interpreter! The flag is at the start of memory. You get one line:
> ls

全然わからない。何も表示されない。問題文のリンク先に飛ぶと AlphaBeta っていう brainfuck's 系の言語の説明があった。

Example の Hello World! を実際に試す。

λ nc misc.2019.chall.actf.co 19600
Welcome to the AlphaBeta interpreter! The flag is at the start of memory. You get one line:
> cccCISccccCIScccCIYxSGSHaaCLgDLihhhDLDLgggDLTTGaaCLSGccbbbCLDLgggDLjggggDLSHDLTTGaaaCL
Hello World!

なんかでた。理解を深めるため、命令を短くする。wikipedia の内容によるうと

L outputs a character to the screen

ということなので、最初に出現する L 以降を削除してみる。

λ nc misc.2019.chall.actf.co 19600
Welcome to the AlphaBeta interpreter! The flag is at the start of memory. You get one line:
> cccCISccccCIScccCIYxSGSHaaCL
H

予想通り、1文字だけ出力された。また、文字より数値を出力させた方がデバッグしやすいので

M outputs a number to the screen

の通り M を使う。また、レジスタ1,2は a, g でインクリメンタルできるらしい。

a adds 1 to register 1
g adds 1 to register 2

λ nc misc.2019.chall.actf.co 19600
Welcome to the AlphaBeta interpreter! The flag is at the start of memory. You get one line:
> aM
0

λ nc misc.2019.chall.actf.co 19600
Welcome to the AlphaBeta interpreter! The flag is at the start of memory. You get one line:
> gM
0⏎

全然変わらない。ということはレジスタ3が怪しい。

C sets register 3 to the value of register 1
D sets register 3 to the value of register 2

レジスタ3に値をセットするためには CD を使えば良さそう。

λ nc misc.2019.chall.actf.co 19600
Welcome to the AlphaBeta interpreter! The flag is at the start of memory. You get one line:
> aCM
1

ビンゴ。つまり、レジスタ1,2でメモリの内容を読み取り、その値をレジスタ3にコピーしたあとで出力すれば良さそうだとわかった。

あとは wikipedia の内容をもう少し読めば

G sets register 1 to the memory at the memory pointer
S adds 1 to the register

が使えそうだとわかる。またフラグはメモリの先頭から始まっているらしいので

λ nc misc.2019.chall.actf.co 19600
Welcome to the AlphaBeta interpreter! The flag is at the start of memory. You get one line:
> GCM
97

λ nc misc.2019.chall.actf.co 19600
Welcome to the AlphaBeta interpreter! The flag is at the start of memory. You get one line:
> GCL
a

λ nc misc.2019.chall.actf.co 19600
Welcome to the AlphaBeta interpreter! The flag is at the start of memory. You get one line:
> GCLSGCL
ac

解けそう。最後に適当に繰り返して終わり。

λ nc misc.2019.chall.actf.co 19600
Welcome to the AlphaBeta interpreter! The flag is at the start of memory. You get one line:
> GCLSGCLSGCLSGCLSGCLSGCLSGCLSGCLSGCLSGCLSGCLSGCLSGCLSGCLSGCLSGCLSGCLSGCLSGCLSGCLSGCLSGCLSGCLSGCLSGCLSGCLSGCLSGCL
actf{esolangs_sure_are_fun!}

フラグ

actf{esolangs_sure_are_fun!}

参考リソース

Classy Cipher (ångstromCTF 2019)

解説

classy_cipher.py の内容は以下の通り。

from secret import flag, shift

def encrypt(d, s):
e = ''
for c in d:
e += chr((ord(c)+s) % 0xff)
return e

assert encrypt(flag, shift) == ':<M?TLH8<A:KFBG@V'

フラグを shift 分ずらしてるだけ。フラグの形式は actf{xxxxxx} となっているはずなので a: が対応するような shift を探す。

Haskell で書くとこんな感じ。

import Data.Char

solve :: Char -> Char -> Int
solve plain encrypted = head [ n | n <- [0..0xff], shift n plain == encrypted]

shift :: Int -> Char -> Char
shift k c = chr ((ord c + k) `mod` 0xff)

試してみます。

*Main> solve 'a' ':'
216

シフト数がわかったので復号します。

*Main> map (shift (-216)) ":<M?TLH8<A:KFBG@V"
"actf{so_charming}"

フラグ

actf{so_charming}

Really Secure Algorithm (ångstromCTF 2019)

解説

really_secure_algorithm.txt の内容は以下の通り。

p = 8337989838551614633430029371803892077156162494012474856684174381868510024755832450406936717727195184311114937042673575494843631977970586746618123352329889
q = 7755060911995462151580541927524289685569492828780752345560845093073545403776129013139174889414744570087561926915046519199304042166351530778365529171009493
e = 65537
c = 7022848098469230958320047471938217952907600532361296142412318653611729265921488278588086423574875352145477376594391159805651080223698576708934993951618464460109422377329972737876060167903857613763294932326619266281725900497427458047861973153012506595691389361443123047595975834017549312356282859235890330349

RSA 暗号の p, q が与えられている問題なので、すぐに解ける。

過去に同じような問題をやったことがあったので、その時のコードを流用。

import Crypto.PubKey.RSA
import Crypto.Number.ModArithmetic
import Crypto.Number.Serialize

import Data.Text
import Data.Text.Encoding

solve :: Integer -> Integer -> Integer -> Integer -> Text
solve p q e c = toDisplay $ expSafe c d n
where
n = p * q
d = case generateWith (p,q) 0 e of
Nothing -> error "generateWith error"
Just (_pubKey, privateKey) -> private_d privateKey

toDisplay :: Integer -> Text
toDisplay = decodeUtf8 . i2osp

試してみます。

> p = 8337989838551614633430029371803892077156162494012474856684174381868510024755832450406936717727195184311114937042673575494843631977970586746618123352329889
> q = 7755060911995462151580541927524289685569492828780752345560845093073545403776129013139174889414744570087561926915046519199304042166351530778365529171009493
> e = 65537
> c = 7022848098469230958320047471938217952907600532361296142412318653611729265921488278588086423574875352145477376594391159805651080223698576708934993951618464460109422377329972737876060167903857613763294932326619266281725900497427458047861973153012506595691389361443123047595975834017549312356282859235890330349
> solve p q e c
"actf{really_securent_algorithm}"

フラグ

actf{really_securent_algorithm}