こういうはまり方をした。
https://run.dlang.io/is/LpLTja
longを引数に取る f が呼ばれると思ってたんだけど、その実 bool のほうが呼ばれてた。0と1に限っては、こういうことになるらしい。 回避策としては 0L、1Lと書いてlongを明示するか、 int も overload して、 long を呼ぶか。
こういうはまり方をした。
https://run.dlang.io/is/LpLTja
longを引数に取る f が呼ばれると思ってたんだけど、その実 bool のほうが呼ばれてた。0と1に限っては、こういうことになるらしい。 回避策としては 0L、1Lと書いてlongを明示するか、 int も overload して、 long を呼ぶか。
奈良高専は insecure というチームで、 theoldmoon0602、 thrust2799、 kyumina、 miwpayou の四人で、高専セキュリティコンテストに参加しました。現地行きたかったけど申し込み失敗しました。
これは二日間(実質24時間程度)の Jeopardy + King of the Hill なCTFで、 Jeopardy が 22問くらい、 KoH が 2問ありました。 KoH では 30点が 10分ごとに与えられます。
私チーム insecure はチームワークを遺憾なく発揮して、 3930 点で 1位(35チーム中)でした。私はそのうち、 1400 + 630 点を入れましたので、解いたものについて 簡単な Write up をします。
バイナリが降ってくるので strings する
途中で追加された問題。31M くらいのバイナリが渡されて、これが独自FSらしい。めっちゃよなべしたけど解けなかった。最終的には解いた。
バイナリは GPT でパーティショニングされているということ。1クラスタ4096byte 単位で管理されていること。AllocationTableのサイズ、DirectoryEntry 構造体が示されていた。
最初は binwalk などしていたが、 何かしらの zip ファイルが存在していて、 flag.png、 _MAXOSX/.flag.png が入ってることしかわからず、 unzip はできなかった。
二日目になってやっと参考になるサイトを見つけて ( http://memes.sakura.ne.jp/memes/?page_id=2303 ) 、AllocationTable がどのような存在かを理解したので、ごちゃっとパーサを書いたらunzippable な zip を取り出せました。
方針としては、
です。
import struct import os import codecs import sys from io import SEEK_CUR,SEEK_SET LBA_SIZE = 512 f = open("raw.dmg", "rb") f.seek(512, SEEK_CUR) # skip mbr f.seek(512, SEEK_CUR) # gpt header (primary) f.seek(32, SEEK_CUR) # partition entry first_sector = struct.unpack('<Q', f.read(8))[0] last_sector = struct.unpack('<Q', f.read(8))[0] f.seek(8, SEEK_CUR) partition_name = f.read(72) partition_name_str = codecs.decode(partition_name, encoding='utf_16_le') if len(partition_name_str)>0: print("------------") print("first sector: {}".format(first_sector)) print("last sector: {}".format(last_sector)) print("name: {}".format(partition_name_str)) print() f.seek( first_sector*LBA_SIZE, SEEK_SET) AT = f.tell() print("ALLOCATION TABLE IS {:0X}".format(AT)) allocationTable = [] for _ in range(8192): v = f.read(2) v = struct.unpack("<H", v)[0] allocationTable.append(v) print("----------------") print("Allocation Table"); for i, v in enumerate(allocationTable): if v == 0: continue # print("[{0}] {1:0X}( {1} )".format(i, v)); pass DIRENTS = f.tell() print("DIRECTORY ENTRIES AT: {:0X}".format(DIRENTS)) binvalue = [] while f.tell() < (last_sector+1)*LBA_SIZE: # read directory entry tell = f.tell() magic = f.read(1) if magic != b'\x0f': f.seek(31, SEEK_CUR) continue name = f.read(8) ext = f.read(3) size = struct.unpack("<I", f.read(4))[0] offset = struct.unpack("<H", f.read(2))[0] attr = f.read(12) reserved = f.read(2) print("-----") print("@ {:0X}".format(tell)) print("magic: {}".format(magic)) print("name: {}".format(name)) print("ext: {}".format(ext)) print("size: {0}({0:0X})".format(size)) print("offset: {0}({0:0X})".format(offset)) print("attr: {}".format(attr)) print("resv: {}".format(reserved)) nextoffset = offset while nextoffset != 0xffff: f.seek(0x1000 * nextoffset + AT, SEEK_SET) b = f.read(0x1000) binvalue.append(b) print("atvalue: ", end="") print(allocationTable[nextoffset]) nextoffset = allocationTable[nextoffset] if nextoffset >= len(allocationTable): break print() break o = open("kore", "wb") for b in binvalue: o.write(b)
これはほとんど thrust2799 が解いた。
わけのわからん文字列が与えられるけど、 base64 -d すると16進値っぽい数字列になる。これを decode('hex') してやると KP から始まる文字列になるので、エンディアンをひっくり返してあげて、保存する。 file をかけると Word 2007 と出るので、 libre Office Writer で開くと 縦にフラグが書いてある。コピーして適当なファイルに保存して cat | tr -d '\n' した。
pcap が降ってくるので strings してうまいことつなぎ合わせる
pcap が降ってくるので strings してBase64っぽい文字列を見かけたら base64 -d する
ログインフォームがあるのでユーザ名 admin、パスワード 'or'a'='a と入力する
ログインフォームと、ログインのロジックをPHPで書いたものが表示されるので ざっと眺めて login.db みたいな名前がひっかかる。アクセスすると sqlite dbファイルが降ってくるので apt install sqlite3 して sqlite3 login.db する。 .schema で users というテーブルだけがあることがわかるので select * from usersすると admin のパスワードがフラグになっている。
難読化された javascript がある。適当にBeautifyして処理を一つずつ追ってやる。すると DOM を書き換えるしょりと、別のことをしている処理が存在することがわかる。 別のことをしている処理が怪しいので単体で実行するように書き換え、余計な分岐をはずしてやるとフラグが手に入る。
いわゆる Javascript puzzle。解き方が何処かに乗ってないかと思って調べたら burningCTF でまったく同じ問題が出題されていることがわかったのでWriteUpをみながら真似をした。
pcap ファイルが降ってくる。サイズはそこそこ。みると、幾つかの ssh ログインとたくさんの TCP SYN がある。ssh はよくわからないIP同士で通信していることが多い。TCP SYNは 10.201.1.101から10.201.1.102 へのもの。最初はポートスキャンかなと思っていた。
ちょっとだけある HTTP パケットにフラグが書いてあり、さらにバイナリが存在することが示唆される。フラグに port knocking とあるので TCP SYN のうちACKが帰ってきたものを真似て port knocking してやると a.out が手に入る(port knocking しなくてもいいという説がある→作問者がいるって言ってた)。
フラグは thrust2799 が手に入れ、私が port knocking してみれば、と言った。 thrust2799 はバイナリ読めないと開始前から何度も言っていたので、私が解析することにしてファイルを貰った。
radare2 で見ていたがわかりにくかったので Bokken を入れてる miwpayou にも解析してもらった。どうやら a.out は 8081番で待ち受ける fork型のサーバで、fork した先でstrncmp や strlen を使い、一部で popen していることがわかった。
gdb が存在することを思い出したので set follow-fork-mode child して b do_workして、fork後の処理を追う。 strncmp では recv した文字列を SCK と比較していることがわかった。さらに次では recvした文字列+3を popenに突っ込んでいることがわかった。
そこで試しに SCKwhoami と送ってみたところ ubuntu というレスポンスが来た。 SCKls → SCKcat README → SCKecho insecure >> flag してKoHになった。
余談だけど SCKkillall a.out したらサーバからのれすぽんすがなくなった。これを運営に報告したらしばらく経って復活した。復活して SCKwhoami したら root だった。
ptr-yudaiのブログもあわせてどうぞ
開会です。いろんな説明を受けた後、「セキュリティ基礎」がありました。
セキュリティ基礎では、今後セキュリティ業界でどんな仕事がAIに取って代わられるだろうということをグループディスカッションしました。
その後、今度は特別講義として、2つの講義がありました。一つはJPCERTの人の話で、その人がどんな仕事をしているかという話なのですが、なんでこんなにおもしろい話ができるんだというほど面白く話をしてくれました。特に森見登美彦の太陽の塔を引用したところは最高でした(これは本質じゃないけど)。
続いて、チュータの人の紹介を兼ねた発表がありました。すごかった。
そこまでして、夕飯を食べ、それからグループワークでした。グループワークでは、数日かけて、8人程度で、何か問題を解決する姿勢を見せていこうというようなものです。いろんな人に話を聞き、どんなことを自分たちがすればいいのかを考えて、最終日に発表することになります。
Day2では、Dの解析トラック(カーネルのエクスプロイトまわり)の授業を受講しました。講師は、がちゃぴん先生とかるくす先生です。D1ががちゃぴん先生のカーネルエクスプロイト入門編で、D2,3がるくす先生による実践編のような位置づけでした。
D1では、まずカーネルソースの大雑把な構造から始まりました。大体この話だけで学校のオペレーティングシステムの授業を越えて行きました。
それから、カーネル周りの知識として、カーネルのダンプ(メモリ全部のダンプ)の解析(あるプロセスが脆弱性を突かれてメモリをアホほど食べているのでそれを突き止める。フォレンジックっぽい)をやったり(できなかった)、Dirty COWについて簡単な仕組みとPOCを渡されてこれをもうちょい実用的にしてくれといわれたりしました(できなかった)。あとあと、セキュリティキャンプの応募課題(るくす氏が出してた)のフォローアップがありました。私はこの課題は挑戦しかけて諦めたのでなにもわからなかった。
というわからないづくしの時間をすごしました。でも絶対無駄じゃないし、丁寧に資料が作られていたのでまた読み返してきっちり倒します。
お昼を挟んで、こんどはるくす先生のD2,3でした。これは究極的にはBadIRETで権限昇格をしようというものなのですが(さらに言えばWebKitの任意コード実行から権限昇格したい)、まず前座としてBadIRETの原理でカーネルを落とそうということをやりました。落とすだけならもうコードをるくす先生が書いていたのですが、これにちょっとだけ細工をして、レジスタをいじってから落とすとかしました。運が良いと結果が出るらしいですが、徳を積みすぎてほぼ毎回でました。
こんどはブラウザほうからアプローチして、(これも任意コード実行のところはるくす先生が書いてくれていたので、)試しに fd = open("/dev", 0); getdents(fd, buf, 4096); write(1, buf, 4096)
するシェルコ―ドを書いて動かそうという演習がありました。これがいろいろはまりどころがあって、なかなか進まず、なんとかこれができたくらいで夕食になりました。
夕食は同じ講義の人とたべて、ちょっと仲良くなりました。
今日は miyagaw61 さんと __ukun さんの顔と名前とアイコンとハンドル画一致した
— ふるちゅき (@theoldmoon0602) 2017年8月15日
夕食後も同じことをやって、更に進んで、BadIRETでの権限昇格コードを書き始めようというところまで来ました。しかしこれがとてもむずかしく、原理が理解できず、コードもまたかけない。悩みに悩んでタイムアップでした。悔しい。
21時から22時までは、(権限昇格が難しすぎたので急遽)カーネルの脆弱性の緩和策などのサーベイになりました。
これでやっと講義がおわり、昨日が終わったというわけです。本当に疲れていて、疲れ of 疲れという感じでした。
るくす先生「どうもみなさまお疲れ様でした」
— ふるちゅき (@theoldmoon0602) 2017年8月15日
みんな「……」(疲れて声が出ない) #seccamp
今日は2つの講義と、2つのBoFと企業講演とグループワークがありました。
午前の講義では、Dトラックの、「マルウェア x 機械学習」を受けました。
講義としては、機械学習の簡単な説明から始まり、じゃあ機械学習でマルウェア検知するならどうやってやる? みたいな話をグループでしました。ありきたりな選択肢しか出ませんでしたがまあそれは良いです。講師の人からは、バイナリのタイムスタンプがわりと効果的という話をききました。
ここまでやって、続いてデータセット(トレーニング用マルウェア・正常プログラム1000件ずつ、評価用マルウェア・正常プログラム500件ずつ)と、簡単なランダムフォレストの実装、バイナリファイルからの特徴量の抽出サンプル(ひどい特徴の選び方をしているやつ。出力フォーマットのサンプルというわけ)が渡されまして、これを改善していく(特徴の選び方を変えていく)という演習をやりました。
最初はAccurancyが5割とか(五割くらい検知するし、五割くらい誤検知する感じ)だったわけですが、試行錯誤で、87%程度まで上がりました(7割くらい検知するし、1割くらい誤検知する感じだったと思う)。特徴としては{タイムスタンプ、バイナリサイズ、DLLのリンク量、DLLから読んでる関数の数、シンボル数}くらいを採用しました。9割行かなくて残念でしたが、同じ講義を受けていた人の中では一番いい値が出たようです。
マルウェア分類のAccuracyが87.5% になった。9割届かず悲しい #seccamp
— ふるちゅき (@theoldmoon0602) 2017年8月16日
そこまでで時間いっぱいでした。続いてお昼ですが、一人で黙々と食べました。おわり。
午後の講義は「信じて送り出した家庭用ルータがNetBSDにドハマリしてloginプロンプトを返してくるようになるわけがない」でした。この講義では結局VirtualBoxにNetBSDを入れて、そこでラズベリーパイ用のNetBSDをビルドしました。そしてそれをラズパイに書き込んで起動ヤッターという感じです。それだけしかしていなくて、あとはこぼれ話がいろいろという感じだたのですが、私はラズベリーパイ用のイメージのクロスコンパイルで失敗しまくってうえええって言っていました。これの原因はメモリが足りないというものだったのですが、1Gじゃあ足りないんですね……。
私だけツールチェインのビルドに失敗するんですが、徳ですか!? #seccamp
— ふるちゅき (@theoldmoon0602) 2017年8月16日
はい、そのあと夕食ですが、割と席指定の夕食で、同じ講義を受けていた ukn さんと、昨日お世話になったるくす先生、コアキャンプに来ていた hama さんと食べました。私は静かに座っているかかりをしていました。
(夕食の席で)るくす氏「一番つらかった講義ってなんですか?」
— ふるちゅき (@theoldmoon0602) 2017年8月16日
夕食後はBoFと企業講演でした。BoFはあんまり書くことないと思うんですが、二個目の講義がものすごくためになるというか、セキュリティとはみたいな本質情報を突いてくれるもので、今年のキャンプで一番忘れられない話になりそうでした。
その後はグループワークでした。グループワークのメンバリーダが結構苦手のようです。考え方が合わないつらい。
午前では、はじめはブラウザ上のスマホゲーっぽいのをチートしてたのですが解けなかった(あると思ってたサーバ側の処理がなかったらしくてめっちゃ簡単だった)。
続いてはAndroidエミュレータ立ち上げて、講師の人が2週間くらいで作ったと言ってたゲームのチートをやりました(ゲームとしての完成度が高くてすごかった)。メモリ書き換えとかファイル書き換えとか通信書き換えをやっていろいろチートしました。ファイル書き換えはやったことなかったので憶えました。あと、通信がxor -> base64 で難読化されてたんですが、 xor に気がつけずちょっと悔しかったです。
この講義ではどうやってチートするのかということと合わせて、開発者側の視点での話も聞けたので大変良かったです。
午後はまた別のゲームチートで、開幕即演習というか演習しかなく、ひたすら手を動かしていました。UnityゲームのC#なdllをいじるのは一瞬だったんですが、apkの.soを解析して……となるとなかなか厳しく、有意義でつらい時間を過ごしました。
企業講演でサイボウズさんのお話を聞き、そしてグループワークでした。
グループワークのやばいところは個人的にリーダへのヘイトが溜まっていくところです。ずっと相容れなさを感じてる。
キャンプ恒例のプレゼントがありました。翌日時間がなさそうだったからこの日にあったんですかね。
ありがとうございます!!!! #seccamp pic.twitter.com/fL1WouStrS
— ふるちゅき (@theoldmoon0602) 2017年8月17日
講義が終わったという安堵と、前日の晩に資料作成のうち私のやるべきところは終わっていた(たたき台ばかりつくってた)のでほとんど死んでました。
グループワーク発表は面白いところとか上手なところとかそうでないところとかありましたが、去年の程の面白さが合ったのかどうかは微妙じゃないかなと思っています。うちのチームもそれなりの発表をしました。
それでお昼を食べ、午後は成果発表でした。まずは集中コースでptr-yudaiの発表とか、その他すごい発表を聞いていました。
名残惜しいですがこれでだいたい全過程が終了となりまして、あとは挨拶などを聞き、写真撮影などし、解散でした。
終わった後ご飯でもという感じだったのですが、きっと都心方面だろうとたかをくくって先に歩を進めていたら逆で、結局参加できずまっすぐホテルに来ました。
キャンプでは毎日、健康調査票というのを提出していて、これは各食事ちゃんと摂ったかとか寝れてるかとかを丸とか三角とかで書くわけですが、そこに記述欄がありました。それで
健康調査票ってのを日毎に提出するんですが、記述欄に「体が冷える」とか「疲れてる」って雑に書いちゃうとめっちゃ丁寧なお返事をもらってしまい恐縮する
— ふるちゅき (@theoldmoon0602) 2017年8月16日
というわけです。上述した**さんがこのお返事を書いてくれたひとで、かなり嬉しかったので、**さんには悪いなと思いつつ、ちょっとどんな内容だったかを書いていきます。
Day 2
ふるつき「体がひえる」
**さん「上着で***てください。必要であればブランケットの貸出もあるので言ってください(空調の調節等何かあればスタッフに言ってください」
Day 3
ふるつき「疲れてる」
**さん「お疲れ様です。なかなか難しいと思いますが休めるときは早めに休んでください。何かあれば連絡を……」
Day 4
ふるつき「いきてます」
**さん「良かったです!!!! コメントありがとう。体調について分かりやすかったです。今日までお疲れ様でした、ラスト一日も頑張って、そして***ください」
記述欄は書いていきましょうという話。
--
ptr-yudaiがキャンプ中に私のツイッターを監視していたりしたらしいのですが、その画面を見た誰か(数人いるっぽい)が「ふるつきさんですか!?」と尋ねる事案が発生していたっぽいです。一体誰なんだ。