I'm starting to tinker with buffer overflows, and wrote the following program:
#include <unistd.h>
void g() {
execve("/bin/sh", NULL, NULL);
}
void f() {
long *return_address;
char instructions[] = "\xb8\x01\x00\x00\x00\xcd\x80"; // exit(1)
return_address = (long*) (&return_address + 2);
*return_address = (long)&g; // or (long)instructions
}
int main() {
f();
}
It does what I expect it to do : return_address overwrite the return address of f with the address of g, which opens a shell. However, if I set the return address to instructions, I got a segmentation fault, and none of the instructions in instructions is executed.
I compile with GCC, using -fno-stack-protector.
How could I prevent this segmentation fault occurring ?
At least one problem isn't related to the buffer overflow.
execve("/bin/sh", NULL, NULL);
That first NULL becomes the argv of the process you're starting. argv must be an array of strings that is terminated with a NULL. So a segfault may happen when /bin/sh starts up, tries to read argv[0], and dereferences NULL.
void g(void) {
char *argv[] = { "/bin/sh", NULL };
execve(argv[0], argv, NULL);
}
You might also add -z execstack to the gcc command line, which will tell the linker to permit an executable stack. You should also verify that the instructions you have there are what exit(1) compiles to on your system if you got them from a tutorial somewhere.
Related
I am currently tackling on an assignment, where I need to upload exploit.c and target.c onto a ubuntu server, and successfully achieve a buffer overflow attack with exploit onto target. I was provided a shellcode. Now, target.c is not to be altered, just exploit.c. I had to use GDB on exploit.c to force an external breakpoint on foo() from target.c, to figure out the return addresses using info frame.
I was provided with the working shellcode, and minimal instructions.
I am pretty sure I was able to successfully pull the return addresses, but my issue is that I cannot figure out what code to put into exploit.c to have it successfully perform a buffer overflow attack. I was also instructed that one of the return addresses must be input into the exploit code for it to function properly.
I understand that the exploit is trying to call back to the return address, to then push itself into the buffer, so I can obtain access to the shell.
Here is exploit.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "shellcode.h"
// replace this define environment to have the correct path of your own target code
#define TARGET "/*******************"
int main(void)
{
char *args[3];
char *env[2];
char *tmp = NULL;
// Creating an input buffer that can cause buffer overflow in strcpy function in the target.c executable code
int buffSize = 1000;
char buff[buffSize];
// Intialize buffer elements to 0x01
int i;
for (i=0; i < buffSize; i++) buff[i] = 0x01;
// write your code below to fill the 22 bytes shellcode into the buff variable, and
// at the correct location overwrite the return address correctly in order to achieve stack overflow
// Your own code starts here:
strcpy (buff[buffSize-22], shellcode);
// Your code ends here.
// prepare command line input to execute target code
args[0] = TARGET; // you must have already compiled and generated the target executable code first
args[1] = buff; // the first input parameter to the target code (artfully crafted buffer overflow string)
args[2] = NULL;
env[0] = "FOO=bar";
env[1] = NULL;
if (0 > execve(TARGET, args, env))
fprintf(stderr, "execve failed.\n");
return 0;
}
Here is the target.c code
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int foo(char* arg)
{
char localBuf[240];
short len = 240;
float var1=2.4;
int *ptr = NULL;
strcpy(localBuf, arg);
printf("foo() finishes normally.\n");
return 0;
}
int kbhit(void)
{
struct timeval tv;
fd_set read_fd;
tv.tv_sec=0; tv.tv_usec=0;
FD_ZERO(&read_fd); FD_SET(0,&read_fd);
if(select(1, &read_fd, NULL, NULL, &tv) == -1)
return 0;
if(FD_ISSET(0,&read_fd))
return 1;
return 0;
}
int main(int argc, char *argv[])
{
if (argc != 2)
{
fprintf(stderr, "target: argc != 2\n");
exit(EXIT_FAILURE);
}
printf("Press any key to call foo function...\n");
while(!kbhit())
;
foo(argv[1]);
return 0;
}
I compiled both target and exploit. Then I ran GDB on exploit, and formed a breakpoint using "break target.c:10". Using Info Frame I was able to obtain the return addresses.
I used strcpy, because it is essentially the only line of code we were taught for this section involving overflow attacks, even though it clearly states in the document "Fill the shell executable code (in the string array shellcode[]) byte-by-
byte into the buff for your modified return address to execute, do not
use strcpy() because shellcode[] is not an ASCII string (and not
copying NULL byte, too)."
Exploit compiles fine, and it runs fine, but it does not give me access to a shell. I was instructed that I would know if it worked, if I was presented with two dollar signs ($$) instead of one ($).
I am a network engineer, and I am not entirely savvy with C, or attacking vulnerabilities in programs, any help would be appreciated. The entire lesson revolves around "stack overflow", but this assignment is called "buffer overflow attack".
With the same command in my coworker's PC, my program works without the problem.
But in my PC, the program crashes with segfault;
GDB backtrace at core reads as follows:
#0 strrchr () at ../sysdeps/x86_64/strrchr.S:32
32 ../sysdeps/x86_64/strrchr.S: no such file or directory
(gdb) bt
#0 strrchr () at ../sysdeps/x86_64/strrchr.S:32
#1 0x00007f10961236d7 in dirname (path=0x324a47a0 <error: Cannot access memory at address 0x324a47a0>) at dirname.c:31
I'm already compiling the executable with -g -ggdb options.
Odd thing is that.. with valgrind the program works without error in my PC as well.
How can I solve the problem? I've observed that the errors occur only with strrchr, strcmp, strlen, ... string.h functions.
+Edit: the gdb backtrace indicates that the program crashes here:
char* base_dir = dirname(get_abs_name(test_dir));
where get_abs_name is defined as
char* get_abs_name(char* dir) {
char abs_path[PATH_MAX];
char* c = malloc(PATH_MAX*sizeof(char));
realpath(dir, abs_path);
strcpy(c, abs_path);
return c;
}
+Edit2: 'dir' is a path of certain file, like '../program/blabla.jpg'.
Using valgrind,
printf("%s\n", dir)
normally prints '/home/frozenca/path_to_program'.
I can't guess why the program crashes without valgrind..
We cannot know for sure without a Minimal, Complete, and Verifiable example. Your code looks mostly correct (albeit convoluted), except you do not check for errors.
char* get_abs_name(char* dir) {
char abs_path[PATH_MAX];
char* c = malloc(PATH_MAX*sizeof(char)); /* this may return NULL */
realpath(dir, abs_path); /* this may return NULL */
strcpy(c, abs_path);
return c;
}
Now, how could this lead to an error like you see? Well, if malloc returns NULL, you'll get a crash right away in strcpy. But if realpath fails:
The content of abs_path remains undefined.
So strcpy(c, abs_path) will copy undefined content. Which could lead to it copying just one byte if abs_path[0] happens to be \0. But could also lead to massive heap corruption. Which happens depends on unrelated conditions, such as how the program is compiled, and whether some debugging tool such as valgrind is attached.
TL;DR: get into the habit of checking every function that may fail.
char* get_abs_name(char* dir) {
char abs_path[PATH_MAX];
char* c = malloc(PATH_MAX*sizeof(char));
if (!c) { return NULL; }
if (!realpath(dir, abs_path)) {
free(c);
return NULL;
}
strcpy(c, abs_path);
return c;
}
Or, here, you can simplify it alot assuming a GNU system or POSIX.1-2008 system:
char * get_abs_name(const char * dir) {
return realpath(dir, NULL);
}
Note however that either way, in your main program, you also must check that get_abs_name() did not return NULL, otherwise dirname() will crash.
Drop your function entirely and use the return value of realpath(dir, NULL) instead.
Convert type
char* c = malloc(PATH_MAX*sizeof(char));
Thanks!
Firstly, I'm using this compilation flags:
gcc -fno-stack-protector -z execstack -m 32
Ok lets look, on code below
int main(int argc, char *argv[]){
char pass[8];
char logged = 'n';
strcpy( pass, argv[1] );
if( logged == 'y' ){
printf("Hello \n");
} else {
printf("Run hacker :(\n");
}
return 0;
}
And second code
int main(int argc, char *argv[]){
char logged = 'n';
char pass[8];
strcpy( pass, argv[1] );
if( logged == 'y' ){
printf("Hello \n");
} else {
printf("Run hacker :(\n");
}
return 0;
}
These codes are vulnerable for stack overflow attack. (passing 'yyyyyyyyy' as arg would pass the test)
But why? Order of local variables in second code is different than in first code. So queue of pushing local variables on stack should be other too, but it is not?
First a stack overflow vulnerability is not exactly the same thing as stack corruption.
This code is writing an automatic variable that is allocated on the stack (in your frame). The compiler reserved 8 bytes on the stack for the pass string. If you write more than 8 bytes, you will be corrupting whatever else is on the stack right next to pass[] array. Doesn't really matter what it is; it will be corrupted, and that is what stack corruption means.
With some skill, one can generate an input (argv[1]) string (really an instruction byte stream) that will align with the return instruction from the main and thus can force execution of your code. That is what vulnerability is.
You seem to understand that the order of variables on your stack just determines what and how may be corrupted, and you should see exactly what you expected if you turn off the stack protection by using -fno-stack-protector, plus minus the exact stack layout imposed by your compiler. With the protection on, both cases should dump the stack smashing backtrace and diagnostics.
Note that different linux distrubution set the no-stack-protector default to different values, but as long as you explicitly control it on the command line you are all set.
The -z execstack option is a mechanism dealing with whether or not to allow executing on the stack; here it is irrelevant as your code is just corrupting the stack.
I was working with example from K&R, its a cat utility to view files
#include <stdio.h>
main(int argc,char **argv){
FILE *fp;
void filecopy(FILE *,FILE *);
if(argc==1)
filecopy(stdin,stdout);
else // accidentally mistyped
while(--argv > 0) // should have been --argc > 0
if((fp=fopen(*++argv,"r"))==NULL){
printf("cat: can't open %s\n",*argv);
return 1;
}else{
filecopy(fp,stdout);
fclose(fp);
}
return 0;
}
void filecopy(FILE *ifp,FILE *ofp)
{
int c;
while((c=getc(ifp))!=EOF)
putc(c,ofp);
}
When compiled with gcc cat.c,
and when I ran ./a.out cat.c from the terminal,all I got was some chinnesse symbols and some readable text(names like _fini_array_,_GLOBAL_OFFSET_TABLE_ and etc..) and the garbage just went on until I pressed Ctrl+C, I wanted to ask why I didn't got Segmentation fault, because didn't the program was reading every memory location from argv start address? and I shouldn't have the rights to do so?
Let's look at these two consecutive lines:
while(--argv > 0)
if((fp=fopen(*++argv,"r"))==NULL){
Every time you decrement argv, you end up incrementing it on the next line. So overall, you are just decrementing and incrementing argv a lot but you are never actually reading past the bounds of the argv memory area.
Even if you were reading past the bounds of the argv memory area, that would be undefined behavior and you are not guaranteed to get a segmentation fault. The result you get depends on your compiler your, operating system, and the other things in your program.
I suspect that executing --argv also gives you undefined behavior, because after that line is executed, the pointer would probably point outside of the array allocated for argv data. But, since you didn't dereference argv while it was pointing there, it turned out to be OK.
I have installed the linux distro named DVL (damn vulnerable linux), and I'm exercising with buffer overflow exploits.
I wrote two virtually identical programs which are vulnerable to bof:
//bof_n.c
#include <stdio.h>
void bof() {
printf("BOF");
}
void foo(char* argv) {
char buf[10];
strcpy(buf, argv);
prinf("foo");
}
int main(int argc, char* argv[]) {
if (argc >= 1) {
foo(argv[1]);
}
return 0;
}
and
//bof.c
#include <stdio.h>
void bof() {
printf("BOF!\n");//this is the only change
}
void foo(char* argv) {
char buf[10];
strcpy(buf, argv);
prinf("foo");
}
int main(int argc, char* argv[]) {
if (argc >= 1) {
foo(argv[1]);
}
return 0;
}
After that I compiled both of them, and I obtained the bof() function address in both cases (e.g., objdump -d bof.o | grep bof). Let's name such an address ADDR which is on 4 byte.
I also found that if I write 32 byte in the buf variable, the EIP register is completely overwritten (I cannot copy here the output of gdb since it is on a virtual machine).
Now, if I do:
./bof `perl -e 'print "\x90"x28 . "ADDR"'`
I get:
fooBOF!
Segmentation fault
Instead if I try the same approach but using bof_n, I only get the "Segmentation fault" message.
Therefore I tried to increment the number of time ADDR value is repeated, and I found that if it is being repeated for at least 350 times, I get the wanted result. But instead of having the output above exactly, I get a long list of "BOF" messages one after the other. I tried to obtain just one "BOF" message, but apparently I cannot do that (I got or zero, or a long list of them).
Why this is happening? Any idea?
I'm using DVL with gcc 3.4.6
What's your goal?
You should really be using a debugger for this, try the GDB Debugger or gdb. With it you can see the memory/registers/stack and disassembly of whats currently going on in the system.
I'd guess that in the first function, the string being only 3 characters in length, gets optimized to \x42\x4f\x46\x00, so the disassembly may be slightly different.
The C source is pretty much irrelevant, you'll need to either disassemble or fuzz both binaries to find appropriate size for both NOP sleds.
I found out the solution. The issue was about the printing of the message and not the buffer overflow exploit itself.
In fact the register eip was being correctly overwritten also in the bof_n example, and the program flow was being correctly redirected in the bof() function. The problem was that, apparently, the stdout were not flushed out before the Segmentation fault and hence no message was being shown.
Instead, using fprintf(stderr, "BOF");, I finally get the "BOF" message.