Pragyan CTF 2022 Writeup

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

Welcome (Sanity Check)

Discordに入り、#rulesチャネルを見ると、ルールの文中にwelcome challengeのフラグが書いてあった。

p_ctf{pl34s3_f0ll0w_th3_rul3s}

PHP train (Web)

PHPの==と===の違いなどを使って、条件を満たすようにする。
FLAG1については、param1を配列として指定すれば、CONSTANT1の値に関係なく、条件を満たす。
FLAG2については、param2とparam3で異なる文字列で、sha1を比較しているが、これも配列指定をすればよい。
FLAG3については、param4で1.2e3と同じ数値を指定すればよい。
FLAG4については、param5のパラメータにスペースを含めることによって条件を満たすようにする。
FLAG5については、param6にmd4が0eから始まる文字列を指定する。その際https://github.com/spaze/hashes/blob/master/md4.mdを参考にする。
FLAG6については、helloworldを置換後に、helloworldになるよう、param7に間に挟み込みよう文字列を指定する。
FLAG7については、param8に1~25の数値を含むように、.envも含めて文字列を指定する。
以下のようにパラメータを指定してアクセスしたら、フラグが表示された。

https://phptrain.challs.pragyanctf.tech/?param1[]=a&param2[]=b&param3[]=c&param4=1200&param5=%2089&param6=gH0nAdHk&param7=hellohelloworldworld&param8=1.env
p_ctf{ech0_1f_7h3_7r41n_d035_n07_5t0p_1n_y0ur_5t4t10n_7h3n_1t5_n07_y0ur_7r41n}

Hidden and Protected (Forensics)

pngの末尾にzipがくっついている。zipを抽出して、解凍すると、2つのファイルが展開される。

・Double_Encrypted_Password.txt
・image2.jpeg

txtにはこう書いてある。

ABBBAABABBBAABABAABABABABAAAAAABBBAABABBBBAAAABABB

ベーコン暗号と推測し、https://www.dcode.fr/bacon-cipherで復号する。

OLSSVAOLYL

さらにシーザー暗号と推測し、https://www.geocachingtoolbox.com/index.php?lang=en&page=caesarCipherで復号する。

Rotation 7:
HELLOTHERE

これをパスフレーズとしてsteghideを実行する。

$ steghide extract -sf image2.jpeg -p HELLOTHERE
wrote extracted data to "flag.txt".
$ cat flag.txt
p_ctf{G3N3R4L_K3N0B1_TH3_N3G0T14T0R}
p_ctf{G3N3R4L_K3N0B1_TH3_N3G0T14T0R}

Ye Olde Threat (Forensics)

base64文字列をデコードする。

$ echo aHR0cHM6Ly9kcml2ZS5nb29nbGUuY29tL2ZpbGUvZC8xQUVremJqV1JDZFVwNEY3ZjJ3dExnSWQyd2RTRlkwUXE= | base64 -d
https://drive.google.com/file/d/1AEkzbjWRCdUp4F7f2wtLgId2wdSFY0Qq

このURLにアクセスし、ファイルをダウンロードする。pngのようだが、ところどころ壊れているので、修正していく。

・オフセット0x00000000-0x00000005(シグネチャ部分)
 89 49 6c 47 63 6d -> 89 50 4e 47 0d 0a
・オフセット0x0000000c-0x0000000f(IHDRチャンク名)
 31 48 44 72 -> 49 48 44 52
・オフセット0x00000025-0x00000028(IDATチャンク名)
 31 64 61 37 -> 49 44 41 54
・オフセット0x0000202d-0x00002030(IDATチャンクサイズ)
 08 01 09 02 -> 00 00 20 00
・オフセット0x00006041-0x00006044(IDATチャンクCRC)
 f6 48 39 a4 -> a8 9b 38 1a

修正したPNGファイルにはフラグが見つからない。このPNGファイルをStegSolveで開き、Blue plane 2を見ると、フラグが書いてあった。
f:id:satou-y:20220316074848p:plain

p_ctf{K@nye_w@nt5_To_Buy_Th3_3n71r3_E4rth}

Perfect Puzzle (Crypto)

暗号処理は以下の通り。

・p, q: 1024ビット素数
・n = p * q
・e = 65537
・flag_int: flagの数値化
・CipherText = pow(flag_int, e, n)
・Xemu: 1024ビット素数
・Alice = p + Xemu ** 2
・Bob = q * Xemu
・sum = Xemu * (Xemu + 1) >> 1
・Result = invdivsum(sum)
 ・ret = 0
 ・sumの回数繰り返し
  ・sum が i + 1で割り切れる場合、retが1/(i+1)増加
・CipherText, Alice, Bob, Result表示

Resultが2.0になっているので、sumは完全数。見つかっている完全数は限られている。https://en.wikipedia.org/wiki/List_of_Mersenne_primes_and_perfect_numbersを参考にする。
コードから2048ビット未満と推測されるので、それ以下の完全数の総当たりで、条件を満たすものを探す。p, qを割り出せたら、あとはそのまま復号する。

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

CipherText = 149596971555589076155364186420580570749374401138608961998290009825114263148416772478651277321234724062278466223025831775035224109103887370927097231230324908201908263749968832767737017862796581877535194538767636598004538397685647433625803605580636107271455294998120570215920206140799513104969815764397323782297061528436771406170006449117531288516762624306954764709977440515034071280736026924741057203047657693649914954727759355179977253548864147305088480009948437
Alice = 282107567413424138126415916172126379762973487029431335677677351999909735485304760228208386658075380503123901137419658654328175448732740174233462536318627304974901304879611754592414594362308597516549359004265409028150117237817823179344527892839743549388806946124113890322466683297081312475367783738162570544520549179569150033008482592884468354854459703961697842606812
Bob = 66444211849564598649204285138638077348678558382508976397654912859551087895603398701542655997585482323573895063934558694018052442406995794312376093987279068684491709029458439225825559371474422839752950009253788191772668126664288610488535211383255806288769703027437108720296265682565379886651381942112538908474625781728851722312089113677698489150478927871052981645551018211694817730263647445568601019977068831413100897642519594589716447525199022801203505747758062305119871983246399979915166157

ps = [2, 3, 5, 7, 13, 17, 19, 31, 61, 89, 107, 127, 521, 607, 1279, 2203, 2281,
    3217, 4253, 4423, 9689, 9941, 11213, 19937, 21701, 23209, 44497, 86243]

found = False
for p in ps:
    pn = pow(2, p - 1) * (pow(2, p) - 1)
    sum = pn * 2
    x = symbols('x')
    eq = Eq(x * (x + 1) - sum, 0)
    sol = solve(eq, x)
    for Xemu in sol:
        if Xemu > 0 and Bob % Xemu == 0:
            found = True
            break
    if found:
        break

q = int(Bob // Xemu)
p = int(Alice - Xemu ** 2)
n = p * q
e = 65537
phi = (p - 1) * (q - 1)
d = inverse(e, phi)
m = pow(CipherText, d, n)
flag = long_to_bytes(m).decode()
print(flag)
p_ctf{M3rs5nne_pr1mes_4re_c00l}

Blind Scout (Crypto)

5つの鍵のModulusのうち、共通する素数が使われているものを探し、素因数分解する。2つの鍵で復号し、フラグの形式になるものを探す。

from Crypto.PublicKey import RSA
from Crypto.Util.number import *
import itertools
import base64

ns = []
for i in range(1, 6):
    fname = 'pub%d.pem' % i
    with open(fname, 'r') as f:
        pem = f.read()

    pubkey = RSA.importKey(pem)
    n = pubkey.n
    e = pubkey.e
    assert e == 65537
    ns.append(n)

for c in list(itertools.combinations(ns, 2)):
    p = GCD(c[0], c[1])
    if p > 1:
        n1 = c[0]
        n2 = c[1]
        q1 = c[0] // p
        q2 = c[1] // p
        break

with open('cipher.txt', 'r') as f:
    c = bytes_to_long(base64.b64decode(f.read()))

phi1 = (p - 1) * (q1 - 1)
phi2 = (p - 1) * (q2 - 1)
d1 = inverse(e, phi1)
d2 = inverse(e, phi2)
m1 = pow(c, d1, n1)
m2 = pow(c, d2, n2)

for m in [m1, m2]:
    flag = long_to_bytes(m)
    if flag.startswith(b'p_ctf{'):
        print(flag.decode())

復号結果は以下の通り。

p_ctf{010100_100000101100_100110100000111010100010100110100010111001010100111000}

このままではフラグは通らない。問題タイトルから点字と推測する。

01   10 11   11 10 10 10 11 10 10 01 10
10   00 00   01 00 11 01 01 01 10 10 10
00 _ 00 10 _ 00 00 10 00 00 00 11 00 10

i  _ a  m  _ d  a  r  e  d  e  v  i  l

p_ctf{i_am_daredevil} は通らなかった。大文字にしてみる。

p_ctf{I_AM_DAREDEVIL}