CSA Capture The Flag 2019 Writeup

この大会は2019/4/25 11:00(JST)~2019/5/2 11:00(JST)に開催されました。
今回もチームで参戦。結果は5076点で363チーム中3位でした。
自分で解けた問題をWriteupとして書いておきます。

Sanity check(0x00)

問題にフラグが書いてあった。

CSACTF{w3lc0m3_t0_csa_ctf_2019}

Zippy (Forensics)

zipのヘッダ4バイトが壊れているので、修正する。

(誤) 00 00 00 00
(正) 50 4b 03 04

展開すると、flag.txtにフラグが書いてあった。

CSACTF{z1ppy_z1p_z1p}

Down to basic (Crypto)

フラグの先頭がCSACTF{となることからXOR鍵を算出し、さらに推測して、復号する。

c = '\x13\x13eg#v\t\x05\x0f#HE\x04CC\x07\x0f0V\x14\x15\\\x17\t\x0f2AU\x02\x01\x00\x01#\x1fE{\x14\\\x13\x17#qG{\x04\x00\x1e\x11$q\x14J\n'

pre_flag = 'CSACTF{'

key = ''
for i in range(len(pre_flag)):
    code = ord(c[i]) ^ ord(pre_flag[i])
    key += chr(code)

print key

## guess key ##
key += 'd'
print key

flag = ''
for i in range(len(c)):
    code = ord(c[i]) ^ ord(key[i%len(key)])
    flag += chr(code)

print flag

これでXOR鍵はP@$$w0rdであることがわかり、復号できた。

CSACTF{a_class1c_pr0blem_requ1res_a_class1c_s0lut10n}

Flag server (Crypto)

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

・入力データはBase64Base64デコードして以下の文字列にする。
 rowdy123 + <入力> +  + 
・AES暗号化してBase64エンコードして表示

文字列の長さを変えて試すと、10文字にしたとき、80バイトになったので、フラグの長さは46バイトであることもわかり、以下のようなイメージになる。

0123456789abcdef
rowdy123XXXXXXXX
XXFFFFFFFFFFFFFF
FFFFFFFFFFFFFFFF
FFFFFFFFFFFFFFFF
PPPPPPPPPPPPPPPP
0123456789abcdef
rowdy123XXXXXXXX
XXXXXXXXXXXXXXXC
XXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXF
FFFFFFFFFFFFFFFF
FFFFFFFFFFFFFFFF
FFFFFFFFFFFFFPPP

上記のようなイメージで、1文字ずつフラグをはみ出させ、2ブロック目と5ブロック目が一致するものを探すことを繰り返し、フラグを割り出す。

import socket
from base64 import *

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

flag = ''
for i in range(46):
    for c in range(32, 127):
        print '*** flag: %s ***' % flag
        print c, chr(c)

        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        s.connect(('34.74.132.34', 1337))

        data = recvuntil(s, '\n').rstrip()
        #print data
        if i < 16:
            text = 'X' * 8 + 'X' * (15 - (i % 16)) + flag + chr(c) \
                + 'X' * (47 - len(flag))
            print text
        else:
            text = 'X' * 8 + flag[i-15:i] + chr(c) \
                + 'X' * (47 - len(flag))
            print text
        text = b64encode(text)
        #print text
        s.sendall(text + '\n')

        size = len('rowdy123') + 71 - i + 46
        size = size + 16 - size % 16

        data = recvuntil(s, '\n').rstrip()
        #print data

        b64 = ''
        for j in range(size * 4 / (3 * 76) + 1):
            data = recvuntil(s, '\n').rstrip()
            #print data
            b64 += data

        enc = b64decode(b64)
        block1 = enc[16*1:16*2]
        block4 = enc[16*4:16*5]
        if block1 == block4:
            flag += chr(c)
            break

print flag
CSACTF{ser10usly_1_d0nt_kn0w_what_t0_put_here}

Shakespeare (Crypto)

eの値が小さく、平文の上位bitの大半がわかっている。このことから、以下のようなスクリプトで復号することができる。

# solve.sage
n = 1006836100850250538339995467613886290568442651407025704748377068277336663840322807255631910003671238333129747821401677091458273063019582521876226866878065342813920204554381690287329117261946553416320027957955605766622398073392496765842697469834794925876050925562024075006141634323253929247109497487662672405697258396584137509321741864516634651999799054786449300271622909227600883634147669010834991795161523697131426220251208433781244773539764161842505052231971382919967824210675662840663441620651420266255868914927411521784353964212033587445690357323252291991393014956932340980364269027698921907294758481600334348209162447060613941156368610501127037691522939020923578832525761118629677247463329559736428451094014067976185200082383567782696893487896242261214414477694719976590065452677776148568775926581762818443660878148364426201543151986222827020715235149143660874147096198623408316970919847284217769846103787437353010406070576416824522568438694050123611781945190356349091784143598551629255439066014442622323403606886940586598393546395396681244923344136771626115764972416530481118005853215560607832082875977841855163209032040736208394910518736188520737990966314657947406636362327107362260153133199038686467983874712019134795287956681
e = 5
c = 86369436556821828495369673854162831950166653329442831115191625341047835876798749659300641169348489814397657938758507056245452483528302648820602222051650089244062216320589929210206763500165600876025118953025309329553936210852805389539140958630003420335312979086383557665369163943903938569533466433527007050293325014896256907213847510652960137337747717702046541551623527661916693185081633247755853469488660176161381955671005765896319088865538114135666529015146105463393845863266076934785270480138522718706320412272484373838264459071202162126712065303703239606240172920462289657145390815683884595752646574944533803755632028037260059034716445191212182713792979655078091574530502495357406706909373119165806528380721574497499485062395281364120812874772053936952075952742036541816610742884797177390325939200184871385346082698051189479559236698554746173964482593646082680562341702105913029374921266420784671717205191676584949585227373190247242357902713051455202286347791244123645109240863246131183709864782892090906846140511095936549733130467184197878019539182792149521284353941031177248258779486351865884845751206163429977363088579292444338819912920250044064118502755440223212479004336304705210529515747947096857605944524561963438120052349

beta = 1
epsilon = beta^2/7

nbits = n.nbits()
kbits = floor(nbits*(beta^2/e-epsilon))

with open('msg.txt', 'r') as f:
    m0 = int(f.read().rstrip().replace('X', '\x00').encode('hex'), 16)

def_string = ' Message ends.'
def_size = len(def_string)

PR.<x> = PolynomialRing(Zmod(n))
f = (m0 + x * (256**def_size))^e - c
f = f.monic()

x0 = f.small_roots(X=2^kbits, beta=1)[0]
m = m0 + x0 * (256**def_size)
msg = ('%x' % m).decode('hex')
print msg

実行結果は以下の通り。

Message begins. To be, or not to be: that is the question: Whether 'tis nobler in the mind to suffer. The slings and arrows of outrageous fortune. Or to take arms against a sea of troubles. And by opposing end them. To die: to sleep; - Hamlet contemplating suicide in his famous soliloquy. (Hamlet) - CSACTF{w1ll14m-sh4k3sp34r3} Message ends.
CSACTF{w1ll14m-sh4k3sp34r3}

A game of apples (Misc)

90個以上あるリンゴから、サーバプレーヤーと交互に取っていき、最後のリンゴを取れれば、フラグを得ることができる。
残り10個目を取ると負けるので、そうならないようスクリプトを組み実行する。

import socket
from base64 import *

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

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('34.74.132.34', 1338))

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

data = recvuntil(s, '\n').rstrip()
print data
if data.split(' ')[1] == 'I':
    data = recvuntil(s, '\n').rstrip()
    print data
    data = recvuntil(s, '\n').rstrip()
    print data

remain = 999
count = 9
last = False
while True:
    if remain > 10 and remain < 20:
        count = remain - 10
    elif remain < 10:
        count = remain
        last = True
    data = recvuntil(s, '?\n').rstrip()
    print data
    print count
    s.sendall(str(count) + '\n')
    if last:
        break
    data = recvuntil(s, '\n').rstrip()
    print data
    data = recvuntil(s, '\n').rstrip()
    print data
    data = recvuntil(s, '\n').rstrip()
    print data
    remain = int(data.split(' ')[3])

data = recvuntil(s, '\n').rstrip()
print data
CSACTF{0n3_4ppl3_tw0_4ppl3_thr33_4ppl3}

Linux 1 (Misc)

$ ssh user@35.231.176.102 -p1773
The authenticity of host '[35.231.176.102]:1773 ([35.231.176.102]:1773)' can't be established.
ECDSA key fingerprint is SHA256:jnrxrLz++wBMSIAGbBB4KOK2Qpr9LW8kQ5tNvFN5LKc.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added '[35.231.176.102]:1773' (ECDSA) to the list of known hosts.
user@35.231.176.102's password: 

_________   _________   _____  _______________________________
\_   ___ \ /   _____/  /  _  \ \_   ___ \__    ___/\_   _____/
/    \  \/ \_____  \  /  /_\  \/    \  \/ |    |    |    __)  
\     \____/        \/    |    \     \____|    |    |     \   
 \______  /_______  /\____|__  /\______  /|____|    \___  /   
        \/        \/         \/        \/               \/                                                                                                  
Welcome to CSACTF 2019!

If you find any problems, please report to admin.

-[ Rule ]-
 A few rules before you get started:
     + don't leave orphan processes running
     + don't leave exploit-files laying around
     + don't annoy other players
     + don't share passwords/solutions
     + last but not least, don't spoil the fun!

Have fun!
     - Blue
user@23ee3cd1ea3b:~$ ls -la
total 44
drwxr-xr-x 1 user user 4096 Apr 25 03:04 .
drwxr-xr-x 1 root root 4096 Apr 22 20:12 ..
-rw-r--r-- 1 user user  220 Aug 31  2015 .bash_logout
-rw-r--r-- 1 user user 3796 Apr 25 03:03 .bashrc
drwx------ 2 user user 4096 Apr 25 03:04 .cache
-rw-r--r-- 1 user user  655 May 16  2017 .profile
-rw-rw-r-- 1 user user    0 Apr 25 03:04 ahahaha
-rwxrwxr-x 1 root root  267 Apr 22 20:09 flag_reader.py
-rw-rw-r-- 1 root root   40 Apr 22 19:25 readme.txt
-rw-r--r-- 1 user user   19 Apr 25 03:03 temp.txt
user@23ee3cd1ea3b:~$ cat flag_reader.py 
#!/usr/bin/python
import time
import os

f = open('flag.txt', 'r')

# ==== Reading the flag
flag = f.read()
with open('temp.txt','w') as tmp:
  tmp.write('Reading the flag...')

#print flag
time.sleep(99999)

# ==== Done, Cleaning up
os.remove('temp.txt')

f.close()
user@abb60ff15525:~$ ps -ef
UID        PID  PPID  C STIME TTY          TIME CMD
root         1     0  0 11:44 ?        00:00:00 /bin/bash /root/init.sh
user        13     1  0 11:44 ?        00:00:00 python /home/user/flag_reader.py
root        16     1  0 11:44 ?        00:00:00 /usr/sbin/sshd -D
root        18    16  0 11:44 ?        00:00:00 sshd: user [priv]
user        22    18  0 11:44 ?        00:00:00 sshd: user@pts/0
user        23    22  0 11:44 pts/0    00:00:00 -bash
user        38    23  0 11:48 pts/0    00:00:00 ps -ef

python /home/user/flag_reader.pyが実行できて、処理中のようだ。実行中のプロセスの標準出力を見る。

user@abb60ff15525:~$ ls -l  /proc/13/fd/
total 0
lr-x------ 1 user user 64 Apr 25 11:49 0 -> /dev/null
l-wx------ 1 user user 64 Apr 25 11:49 1 -> pipe:[2690442]
l-wx------ 1 user user 64 Apr 25 11:49 2 -> pipe:[2690443]
lr-x------ 1 user user 64 Apr 25 11:49 3 -> /home/user/flag.txt (deleted)
user@abb60ff15525:~$ cat /proc/13/fd/3
CSACTF{f34r_cuts_d33p3r_th4n_sw0rds}
CSACTF{f34r_cuts_d33p3r_th4n_sw0rds}

stephanography (Misc)

ステガノグラフィーの問題だが、使用しているツールに関することが問題文に書かれている。https://github.com/stncal/appaで該当するツールを見つけた。

$ python3 appa.py -d secret_new.png
    :
Appa found an extremely large string in the image. To save your console, results are saved to file: secret_new.results
String length: 187142

secret_new.resultsはhexデータだが、jpgになりそう。hexデコードすると、jpg画像にフラグが書いてあった。
f:id:satou-y:20190506160817j:plain

CSACTF{y1p_y1p!}

We are CSA (Reconnaissance)

https://ctf.utsacyber.com/challenges#We%20are%20CSAのResponceを見る。

{"data": {"files": [], "description": "There's nothing here.\r\n", "tags": [], "minimum": 50, "id": 6, "type_data": {"templates": {"create": "/plugins/dynamic_challenges/assets/create.html", "update": "/plugins/dynamic_challenges/assets/update.html", "view": "/plugins/dynamic_challenges/assets/view.html"}, "scripts": {"create": "/plugins/dynamic_challenges/assets/create.js", "update": "/plugins/dynamic_challenges/assets/update.js", "view": "/plugins/dynamic_challenges/assets/view.js"}, "id": "dynamic", "name": "dynamic"}, "hints": [], "category": "Reconnaissance", "name": "We are CSA", "solves": 28, "decay": 50, "initial": 500, "value": 369, "state": "visible", "type": "dynamic", "max_attempts": 0}, "success": true}

Responceにはこんなコメントが入っている。

<!-- Oops, you found me! Part 1: Q1NBQ1RGe1VORDNSU1Q Check out: http://utsacyber.com/resources.html. We have tons of resources for beginners, especially about RE. -->

Base64デコードしてみる。

$ echo Q1NBQ1RGe1VORDNSU1Q | base64 -d
CSACTF{UND3RSTbase64: 無効な入力

まだ他の場所にもBase64文字列が隠れていそう。http://utsacyber.com/resources.htmlにアクセスしてHTMLソースを見る。
今度はこんなコメントが入っている。

<!-- Part 2: 0TkQxTkdfQ1lCM1JfUz One of our great members, Missing, has an awesome tutorial on Bug Hunting (and MIPS), you might want to check it out.-->

先ほどのBase64文字列と結合して、Base64デコードする。

$ echo Q1NBQ1RGe1VORDNSU1Q0TkQxTkdfQ1lCM1JfUz | base64 -d
CSACTF{UND3RST4ND1NG_CYB3R_Sbase64: 無効な入力

まだ情報が足りない。このページにMissing's Bug Hunting Cookbookというリンクがある。そのページ https://bh-cookbook.github.io/ にアクセスする。
コメントにはこう書かれている。

<!-- Part 3: NDVVIxVFlfMU5fNExMX Thank you Eli for designing the fancy logo on the CTF Homepage. -->

先ほどのBase64文字列とさらに結合して、Base64デコードする。

$ echo Q1NBQ1RGe1VORDNSU1Q0TkQxTkdfQ1lCM1JfUzNDVVIxVFlfMU5fNExMX | base64 -d
CSACTF{UND3RST4ND1NG_CYB3R_S3CUR1TY_1N_4LLbase64: 無効な入力

次はhttps://ctf.utsacyber.com/のページのロゴ画像をダウンロードすると、PNGファイルの後ろにデータがある。

Part 4: zFUU19EME00MU5TfQo=

先ほどのBase64文字列とさらに結合して、デコードする。

$ echo Q1NBQ1RGe1VORDNSU1Q0TkQxTkdfQ1lCM1JfUzNDVVIxVFlfMU5fNExMXzFUU19EME00MU5TfQo= | base64 -d
CSACTF{UND3RST4ND1NG_CYB3R_S3CUR1TY_1N_4LL_1TS_D0M41NS}
CSACTF{UND3RST4ND1NG_CYB3R_S3CUR1TY_1N_4LL_1TS_D0M41NS}