argv0 leakとglibcの対策
32C3 CTFの readme を解いていてargv[0]
` leakというテクニックを知り手で動かしてみたのですが、うまく行きませんでした。
原因がわかったのでまとめておきます。
argv[0]
leakとは
katagaitai勉強会 #4の資料にくわしく載っています。
SSPを有効にしているとき、バッファオーバーフローでデータを大量に書き込みCanaryを書き換えると*** stack smashing detected ***: ./hogehoge terminated
と表示され終了します。
そのときの./hogehoge
という文字列を指すポインタはスタックに積まれているので、それを書き換えてあげれば./hogehoge
の部分を任意のアドレスのデータに置き換えてリークすることができます。
環境間の差異
しかし、先に書いたとおり自分のUbuntu 17.10ではCanaryを書き換えても... <unknown> terminated
としか表示されませんでした。
なぜだろうと思い別のCentOSにバイナリをコピーしてみると以下のように想定どおり表示されました。
$ ./test_ssp
*** stack smashing detected ***: ./test_ssp terminated
Segmentation fault (コアダンプ)
libcのバージョンを比べてみます。こちらがCentOS。glibc 2.17。
$ /lib64/libc.so.6
GNU C Library (GNU libc) stable release version 2.17, by Roland McGrath et al.
動作しなかったUbuntu。glibc 2.26。
$ /lib/x86_64-linux-gnu/libc.so.6
GNU C Library (Ubuntu GLIBC 2.26-0ubuntu2) stable release version 2.26, by Roland McGrath et al.
バージョンにより違いがあると考えて、とりあえずソースを落としてみました。 GNUからダウンロードできます。
以下が動作した環境のglibc 2.17のソースコード。
Canaryチェックにひっかかると__stack_chk_fail
を経由して__fortify_fail
が呼ばれます。
#include <stdio.h>
#include <stdlib.h>
extern char **__libc_argv attribute_hidden;
void
__attribute__ ((noreturn))
__fortify_fail (msg)
const char *msg;
{
/* The loop is added only to keep gcc happy. */
while (1)
__libc_message (2, "*** %s ***: %s terminated\n",
msg, __libc_argv[0] ?: "<unknown>");
}
libc_hidden_def (__fortify_fail)
そしてこれが動作しなかった環境のglibc 2.26のソースです。
__libc_argv[0] may point to the corrupted stack.
と明記され、バックトレースを有効にしない限り情報を表示せず<unknown>
とするようになっています。これが原因だったようです。
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
extern char **__libc_argv attribute_hidden;
void
__attribute__ ((noreturn)) internal_function
__fortify_fail_abort (_Bool need_backtrace, const char *msg)
{
/* The loop is added only to keep gcc happy. Don't pass down
__libc_argv[0] if we aren't doing backtrace since __libc_argv[0]
may point to the corrupted stack. */
while (1)
__libc_message (need_backtrace ? (do_abort | do_backtrace) : do_abort,
"*** %s ***: %s terminated\n",
msg,
(need_backtrace && __libc_argv[0] != NULL
? __libc_argv[0] : "<unknown>"));
}
void
__attribute__ ((noreturn)) internal_function
__fortify_fail (const char *msg)
{
__fortify_fail_abort (true, msg);
}
libc_hidden_def (__fortify_fail)
libc_hidden_def (__fortify_fail_abort)
思いもしないところから情報が漏れるんだなあ… まだまだLinux, pwnに対する知識が足りないと実感します。
Backlinks
There are no notes linking to this note.