Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Format string exploit ends in segfault

I'm reading now the "Hacking - The Art of Exploitation" book.

This is my simplified version of a code for exploiting format-strings.

/* fmt_vuln.c */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main (int argc, char *argv[]){
  char text [1024];

  if (argc < 2){
    printf ("Usage: %s <text to print>\n", argv[0]);
    exit (0);
  }
  strcpy (text, argv[1]);
  printf ("The wrong way to print user-controlled input:\n");
  printf (text);
  printf ("\n");
  return 0;
}

I run this command:

./fmt_vuln AAAA%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x

I got:

The wrong way to print user-controlled input:
AAAA59055000.58e347a0.58b68620.ffffffff.00000000.fba56ac8.58a9fc58.41414141

So, I saw that the 8th format parameter is reading from the beginning of the format string.

Then, When I run the command:

./getenv PATH ./fmt_vuln

I got the address:

0x7ffe2a673d84

So I tried to run: (in order to print the PATH variable)

./fmt_vuln $(printf "\x84\x3d\x67\x2a\xfe\x7f")%08x.%08x.%08x.%08x.%08x.%08x.%08x.%s

I got:

The wrong way to print user-controlled input:
Segmentation fault

Why I got the segfault? from the getenv program I got the address of PATH, but the program still crashes...

Appreciate the help.

like image 307
mik mik Avatar asked Oct 28 '15 07:10

mik mik


1 Answers

Not all processes in a computer share the same memory space for security reasons. What do I mean when I talk about different memory spaces? Consider the following 2 programs:

//program 1
int main(int argc, char** argv){
    printf("%02x", *((uint8_t*)0xf00fba11));
    return 0;
}

//program 2
int main(int argc, char** argv){
    printf("%02x", *((uint8_t*)0xf00fba11));
    return 0;
}

If these programs were to be run simultaneously (and assuming they don't segfault (which they almost certainly would)), they would print different values. How can it be?? They both access memory location 0xf00fba11!... or do they?

In order to understand what is happening here, we first need to understand what is happening when the cpu loads a value from memory. To load a value from memory, the cpu sends a request to RAM, like this:

 cpu
|-------------|                                           |---------|
| read        |-------address out to RAM (0xf00fba11)---->|  RAM    |
|             |                                           |         |
| *0xf00fba11 |<---------data coming back to CPU----------|         |
|-------------|                                           |---------|

There is a special piece of hardware between the cpu and the ram that translates addresses from "virtual addresses" to "physical addresses", it is called the Memory Management Unit (MMU for short). If a program asks for the value at address 0x1000, the MMU might "remap" 0x1000 to 0x8000. If the address 0x1000 is always replaced with 0x8000 before it reaches the RAM for all reads and writes, this might seem like a pointless operation. The program still operates the exact same way... so what's the big deal?

The big deal is that now programs 1 and 2 cannot access each other's data. The MMU can be configured so that there does NOT exist an address that program 1 can read from which contains one of program 2's variables. This "mapping" is unique for each process (mostly) and is configured by the operating system.

Here is an example of how an MMU might affect our toy "f00fba11" example.

Process 1
 cpu
|-------------|                                           |---------|
| read        |---0xf00fba11---| MMU |--0x1000ba11------->|  RAM    |
|             |                                           |         |
| *0xf00fba11 |<---------data coming back to CPU----------|         |
|-------------|                                           |---------|

    Process 2
 cpu
|-------------|                                           |---------|
| read        |---0xf00fba11---| MMU |--0x7000ba11------->|  RAM    |
|             |                                           |         |
| *0xf00fba11 |<---------data coming back to CPU----------|         |
|-------------|                                           |---------|

Both process 1 and process 2 asked for the data stored at memory address 0xf00fba11, but they were given 2 completely different RAM cells! This brillant invention is called "virtual memory". We say that 2 processes have different "address spaces" if the MMU will map their memories differently. The operating system decides on these mappings and configures the MMU to abide by them, thereby "insulating" processes from each other. Consider 2 processes and the memory addresses they might want to access.

Process 1
asks for          | gets physical address
------------------------------------
 0x0000 - 0x0fff  | ERROR SEGFAULT
 0x1000 - 0x1fff  | 0x70000 - 0x70fff
 0x2000 - 0x2fff  | 0x30000 - 0x30fff
 0x3000 - 0x3fff  | 0xa7000 - 0xa7fff
      etc....     | etc.....


Process 2
asks for          | gets physical address
------------------------------------
 0x0000 - 0x0fff  | ERROR SEGFAULT
 0x1000 - 0x1fff  | 0xb1000 - 0xb1fff
 0x2000 - 0x2fff  | 0x40000 - 0x40fff
 0x3000 - 0x3fff  | 0x1c000 - 0x1cfff
      etc....     | etc.....

So if an envrionment variable is loaded at memory address 0x7ffe2a673d84 in process 1, it might translate to physical address 0x63002a673d84. Moreover, when process 2 tries to access *0x7ff32a673d84, it will be mapped to a totally different address, or, in your case, it may be UNMAPPED for process 2, leading to a SEGFAULT.

So the bad news is, I don't think there is any way you can "fix" this issue with your code. Doing what you are trying to do will either give you a segfault or random, useless data. To get at the data you are interested in, you would need to peek in at the MMU configuration settings and alter them, which you aren't allowed to do unless you are running at an elevated privilege level.

Before we part, it's worth noting there may be a few shared addresses between processes in order to pass data back and forth between the 2 processes or to access shared software libraries. That is, 0x1000 will translate to 0x5000 for several different processes.

Or maybe I have no idea what you're talking about. I didn't really follow the line about ./getenv PATH ./fmt_vuln

like image 155
John M Avatar answered Oct 11 '22 19:10

John M