CTFを説明するのは、のどかは
どうも、誰やねんと思われるでしょう
そんなん知ったこっちゃねえ、のどかはです
CTFについて説明します
説明?そんなもん出来ないな、取り合えずこのよくわかんない場所で垂れ流し投稿します
これを連載と呼ぶのかはわからないし、いつまで続けるのかもわからないけども始めていきます
CTFってなに
CTFってなんなんですかね(諦め
はい、次
pwnってジャンル
pwnってジャンルがあります
いくら初心者に説明しても理解させることができず、そのぬかるみに嵌った一部の人間が深淵に踏み込み、上昇負荷を恐れて下層に住み着いてしまった者たちしかいないとの噂ですが真相はわかりません
とにかく、人間でないことは確かなので、ここで説明することで人間をやめる方法を説明するのは気が引けるのでここまでとしておきます
…嘘です、この記事の本題です
人間をやめよう
人間をやめるのは簡単なので、さっさとやめましょう
必要なものはWSLでもなんでもいいんでGNU/Linuxな環境、あればうれしい中二病引き付けなバイナリ大好き発表ドラゴンのGHIDRA、Windowsなんて窓は窓から投げ捨ててもいいんですが、それだと事故言及のパラドックスになるんで、おとなしく窓の上でぬくぬく独占企業契約を結んでいきましょう
丁寧に説明する気はないです
CTFとか自分ちょっとハマってないんで
ついてこれる人が来てください
さて、おもむろにWSLを立ち上げたらCのコードを書き始めましょう
#include <stdio.h>
int main(void){
printf("Hello World");
return 0;
}
はい、ハローワールド
では次に入力を書きましょう
折角なので名前を出したいですね、出してみましょう
#include <stdio.h>
int main(void){
char name[]="secret_name";
printf("Hello World, %s", name);
return 0;
}
さて、出来ましたか?
もちろんコピペなんてだめですよ?この程度
ちなみに今記事書いてる自分は適当に書いていて間違ってないかとひやひやしてます(あ、いえコピペ防止ですよ。狙ってるんです
それでは、このプログラムはなにをしてますか?
…?
え?
わからないんですか?
ええ、そうです。Hello World, secret_nameって出力してます。
いえいえ、そうじゃなくて
これはどのアドレスから呼ばれて挨拶できてるかわからないんですか?
…はい
pwnにおいて重要なのはこの概念です
どんなシステムコールを呼ぶためにスタックにどんなアドレスが積まれて、それがどういう経路によって移動して最終的になぜ書式文字列が適切にヒープに置かれているであろうデータを処理出来たんですか?
ということです
ここにおいて、書き換え可能であるかどうかも考えなければなりません
当たり前ですが、読者と投稿者では必要な権限が別なのですから、シークレットネームを”知る”必要があるのか、”記す”必要があるのかは別になります。
そして、隠れてなきゃいけないんだから乱数という森に隠しておきます。
そうはいっても高々2の何十累乗なので解かれてしまうことだってあるかもしれません。
信頼できないんです
pwnとはこのようなあらゆるバイナリの配慮や適切な処理が、親切に舗装されてることを確認していったなかで、ぶち壊せる経路を探すジャンルです。
ここで先ほどのコードに脆弱性はあるでしょうか?
まあ、ないっちゃないんですけど
ここから変えていくと生まれてしまうことがあるわけです
シークレットネームっていうのは誰かが記したいものだとしましょう
しかも、とても変なプログラマーがその要件を請け負ったようなので、こんな風になりました
#include <stdio.h>
int main(void){
char *s=(char*)malloc(100);
scanf("%s", s);
printf(s);
return 0;
}
さてこのプログラムを実行して以下の入力をしてみましょう
%x%x%x%x
おそらく意味わからん数列が出てきたと思います
これではいけませんね?
Cくんっていっつもそう
さて何故こんなことが起きたでしょう
理由は我々が便利に使ってるCくんのせいです
Cくんはお節介焼きなので、書式文字列という大変便利な機能を持っています
書式文字列は大変便利なので文字列に関する操作のはずなのに、実装するために沢山の高度な技術によってメモリを扱ってくれます
我々は悪いユーザーなので、それをなんやかんやします
これを書式文字列という便利機能を用いた攻撃書式文字列攻撃(Format String Attack)と呼びます
名前なんてどうでもいいです。悪用できるということが分かれば。(ただ検索するうえで便利なのでこういうのは忘れないほうがいいです)
さてこのプログラムは大変おバカなので、そもそも入力された文字列は開発者が扱ってるような書式文字列として認められる、特別な文字列だよーと渡されています
プログラムくんは素直なので、これは書式文字列を適用してお節介を焼かなきゃいけないんだ!となり、%xという文字列が渡されたときに、素直に16進数に変換して処理を続行してくれました。
でも、おかしいですね。普通にCの感覚で考えれば、変数をprintfくんの引数に渡してあげないのにどうして値なんかが飛び出てきたんでしょう。
これはそもそもprintfの引数を評価するものだという認識で考えると分からなくなります。あくまでprintfというのはCをCのコードとして人間の直観を伝える関数を用いて記述した時の特別な便利機能です。()に囲まれた部分を出力するのが%xだ、%sだなんていうのは一言もパソコンくんには理解してもらえません。
コンパイルすれば全てはある箱の上で値を行ったり来たりさせるだけの単純なチューリングマシンにしか成り果てません。
そしてノイマン型だとか言われるものだということは、すべてのプログラムは、プログラムで操れるわけです。(話が壮大ですね
アクセスする方法を知ってるのがプログラムなら、アクセスされるもデータもまたプログラム(プログラムの一部)なわけです
ということは何かというと、アクセスするプログラムがデータを参照するときに隣を見てきてという命令を誰かに意図的に繰り返されてしまったら(データを2つ並べられてしまうでも、条件分岐によってジャンプしてしまうでも)素直に、開発者の意図していない”隣”を見てきしまう訳です。
そして、なんとプログラムにおける関数の戻る場所、つまりプログラムのあるべき場所も帰る場所も次の行く先も隣においてあるのです。
ということで先ほどのプログラムをうまく実行して、本来はあり得ない動作を引き起こしてみましょう
と思いましたが、めっちゃ文章が長くなってることに気付いたのでここまでとします。
唐突な終わり
ポエムみたいな文章を書いて終わるという可読性のかけらもないブログでしたが、また次回もお会いしましょうということで
それでは
pwnを語ったのはのどかはでした
コメント