AIDEMOIRE

【アイデモワール】

脆弱性に関する実験:スタックオーバーフロー(その06)

スタック上のプログラムを実行する方法

前回、スタックオーバーフロー編は一旦終了、と書きましたが、ちょっと進展があったので追記します。

このシリーズの1回目で「スタックに書き込んだプログラムは実行できない」と書きました。でも、それを回避する方法がありました。

スタック オーバーフローを使って任意のロジックを実行するというパズルは解けたので、「では、皆さんはどうやっているのだろう?」とGoogleってみました。

まぁ、色んな人が書いています。その中にこんなのがありました。(詳しく書いてあるので大変参考になります。)

Buffer Overflows and You

Googleでヒットしたのが途中のページだったので「なんだ、これって私が最初に試した方法じゃん。これで実行できるのかな」と思っていたのですが、次のページでプログラムをコンパイルする時に“-z execstack”というオプションを付けてしました。
(最初から読みたい人は Buffer Overflows and You から )

早速、私も試してみました。まずは、第01回目に紹介したオプション無しの方法です。

ubuntu@ubuntu1304d64:~/Temp$ cc -o fake2 fake2.c -fno-stack-protector
ubuntu@ubuntu1304d64:~/Temp$ ./fake2
Segmentation fault (core dumped)
ubuntu@ubuntu1304d64:~/Temp$

NXビット(と思われる)機能のためにアクセス違反になります。では、オプションを付けてコンパイル、実行してみましょう。

ubuntu@ubuntu1304d64:~/Temp$ cc -o fake2 fake2.c -fno-stack-protector -z execstack
ubuntu@ubuntu1304d64:~/Temp$ ./fake2
$ ps
  PID TTY          TIME CMD
 3745 pts/2    00:00:00 bash
 4692 pts/2    00:00:00 sh
 4693 pts/2    00:00:00 ps
$ ps -f
UID        PID  PPID  C STIME TTY          TIME CMD
ubuntu    3745  3744  0 19:18 pts/2    00:00:00 -bash
ubuntu    4692  3745  0 21:07 pts/2    00:00:00 [sh]
ubuntu    4694  4692  0 21:07 pts/2    00:00:00 ps -f
$ env
PWD=/home/ubuntu/Temp
$ exit
ubuntu@ubuntu1304d64:~/Temp$

おぉ、動きますね。このオプションを付けてコンパイルしたプログラムであれば、スタックに書き込んだ命令で何でも実行できます!

調べみるとこのオプションはコンパイラではなくリンカに対するオプションでした。

しかし、普通、このオプションを付けてソフトウェアを作成することはないでしょうから「なんだかなぁ」という感じです。(それを言ってしまえば、-fno-stack-protector オプションの指定も禁じ手みなたいなものですが。)

とは言っても、そのソフトウェアが“-z execstack”付きでコンパイルされてのかどうか知りたくなるというのは人情です。さて、どうすれば。fileコマンドでもnmコマンドでもそれらしい違いは出て来ません。objdumpで逆アセンブルした結果も同じです。(ただし、ビルドIDは除きます。)オプション有り無しの二つのオブジェクトをダンプした結果、0x1ccバイト目(460バイト目)の第一ビットが1か0かだけの違いでした。

460バイト目というファイルのごくごく最初の部分ですから、コンパイルされたプログラム側ではなく、実行ファイルのファイルヘッダ情報として入っているようです。であれば、ELF関連のコマンドで情報がとれそうです。ということで、まずはreadelfを使ってみました。

オプション無しの fake2.n とオプション有りの fake2.y の情報を表示してみましょう。

ubuntu@ubuntu1304d64:~/Temp$ readelf -l fake2.n | grep -A 1 GNU_STACK
  GNU_STACK      0x0000000000000000 0x0000000000000000 0x0000000000000000
                 0x0000000000000000 0x0000000000000000  RW     8
ubuntu@ubuntu1304d64:~/Temp$ readelf -l fake2.y | grep -A 1 GNU_STACK
  GNU_STACK      0x0000000000000000 0x0000000000000000 0x0000000000000000
                 0x0000000000000000 0x0000000000000000  RWE    8
ubuntu@ubuntu1304d64:~/Temp$

どうも“GNU_STACK”に対して“E”フラグ立っているとスタック上のプログラムを実行できるようです。

更に調べるとLinuxのコマンドで GNU_STACK が正常な状態かどうか(つまりスタックが実行可能で無いかどうか)を調べるコマンドがありました。

ubuntu@ubuntu1304d64:~/Temp$ scanelf -qe .
RWX --- ---  ./fake2.y
ubuntu@ubuntu1304d64:~/Temp$


ん、ということは、このビットを強制的に書き換えると任意のバイナリでスタックでのプログラム実行ができてしまうのでしょうか? では実験してみましょう。fake2に関してはGNU_STACKのフラグの位置が分かっているので、それを使います。

ubuntu@ubuntu1304d64:~/Temp$ ./fake2.n
Segmentation fault (core dumped)
ubuntu@ubuntu1304d64:~/Temp$ sed -e "s/\(\x51\xe5\x74\x64\)\x06/\1\x07/" < fake2.n > fake2.m
ubuntu@ubuntu1304d64:~/Temp$ chmod +x fake2.m
ubuntu@ubuntu1304d64:~/Temp$ ./fake2.m
$ ps
  PID TTY          TIME CMD
 3745 pts/2    00:00:00 bash
26962 pts/2    00:00:00 sh
26963 pts/2    00:00:00 ps
$ exit
ubuntu@ubuntu1304d64:~/Temp$

おや、実行できてしまいました。セキュリティ観点からすると、ちょっと驚きです。結局、ELFファイルはSHA1などのシグネチャで改竄されていないことを確認していないようです。

勿論、システムの保護はもっと総合的に行いいますから、単に改竄されたELFが動いたからと言って騒ぐことの程ではありません。上の例ではsedを使って書き換えているので“改ざん”の様に見えますが、リンカなどのコマンドで“設定”することも有るわけですから。

なお、このビットの書き換えについては、クラッキングの対象となる可能性は低いと考えます。なぜなら、このビットを変更できるということは、システム上で他のコマンドを実行できる訳ですから。とは言え、定期的に scanelfなどで確認しておいた方が良いかも知れません。

脆弱性に関する実験:スタックオーバーフロー編(リンク)

脆弱性に関する実験:スタックオーバーフロー(その01) - AIDEMOIRE
スタックへのマシン語の書き込みとその実行に関する実験


脆弱性に関する実験:スタックオーバーフロー(その02) - AIDEMOIRE
戻りアドレスの書き換えによるロジックの変更に関する実験


脆弱性に関する実験:スタックオーバーフロー(その03) - AIDEMOIRE
プログラムによる自分自身のプログラムの変更に関する実験


脆弱性に関する実験:スタックオーバーフロー(その04) - AIDEMOIRE
スタックの書き換えによるシェルの起動


脆弱性に関する実験:スタックオーバーフロー(その05) - AIDEMOIRE
スタックオーバーフローの脆弱性に関する考察


脆弱性に関する実験:スタックオーバーフロー(その06) - AIDEMOIRE
スタック上のプログラムを実行する方法