Theories of Pleiades

技術の話とかイベントに行った話とか思ったこととか

DensanCTFのRev問を作りました・解説

TL;dr

  • 一関高専電算部主催 #DensanCTF のお手伝いをした
  • 初心者向けReversing問題3つ作った
  • 解いてもらえると…嬉しい!


やったこと

一関高専電算部主催のCTF初心者高専生対象のCTF DensanCTF のお手伝いとして宇部高専からReversingの問題を3問ほど作らせていただきました。

CTFはぼくも初心者で,作問をするのは初めての経験でした。

初心者向けとのことで作問やらないかと声を掛けていただいて,直前になって急いで作ったのでクオリティが高いとは言えない感じでした(はい)(反省しています)。


以下,ぼくの作った問題の想定解による解説をさせていただきます。


Hello, Reversing World! (100)

100点のwarm-up問題です。

問題文は以下の通り。

「あれ?フラグはこの形式で間違いないはずなのに…」

えい,えいっ。 何故だ,何度フラグを入力しても正解にならない。タイプミス…は,していないはずだし。

どうやらこの問題を作ったやつは酷く捻くれているらしい。 偽のフラグで私たちを騙そうとしているんだ!

フラグは,本当のフラグはどこ? お願い,一緒に正しいフラグを探すのを手伝って!


解法として,まず問題のファイルをダウンロードし,fileコマンドを使ってファイル形式を特定します。

$ file hello_reversing_world
hello_reversing_world: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamicallylinked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 3.2.0, BuildID[sha1]=7e89c845c01192e6e7335f170c083d8add217bc7, not stripped


実行結果より,このファイルがelfファイルであることがわかります。

次に,実際に実行して動きを確かめてみます。

$ ./hello_reversing_world
FLAG{This_is_Dummy_FLAG...}

実行結果は確かにFLAG{XXXXXXX}の形式を満たしていますが,これをsubmitしても正解にはなりません。

どうやら,問題文の少女はこの問題に直面して困っているようです。


そこで,ファイルの中に正しいフラグが隠されていることを疑います。

ここでは,stringsコマンドとgrepコマンドを使います。

$ strings hello_reversing_world | grep FLAG
FLAG{Bravo!This_is_True_FLAG}

こうして正しいフラグを得ることができました。


More, more, more (300)

300点,実行ファイルを動的解析してみようという問題です。

問題文は以下の通り。

「もっと,もっと頂戴」

うわ言のように彼女は繰り返す。

「まだ足りないの」

何が?何が足りない。私は何を与えれば良い? 一刻も早く彼女を静めたいのに,彼女の求めているものが何なのか私にはわからない。知る由もない。

そうだ,そこの君。私は彼女の面倒を見るので手一杯なんだ。 そこに何か手がかりはない?

彼女が何を求めているのか,それを見つけ出して教えてほしいの。


想定解法として,まずfileコマンドでこのファイルがelfファイルであることがわかります。

そこで,実際に実行してみます。

$ ./more_more_more
Input pass phrase...

パスフレーズを求めるメッセージが出力された後,入力待ちになります。とりあえず何かを入力してみましょう。

$ ./more_more_more
Input pass phrase...
hoge
Oh, no... Your Answer is Wrong. Retry many times!

この実行結果より,hogeパスフレーズとして相応しくなかったということがわかります。


さて,正しいパスフレーズを探さなければなりません。

stringsコマンドで見てみても,パスフレーズらしきものは出力されないことがわかるでしょう(もちろん,フラグも出力されません)。

ここで,プログラムを動かしながら解析する動的解析という手法を用います。

使うコマンドは,ltraceコマンドです。

$ ltrace ./more_more_more
puts("Input pass phrase..."Input pass phrase...
)                   = 21
__isoc99_scanf(0x555a0825101d, 0x7ffe6d6a72d0, 0x7fddb5848720, 0x7fddb5774818

__isoc99_scanfという関数が表示された後,動作が止まります。入力待ちです。

再度適当な文字列を入力して動作を追ってみます。

$ ltrace ./more_more_more
puts("Input pass phrase..."Input pass phrase...
)                   = 21
__isoc99_scanf(0x555a0825101d, 0x7ffe6d6a72d0, 0x7fddb5848720, 0x7fddb5774818hoge
) = 1
strcat("I_w", "ann")                           = "I_wann"
strcat("I_wann", "a_e")                        = "I_wanna_e"
strcat("I_wanna_e", "at_")                     = "I_wanna_eat_"
strcat("I_wanna_eat_", "Mik")                  = "I_wanna_eat_Mik"
strcat("I_wanna_eat_Mik", "an_")               = "I_wanna_eat_Mikan_"
strcat("I_wanna_eat_Mikan_", "Nab")            = "I_wanna_eat_Mikan_Nab"
strcat("I_wanna_eat_Mikan_Nab", "e!!")         = "I_wanna_eat_Mikan_Nabe!!"
strcat("I_wanna_eat_Mikan_Nabe!!", "!")        = "I_wanna_eat_Mikan_Nabe!!!"
strcmp("hoge", "I_wanna_eat_Mikan_Nabe!!!")    = 31
puts("Oh, no... Your Answer is Wrong. "...Oh, no... Your Answer is Wrong. Retry many times!
)    = 50
+++ exited (status 0) +++

すると,数回strcatという関数が呼ばれた後,strcmpという関数に"hoge","I_wanna_eat_Mikan_Nabe!!!"という文字列が与えられていることがわかります。


ここで,"hoge"の部分には自分の入力した文字列が入っています(数回別の文字列を入力して見るとわかるでしょう)。

strcmpというのは,C言語のstring.hで提供される文字列を比較する関数です。

よって,比べられている"I_wanna_eat_Mikan_Nabe!!!"という文字列と同じ文字列が正しい文字列なのではないか?というように推測ができます。


実際に試してみると,以下のようになります。

$ ./more_more_more
Input pass phrase...
I_wanna_eat_Mikan_Nabe!!!
Good job! Your Answer is correct!
The flag is ...
FLAG{I_need_more_orange}

問題文の少女はみかんが食べたかったようです。ぼくかな?


Health care (300)

300点,静的解析の問題です。

問題文はこちら。

「ふぁ~あ,もう21時か」

ここのところずっと残業続きだ。肩も凝るし,何より足のむくみが酷い。 何やら良いリフレッシュはないものか。できることなら1日30時間寝てやりたい。

頭の中でひとりごちながら帰宅の準備を進めていたそのとき,スッコココ,と聞き慣れたSEが静かなオフィスに響く。

『お疲れ様。頑張る後輩へのプレゼントだ』

上司からのメッセージと共に謎のファイルを受信する。 全く,誰がこんな難解なプレゼントを送るんだ。

愉快そうに笑う上司の顔が脳裏に過る。

一体どんなプレゼントなんだろうな。思わず口角が上がる。 …やってやろうじゃないか。


elfファイルが与えられます。実行すると以下のように動作します。

$ ./health_care
Stage 1: Put your answer

何かしらの入力を求められています。

$ ./health_care
Stage 1: Put your answer
hoge
Oh, no... Retry more times!

ダメみたいですね。

今回も同じようにstringsコマンドやltraceコマンドなどを使ってみますが,有効な情報は得られそうにありません。


そこでプログラムを逆アセンブルし,実際にソースを読みながら実行の流れを追ってみます。

$ objdump -M intel -d health_care

出力結果は長いので全ては載せませんが,注目スべきはmain関数の部分です。

00000000000012ab <main>:と書かれている行を探します。


その中を眺めていくと,

12e1:       e8 8a fd ff ff          call   1070 <__isoc99_scanf@plt>
12e6:       8b 45 ec                mov    eax,DWORD PTR [rbp-0x14]
12e9:       83 f8 0a                cmp    eax,0xa
12ec:       74 18                   je     1306 <main+0x5b>

という箇所があることがわかります。

call命令でscanf関数を呼び出していたり,cmp命令で何かを比較していたりします。明らかに怪しいですね。


ここで,scanf関数によって取得された入力値はcallの次の行にあるmov命令によってeaxというレジスタに格納されます。

その後,cmp命令によりeaxと0xaという値が比較され,それが等しいならmain+0x5bの位置にジャンプしていることがわかります。

よって,入力に0xa(つまり10進数で10)を渡せばStage1を通過できそうです。


実際に試してみると,以下のようになります。

$ ./health_care
Stage 1: Put your answer
10
Stage 2: Put your answer

どうやら,Stage1はこれで正解のようです。次はStage2を見てみます。

アセンブル結果のmain関数内を追っていくと,もう1つscanf関数が呼ばれている箇所があります。

1325:       e8 46 fd ff ff          call   1070 <__isoc99_scanf@plt>
132a:       8b 45 f0                mov    eax,DWORD PTR [rbp-0x10]
132d:       83 f0 0d                xor    eax,0xd
1330:       89 45 f4                mov    DWORD PTR [rbp-0xc],eax
1333:       83 7d f4 03             cmp    DWORD PTR [rbp-0xc],0x3
1337:       74 18                   je     1351 <main+0xa6>

このかたまりです。

先ほどと同じように,scanfで取得した値がeaxに格納されます。

その後,xor命令でeaxと0xd(つまり,10進数で13)の排他的論理和を取っていることがわかります。

その後mov命令でDWORD PTR [rbp-0xc]という場所にその結果を格納し,その値と0x3(10進数でも3ですね)を比較していることがわかります。


よって,0xdでXORを取ると3になる値を入力すれば良いことがわかります。

つまり,入力すべき値は14です。

これを踏まえて,もう一度実行してみます。

$ ./health_care
Stage 1: Put your answer
10
Stage 2: Put your answer
14
Good job! The flag is...FLAG{Recovering_from_my_foot's_fatigue_by_foot_bath}

上司からの『足湯で疲れをふっとばす』というリフレッシュ方法の提案がフラグでした。


Reversing問題の作問

Reversing問題の作問はこれが初めてでしたが,思った以上に情報がなくて大変でした。

以下問題ごとにソースコードと作問中試行錯誤したことなどを書いておきたいと思います。今後Rev問作ってみたい人は参考にしてください。


Hello, Reversing World! (100)

stringsコマンドでフラグを得る問題にしようというのは決めていました。

はじめはprintfにフラグを置いてコメントアウトしてみたのですが,コンパイル時にコメント部分は無視されるので実行ファイルからフラグは得られません(それはそう!)。


次に,define文にフラグを置いてmain関数は空のままコンパイルしてみましたがこれもダメでした。最適化とかが働いてるのかな。

ということで一度どこかで出力などして文字列を使わなければなりません。

結局,printfで一旦正解のフラグを出力し,行頭まで遡って偽のフラグを被せて出力するという方法にしました。


ただ,grep FLAGしたときに偽のフラグも一緒に出力されるのは不格好だったため,偽のフラグは1文字ずつ出力する形を取りました。

(にしても別にASCIIでデコードする必要なくて普通にchar配列作れば良かった気がするな)


ソースコードは以下の通りです。


More, more, more (300)

この問題は,どうやってパスフレーズをstringsから隠しつつ比較に使うかを考えていました。

結果的にはデフォルトのstringsコマンドで出力されない3文字ずつの塊に分けてプログラム中でstrcatを使ってひとつの文字列につなぎ直すという手段を取りました。

が,普通に1文字ずつの方がいろいろと良くないですか?という気持ちが今はしています。何故3文字。


ソースコードは以下の通り。


Health care (300)

この問題は一番適当に作りました本当に申し訳ありませんでした。足湯に浸かりながらうわ眠〜とか言いながら作ってました。

はじめは四則演算とかにしようかなと思ったのですが,素直に

if(a-3 == 10){
    処理;
}

とかを書くとコンパイラに最適化されてアセンブリではaと13を比較しているというようになってしまいます。

ちなみにちょうど今日ある本を読んでいたときに最適化を外せるオプションがあることを知りました。


これを回避するために,一度変数を噛ませてあげると良かったりします。

以下のような感じです。

int b = a-3;
if(b == 10){
    処理;
}


また,1問目や2問目のようにフラグをmain関数中で1文字ずつ表示する方法を取ると逆アセンブルしたときmain関数内に大量の

mov DWORD PTR [rbp-0xXX], 0xYY

みたいな命令が吐かれてしまいます。

これは単純にわかる人には第二引数をASCIIで解釈して並べればフラグが得られるというのがわかってしまう上に,初心者がmain関数を読む上では大変邪魔なノイズになり得ます。

ということで,フラグの表示は別の関数に分割しました。

また,そちらを読まれてもフラグがわかるなどということにならないように,表示する文字列は3文字ずつに分割してdefine文に入れました。


もうちょっと頭の良い方法が無いんでしょうか。考えればありそうですが考えるのがしんどかったのでこれで妥協してしまった。コードは目も当てられない状態になっています。


感想

自分の作った問題を解いてもらっていたり「これ解けなかったー」って言われてたりするのちょっとドキドキしちゃうなという気持ちになりました。

初心者向けということでそんなに複雑なことはしなかったのですが,もう少し配布資料を作り込めば良かったかもしれません。

どんな要素を入れようかなとかどんな関数使うとどんな挙動するかなとか考えるのが楽しくて,久々に低レイヤの機運が高まるなどをしました。


個人的にはHello, Reversing World!の挙動が気に入っているのですが,それとは別にMore, more, moreはTwitter上で「はすみさんの作問だとすぐわかった」みたいな感想がたくさんあってちょっと嬉しかったです。

また機会があれば作問とかしたいなあと思っています。

もし出番がありそうなら誰か呼んでください。初心者向けしか作れませんよろしくお願いします。


DensanCTF運営のみなさん,参加者のみなさん,お疲れ様でした!

19歳の誕生日を迎えました

tl; dr

  • 19歳になりました(これマジ?)
  • 内定取るぞ
  • 強く生きていきます


人生19年目突入

してしまった……………………(絶望)


惰性でなんとか生き長らえてきたこの人生ももう19年目になるそうです。

全然そんな感じしなくて不思議だ。


本日1/10は私hsm_hxの誕生日です。おめでとうございます。


今日の誕生日に合わせてたくさんの人から誕生日プレゼントをいただきました。

以下に貰ったものを載せていきます。漏れがあったら殴り倒してください。


なんというか,これだけ応援してもらってるの本当にありがたいなあという感じです。

期待に応えられるようこれからも頑張っていきます。


今年の目標みたいなやつ

一区切りなので,今年の目標でも立てようと思います。


今年は自分に言い訳をしない1年にしたいと思っています。

せっかくいろんな方から文具とか鞄とか社会性グッズを貰ったので,フルに活用したいです。もちろん本も絶対に積まないという強い意志があります。


自分に言い訳をしないという目標について,具体的には

  • 成績の低下を技術の勉強のせいにしない
  • レポートはちゃんと出す
  • 内定を取る(ここ重要)

に重点を置いて頑張りたいと思っています。


成績については2年生のとき1位だったのに今は37位なので,もういちど1桁順位を狙うぐらいはしたいと思っています。


あとはそうですね,好きなことをするのはやめたくないですが,ちゃんとやることはやるぞという気持ちです。

Twitterとか本当にね。


余談

今ぼくは自分の「今」のことを「ボーナスステージ」だと思っています。

この場で不幸自慢をする気は無いですが,何度か死にかけた経験も死にたくなったこともあります。

そのことを考えると,いつ死んでいてもおかしくなかったと思うし,今後だっていつ死ぬかわからないと思っています。


いつ死んでいてもおかしくない,いつ死んでもおかしくないのが人生なので,ぼくはあんまり遠い将来のことは考えずに身の丈にあった生き方をしていきたいなと思っています。


ぼくは遠い未来をしっかり見据えることができるほど器用ではないので,とにかく今,現在に必死にしがみついて生きていきます。

という感じの19歳にしたいです。


ところで今年一番の目標はプロコン本戦出場・何かしらを受賞することです。がんばります。

弊部の将来有望なプロ後輩(@sora_1_5@omusubi__0418)を絶対にプロコン会場に連れていきます。


19歳も頑張って生きていきます。対戦よろしくおねがいします。

第2回高専キャリア全国大会に参加しました

tl;dr

  • 部活と自分の思想について5分話した
  • 新しい人脈がいっぱいできた
  • 思想多様性を感じた
  • オタクと行く秋葉原は最高


高専キャリア全国大会

1/5(土)に東京・六本木ヒルズにて行われた第2回高専キャリア全国大会に参加してきました。



このイベントなんと登壇すれば交通費全額支給(!!!)とのことで,何も考えず知った瞬間に応募しました。

(運営さんのミスで結局見学の学生にも交通費が出たらしい)


テーマは「進捗」とのことで,最近進捗は生んでるけど全部対外発信しちゃってるし何の話しよう…としばらく悩んだのですが,結局部活運営の話をすることに。


ただ,部活運営の話だけでは以前ブログに書いたものと変わり映えしないため,部活の話をしつつそのバックにある自分の考え方の話をメインで資料を作りました。



…というのが,前日ホテルに着いてからの話。

「5分過ぎたら容赦なく切るぞ!」と言われていたのですが,ぼくの場合練習すると緊張したときに練習通りできなくて頭が真っ白になってしまうので完全にアドリブで臨みました。


ということで全く無対策で当日を迎え,六本木ヒルズに向かいます。


会場に着いたら見慣れた人々や主催のりゅーかんさんなどがいて,イケてる会場にそわそわしてしまいあっちこっちうろうろしたりをしていました。


会場の一角にはこんなものも存在しており,なるほど高専だなという気持ちになりました。



イベントは高専の先生や高専卒の先輩,高専生が好きな人事さんの4人によるパネルディスカッションから始まりました。

乗っけから哲学っぽい話があったりで「おっ,これはぼくのプレゼン捗りそうだぞ」と密かににっこり。


特に「悪い環境の中にあるオアシスを開拓していく」みたいな話があったときは

「ぼくの話しました!!!!!?????」

とテンションが上がっていたりしました。


プレゼンパートでは,現役高専生・高専中退生・高専卒業生の最近の進捗に関するプレゼンを半日ぎゅーーーーっと詰め込んでたくさん聞くことができました!

開発したプロダクトに関する話や起業の話,コンテストの話などなど,高専生の"湯葉"たちによるいろいろな話がありました。


高専生と一口に言っても普段関わるような情報系の人から機械系の人や高専に在学しながらイラストレーターとして活動しているといった変わった経歴の人などいろいろで,どの話も聞き応えがありました。



ちなみにぼくの発表は「世界征服が夢の高専生が部活を征服する話」というものです。


普段は「進捗」そのものを発表することが多かったのですが,今回は一応「高専キャリア」だったので自分の考え方や今後やりたいこと,何のために今この場で登壇しているのか,そういったことに重点を置いて喋ってみました。

正直スライドを作っているときは「えっこれ完全に宗教勧誘じゃねえ?」と思っていたのですが,かなり良い反響をいただけて嬉しかったです!



これはスライドを作っているときのぼくです。


そしてこちらは登壇後のぼくです。


たくさんうれしいお言葉をいただきましたが,載せきれないので一部のみ。



はい。



残念ながらMVPはいただけなかったのですが,嬉しいお言葉をたくさんいただきました✌


主催が今日イチって言ってるので実質MVPですよろしくおねがいします。


懇親会でははじめましての人ともたくさんお話できてうれしかったです!

めちゃくちゃに実り多い1日でした。

また次に開催されたときもお邪魔したいなと思っています。


個人的には,こういった場にぼく以外の宇部高専生が来ていた(しかも2人も)のが本当にうれしくて,それだけでも自分が学校で頑張っている甲斐があったなと思ったりしています。

次のときはもっと増えるとうれしい!(運営様交通費補助のほどよろしくお願いいたします)


ただ,時間制限が機能していなかったことは若干気になった点ではあります。

ぼくは5分ジャストで話したのですが,間に合わせるためにマシンガントークになってしまったり途中で息切れしたりしていたので,時間超えて喋ってる人ずるい><になりました(ずるいので)。


次のときは時間に余裕持ってスケジュール組んであると嬉しいです(交代の時間も考慮されていなかったので気になってしまった)。



このあと,一部メンバーでサイゼリヤに行って食事会をしました。

結構深淵な話になったりもしたのですが,あまりに安心感のある空気だったせいで後半には完全に酔っ払いのようなテンションで部活disとかをしてしまいました。


宇部高専の校歌があまりに†中二病†すぎるみたいな話で盛り上がりました。


東京散策

前後の空き時間を使って東京を散策しました。

就活とかがあって割と東京行くにも何を見よう…とネタ切れを起こしていたのですが,結局時間は足りなかったです。


初日は木更津高専の人々と秋葉原に,その後一人で東京駅のジュンク堂書店に,更に銀座の10FACTORYに行ってきました。


10FACTORYの生搾りみかんジュースがこの日の何よりの目的だったのですが,間違いなく「飲むみかん」という感じでした。めちゃくちゃに美味しかったです。


2日目は開場までの時間でおすすめしてもらっていた銀座伊東屋に行ってきました。

完全に文具オタクの聖地という感じで,文具オタクなので嬉しかったです。

文具もあった上になんか旅行に良さそうなアイデアグッズとかも置いてあって,限界が捗りそうな店だなになりました。

全然回りきれなかったのでまた遊びに行きたいです。


3日目は集まっていた各地の高専生と一緒に秋葉原に行きました。

ジャンクショップでThinkPadが安かったのでとりあえず2枚買っておきました。


その後露店でIBMThinkPadを売ってあるのを見つけ,IBM ThinkPadが欲しい6人のオタクによるじゃんけん大会が行われました。


勝ちました✌


結局12人のオタク全員で6台のThinkPadを購入する店荒らしが行われました。限界。

3台のThinkPadを持って山口に帰るのはしんどかったです。


そんなこんなでオタクとの3日間が終わり,山口に帰城。

帰りの飛行機がめちゃくちゃにエモかったです。


これは東京で得た戦利品たち。


余談

東京から帰ると大量の誕生日プレゼントが届いていました。

こんな感じにして活用させていただきます。


今日も起きたらたくさんプレゼントが届いていました。


本当にありがたいと思います。自分のような人間に期待してこうやって応援してくださる人がたくさんいるので,期待されているうちはちゃんと生きて期待に応えないといけないなあとかを考えました。


ところでぼくの誕生日は1/10です。よろしくおねがいします。

http://amzn.asia/eXSkjq1