Unsorted Bin Attack
Reading time: 12 minutes
tip
AWS हैकिंग सीखें और अभ्यास करें:HackTricks Training AWS Red Team Expert (ARTE)
GCP हैकिंग सीखें और अभ्यास करें: HackTricks Training GCP Red Team Expert (GRTE)
Azure हैकिंग सीखें और अभ्यास करें:
HackTricks Training Azure Red Team Expert (AzRTE)
HackTricks का समर्थन करें
- सदस्यता योजनाओं की जांच करें!
- हमारे 💬 Discord समूह या टेलीग्राम समूह में शामिल हों या हमें Twitter 🐦 @hacktricks_live** पर फॉलो करें।**
- हैकिंग ट्रिक्स साझा करें और HackTricks और HackTricks Cloud गिटहब रिपोजिटरी में PRs सबमिट करें।
बुनियादी जानकारी
For more information about what is an unsorted bin check this page:
Unsorted lists are able to write the address to unsorted_chunks (av)
in the bk
address of the chunk. Therefore, if an attacker can modify the address of the bk
pointer in a chunk inside the unsorted bin, he could be able to write that address in an arbitrary address which could be helpful to leak a Glibc addresses or bypass some defense.
So, basically, this attack allows to set a big number at an arbitrary address. This big number is an address, which could be a heap address or a Glibc address. A traditional target was global_max_fast
to allow to create fast bin bins with bigger sizes (and pass from an unsorted bin attack to a fast bin attack).
- Modern note (glibc ≥ 2.39):
global_max_fast
became an 8‑bit global. Blindly writing a pointer there via an unsorted-bin write will clobber adjacent libc data and will not reliably raise the fastbin limit anymore. Prefer other targets or other primitives when running against glibc 2.39+. See "Modern constraints" below and consider combining with other techniques like a large bin attack or a fast bin attack once you have a stable primitive.
tip
T> aking a look to the example provided in https://ctf-wiki.mahaloz.re/pwn/linux/glibc-heap/unsorted_bin_attack/#principle and using 0x4000 and 0x5000 instead of 0x400 and 0x500 as chunk sizes (to avoid Tcache) it's possible to see that nowadays the error malloc(): unsorted double linked list corrupted
is triggered.
Therefore, this unsorted bin attack now (among other checks) also requires to be able to fix the doubled linked list so this is bypassed victim->bk->fd == victim
or not victim->fd == av (arena)
, which means that the address where we want to write must have the address of the fake chunk in its fd
position and that the fake chunk fd
is pointing to the arena.
caution
Note that this attack corrupts the unsorted bin (hence small and large too). So we can only use allocations from the fast bin now (a more complex program might do other allocations and crash), and to trigger this we must allocate the same size or the program will crash.
Note that overwriting global_max_fast
might help in this case trusting that the fast bin will be able to take care of all the other allocations until the exploit is completed.
The code from guyinatuxedo explains it very well, although if you modify the mallocs to allocate memory big enough so don't end in a Tcache you can see that the previously mentioned error appears preventing this technique: malloc(): unsorted double linked list corrupted
How the write actually happens
- The unsorted-bin write is triggered on
free
when the freed chunk is inserted at the head of the unsorted list. - During insertion, the allocator performs
bck = unsorted_chunks(av); fwd = bck->fd; victim->bk = bck; victim->fd = fwd; fwd->bk = victim; bck->fd = victim;
- If you can set
victim->bk
to(mchunkptr)(TARGET - 0x10)
before callingfree(victim)
, the final statement will perform the write:*(TARGET) = victim
. - Later, when the allocator processes the unsorted bin, integrity checks will verify (among other things) that
bck->fd == victim
andvictim->fd == unsorted_chunks(av)
before unlinking. Because the insertion already wrotevictim
intobck->fd
(ourTARGET
), these checks can be satisfied if the write succeeded.
आधुनिक सीमाएँ (glibc ≥ 2.33)
To use unsorted‑bin writes reliably on current glibc:
- Tcache interference: for sizes that fall into tcache, frees are diverted there and won’t touch the unsorted bin. Either
- make requests with sizes > MAX_TCACHE_SIZE (≥ 0x410 on 64‑bit by default), or
- fill the corresponding tcache bin (7 entries) so that additional frees reach the global bins, or
- if the environment is controllable, disable tcache (e.g., GLIBC_TUNABLES glibc.malloc.tcache_count=0).
- Integrity checks on the unsorted list: on the next allocation path that examines the unsorted bin, glibc checks (simplified):
bck->fd == victim
andvictim->fd == unsorted_chunks(av)
; otherwise it aborts withmalloc(): unsorted double linked list corrupted
.- This means the address you target must tolerate two writes: first
*(TARGET) = victim
at free‑time; later, as the chunk is removed,*(TARGET) = unsorted_chunks(av)
(the allocator rewritesbck->fd
back to the bin head). Choose targets where simply forcing a large non‑zero value is useful. - Typical stable targets in modern exploits
- Application or global state that treats "large" values as flags/limits.
- Indirect primitives (e.g., set up for a subsequent [fast bin attack](Fast Bin Attack) or to pivot a later write‐what‐where).
- Avoid
__malloc_hook
/__free_hook
on new glibc: they were removed in 2.34. Avoidglobal_max_fast
on ≥ 2.39 (see next note). - About
global_max_fast
on recent glibc - On glibc 2.39+,
global_max_fast
is an 8‑bit global. The classic trick of writing a heap pointer into it (to enlarge fastbins) no longer works cleanly and is likely to corrupt adjacent allocator state. Prefer other strategies.
Minimal exploitation recipe (modern glibc)
Goal: achieve a single arbitrary write of a heap pointer to an arbitrary address using the unsorted‑bin insertion primitive, without crashing.
- Layout/grooming
- Allocate A, B, C with sizes large enough to bypass tcache (e.g., 0x5000). C prevents consolidation with the top chunk.
- Corruption
- Overflow from A into B’s chunk header to set
B->bk = (mchunkptr)(TARGET - 0x10)
. - Trigger
free(B)
. At insertion time the allocator executesbck->fd = B
, therefore*(TARGET) = B
.- Continuation
- If you plan to continue allocating and the program uses the unsorted bin, expect the allocator to later set
*(TARGET) = unsorted_chunks(av)
. Both values are typically large and may be enough to change size/limit semantics in targets that only check for "big".
Pseudocode skeleton:
// 64-bit glibc 2.35–2.38 style layout (tcache bypass via large sizes)
void *A = malloc(0x5000);
void *B = malloc(0x5000);
void *C = malloc(0x5000); // guard
// overflow from A into B’s metadata (prev_size/size/.../bk). You must control B->bk.
*(size_t *)((char*)B - 0x8) = (size_t)(TARGET - 0x10); // write fake bk
free(B); // triggers *(TARGET) = B (unsorted-bin insertion write)
note
• अगर आप size से tcache को bypass नहीं कर पा रहे हैं, तो corrupted chunk को free करने से पहले चुने हुए size की tcache bin को भर दें (7 frees) ताकि free unsorted में जाए।
• अगर program अगली allocation पर तुरंत unsorted-bin checks के कारण abort कर देता है, तो फिर जाँच करें कि victim->fd
अभी भी bin head के बराबर है और आपका TARGET
पहले write के बाद exact victim
pointer रखता है।
Unsorted Bin Infoleak Attack
यह वास्तव में एक बहुत बुनियादी अवधारणा है। unsorted bin में मौजूद chunks में pointers होते हैं। unsorted bin का पहला chunk वास्तव में fd
और bk
लिंक रखेगा जो main arena (Glibc) के किसी हिस्से की ओर इशारा करते हैं।
इसलिए, अगर आप कोई chunk unsorted bin में डालकर उसे पढ़ सकते हैं (use after free) या कम से कम 1 pointer को overwrite किए बिना उसे फिर से allocate करके पढ़ सकें, तो आपको Glibc info leak मिल सकता है।
A similar attack used in this writeup, था 4 chunks की संरचना (A, B, C और D - D केवल top chunk के साथ consolidation को रोकने के लिए) का दुरुपयोग, जहां B में एक null byte overflow का उपयोग किया गया ताकि C यह दर्शाए कि B unused था। साथ ही, B में prev_size
डेटा को modify किया गया ताकि size B के size के बजाय A+B दिखे।
फिर C को deallocated किया गया और A+B के साथ consolidated किया गया (लेकिन B अभी भी use में था)। A के size का एक नया chunk allocate किया गया और फिर libc leaked addresses को B में लिखा गया, जहाँ से वे leak हुए थे।
References & Other examples
- https://ctf-wiki.mahaloz.re/pwn/linux/glibc-heap/unsorted_bin_attack/#hitcon-training-lab14-magic-heap
- लक्ष्य एक global variable को 4869 से बड़े मान से overwrite करना है ताकि flag प्राप्त करना संभव हो और PIE enabled नहीं हो।
- arbitrary sizes के chunks generate करना संभव है और desired size के साथ एक heap overflow मौजूद है।
- हमला 3 chunks बनाकर शुरू होता है: chunk0 overflow को abuse करने के लिए, chunk1 जिसे overflow किया जाएगा और chunk2 ताकि top chunk पिछले ones को consolidate न करे।
- फिर chunk1 को freed किया जाता है और chunk0 को overflow किया जाता है ताकि chunk1 का
bk
pointer इस तरफ इशारा करे:bk = magic - 0x10
- फिर chunk3 को chunk1 के समान size से allocate किया जाता है, जो unsorted bin attack को trigger करेगा और global variable का मान modify करेगा, जिससे flag मिलना संभव हो जाएगा।
- https://guyinatuxedo.github.io/31-unsortedbin_attack/0ctf16_zerostorage/index.html
- merge function vulnerable है क्योंकि अगर दोनों पास किए गए indexes एक ही हैं तो यह उस पर realloc करेगा और फिर उसे free करेगा पर freed region का pointer वापस करेगा जिसे reuse किया जा सकता है।
- इसलिए, 2 chunks बनाए जाते हैं: chunk0 जिसे खुद के साथ merge किया जाएगा और chunk1 ताकि top chunk के साथ consolidate न हो। फिर, merge function को chunk0 के साथ दो बार कॉल किया जाता है जिससे use after free होगा।
- फिर,
view
function को index 2 के साथ बुलाया जाता है (जो use after free chunk का index है), जो एक libc address leak करेगा। - चूँकि binary में protections हैं ताकि केवल
global_max_fast
से बड़े sizes ही malloc हों और इसलिए कोई fastbin use न हो, तो unsorted bin attack का उपयोग करके global variableglobal_max_fast
को overwrite किया जाएगा। - फिर, edit function को index 2 (use after free pointer) के साथ कॉल करना संभव है और
bk
pointer कोp64(global_max_fast-0x10)
की ओर overwrite किया जा सकता है। फिर, नया chunk बनाना previously compromised free address (0x20) का उपयोग करेगा और यह unsorted bin attack को trigger करेगा,global_max_fast
को एक बहुत बड़े मान से overwrite करते हुए, जिससे अब fast bins में chunks बनाना संभव हो जाएगा। - अब एक fast bin attack किया जाता है:
- सबसे पहले यह पाया गया कि fast chunks of size 200 को
__free_hook
स्थान में काम में लाया जा सकता है: gef➤ p &__free_hook
$1 = (void (**)(void *, const void *)) 0x7ff1e9e607a8 <__free_hook> gef➤ x/60gx 0x7ff1e9e607a8 - 0x59 0x7ff1e9e6074f: 0x0000000000000000 0x0000000000000200 0x7ff1e9e6075f: 0x0000000000000000 0x0000000000000000 0x7ff1e9e6076f <list_all_lock+15>: 0x0000000000000000 0x0000000000000000 0x7ff1e9e6077f <_IO_stdfile_2_lock+15>: 0x0000000000000000 0x0000000000000000
- अगर हम इस स्थान पर size 0x200 का एक fast chunk पा लें, तो किसी function pointer को overwrite करना संभव होगा जो execute होगा।
- इसके लिए, एक नया chunk size
0xfc
का बनाया गया और merged function को उस pointer के साथ दो बार कॉल किया गया, इस तरह हमें fast bin में size0xfc*2 = 0x1f8
का freed chunk का pointer मिला। - फिर, edit function को इस chunk पर बुलाया गया ताकि fast bin के इस chunk का
fd
address previous__free_hook
function की तरफ point करे। - फिर, size
0x1f8
का एक chunk बनाया जाता है ताकि fast bin से previous useless chunk मिल सके और फिर एक और size0x1f8
का chunk बनाकर fast bin में__free_hook
स्थान पर chunk प्राप्त किया जाता है और वहांsystem
function के address से overwrite कर दिया जाता है। - और अंत में
/bin/sh\x00
string वाला एक chunk बनाया जाता है और delete function द्वारा free किया जाता है, जिससे__free_hook
function trigger होता है जो अब system की ओर इशारा करता है और/bin/sh\x00
उसे parameter के रूप में मिलता है। - CTF https://guyinatuxedo.github.io/33-custom_misc_heap/csaw19_traveller/index.html
- 1B overflow का दुरुपयोग करके chunks को unsorted bin में consolidate करके libc infoleak प्राप्त करने और फिर malloc hook को एक one gadget address से overwrite करने के लिए fast bin attack करने का एक और उदाहरण।
- Robot Factory. BlackHat MEA CTF 2022
- हम केवल
0x100
से बड़े sizes के chunks ही allocate कर सकते हैं। - Unsorted Bin attack का उपयोग करके
global_max_fast
को overwrite करें (ASLR के कारण यह 1/16 बार काम करता है, क्योंकि हमें 12 bits बदलने की ज़रूरत है, पर वास्तव में 16 bits बदलने होंगे)। - Fast Bin attack करके एक global array of chunks को modify किया जाता है। इससे एक arbitrary read/write primitive मिलता है, जो GOT को modify करने और किसी function को
system
की ओर निर्देशित करने की सुविधा देता है।
References
- Glibc malloc unsorted-bin integrity checks (example in 2.33 source): https://elixir.bootlin.com/glibc/glibc-2.33/source/malloc/malloc.c
global_max_fast
and related definitions in modern glibc (2.39): https://elixir.bootlin.com/glibc/glibc-2.39/source/malloc/malloc.c
tip
AWS हैकिंग सीखें और अभ्यास करें:HackTricks Training AWS Red Team Expert (ARTE)
GCP हैकिंग सीखें और अभ्यास करें: HackTricks Training GCP Red Team Expert (GRTE)
Azure हैकिंग सीखें और अभ्यास करें:
HackTricks Training Azure Red Team Expert (AzRTE)
HackTricks का समर्थन करें
- सदस्यता योजनाओं की जांच करें!
- हमारे 💬 Discord समूह या टेलीग्राम समूह में शामिल हों या हमें Twitter 🐦 @hacktricks_live** पर फॉलो करें।**
- हैकिंग ट्रिक्स साझा करें और HackTricks और HackTricks Cloud गिटहब रिपोजिटरी में PRs सबमिट करें।