문제

So, I am trying to perform a buffer overflow on this code, with the purpose of changing the variable target to 'Y'. The thing is, I can't seem to get the buffer to overflow enough to generate a runtime error. Can anyone help me understand as to why?

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

enum {SIZE = 50};

char target = 'Z';
char name[SIZE];
FILE *f;

void read(char *s) {
   char buffer[SIZE];
   int i = 0; 
   int c;

   for (;;) 
   {
      c = getchar();
      if ((c == EOF) || (c == '\n')) 
         break;
      buffer[i] = c;
      i++;
   }
   buffer[i] = '\0';

   for (i = 0; i < SIZE; i++) 
      s[i] = buffer[i];
}


int main(void) 
{
   read(name);

   if (strcmp(name, "ABCD") == 0) 
      target = 'Y';

   printf("%s\n", name);
   printf("%c\n", target);

   exit(0);
}
도움이 되었습니까?

해결책 2

Try this 'non-crashing' version of your code, that does overwrite the bounds of the name array for modest sizes of input string:

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

enum {SIZE = 50};

char target_1 = 'Z';
char name[SIZE] = "Help";
char target_2 = 'Z';

static void read(char *s)
{
   char buffer[2*SIZE];
   int i = 0; 
   int c;

   while ((c = getchar()) != EOF && c != '\n')
      buffer[i++] = c;
   buffer[i] = '\0';

   for (int j = 0; j < i; j++) 
      s[j] = buffer[j];
}


int main(void) 
{
   printf("Data-1: %c %s %c\n", target_1, name, target_2);
   read(name);
   printf("Data-2: %c %s %c\n", target_1, name, target_2);
   return(0);
}

Example runs:

$ for len in 48 49 50 51 52 53 54 ; do echo $len; perl -e "print 'a' x $len" | ./bo; done
48
Data-1: Z Help Z
Data-2: Z aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Z
49
Data-1: Z Help Z
Data-2: Z aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Z
50
Data-1: Z Help Z
Data-2: Z aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaZ Z
51
Data-1: Z Help Z
Data-2: a aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Z
52
Data-1: Z Help Z
Data-2: a aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Z
53
Data-1: Z Help Z
Data-2: a aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Z
54
Data-1: Z Help Z
Data-2: a aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Z
$

You can see that the target_1 is at a higher memory address than name, and that it does get overwritten from an input of size 52 or larger.

When I say 'non-crashing', I mean 'it does not crash for input lines that are long enough to overflow the name buffer but not so long as to overflow the buffer buffer' (roughly lengths 50 to 100). Using longer input strings, the program crashes with a segmentation fault, but it is interesting that it crashes after printing the second line of output, rather than during the return from read().

140
Data-1: Z Help Z
Data-2: a aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Z
Segmentation fault: 11

Tested on Mac OS X 10.9.2 Mavericks, with GCC 4.9.0.

다른 팁

If you're trying to do this with a changed return address, you'll need to overwrite it with a specific address that you want. In this case for the desired result, you could overwrite with the address of the code target = 'Y';

Depending on how the stack is organized, you may be able to directly clobber the return address from read() that will return you to just after the first line of main where it is called. Failing that, you have the opportunity to clobber i, which will let you arbitrarily write to memory. You will need to be careful as you write these bytes, as it will affect your index.

I'm going to pretend this is being done on an ARM, as I'm more familiar with their stack layout and calling conventions. So, your stack my look like so:

STACKPTR-60: buffer[0]
 |   |   |
STACKPTR-11: buffer[49]
STACKPTR-10: padding
STACKPTR -9: padding
STACKPTR -8: i (bits 7..0)
 |   |   |
STACKPTR -5: i (bits 31..24)
STACKPTR -4: c (bits 7..0)
 |   |   |
STACKPTR -1: c (bits 31..24)
STACKPTR +0: link register (return address) (bits 7..0)
 |   |   |
STACKPTR +3: link register (return address) (bits 31..24)

So, with this stack layout, as you overflow buffer, you will first fill two pad bytes. After that, you fill the least significant byte of i. In this case, you can skip a huge overflow and just modify your index to point to the stacked link register. You want i=60 to start writing to the return address, but after you write it, it will be incremented, so you really want i=59, which will then be incremented to i=60. At this point, you can then write the desired return address from least significant to most significant, followed by an EOF or \n.

It is really important to note that this is all incredibly architecture specific, and compiler specific. Compiler settings will determine the size of ints and whether the stack will be padded to align variables, and the order the variables appear on the stack. The architecture will determine what your stack frame will look like.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top