[转载]Adjacent Memory Overflows
Author: mercyDate: 3/04/2003
Subject: Adjacent Memory Overflows
Overview of article:
This article is meant to be presented as an informative, step by step log of exploiting an adjacent memory
overflow. It is aimed at those who have buffer overflow experience, and hopefully have knowledge of the
organisation of the stack.
An article has been posted in phrack magazine with a very good overview and introduction to this topic, I suggest
you read that and use this text as a reference to theorys presented.
-------------------------------------
Volume 0xa Issue 0x38
05.01.2000
0x0e[0x10]
twitch <[email]twitch@vicar.org[/email]>
-------------------------------------
Character arrays are terminated with NULL bytes(0x00), they let the function know that the end of the buffer
has been reached, and to stop reading at that point.
Q. If an array has no NULL byte, how does it know when to end?
A. It does not know when to end, it will continue reading from memory until a NULL byte is encounted or a seg fault occurs.
Q. How can this knowledge be incorporated into an exploitable situation?
A. Let me copy you passages from three "secure" functions man pages:
NAME: strncpy
SYNOPSIS: char *strncpy(char *dest, const char *src, size_t n);
INFO:
The strncpy() function is similar, except that not more than n bytes of src are copied.
Thus, if there is no null byte among the first n bytes of src, the result will not be null-
terminated.
In the case where the length of src is less than that of n, the remainder of dest will be
padded with nulls.
NAME: fprintf && sprintf
SYNOPSIS:
int fprintf(FILE *stream, const char *format, ...);
int sprintf(char *str, const char *format, ...);
INFO:
Upon successful return, these functions return the number of characters printed (not includ-
ing the trailing '\0' used to end output to strings). The functions snprintf and vsnprintf
do not write more than size bytes (including the trailing '\0'). If the output was trun-
cated due to this limit then the return value is the number of characters (not including the
trailing '\0') which would have been written to the final string if enough space had been
available. Thus, a return value of size or more means that the output was truncated. (See
also below under NOTES.) If an output error is encountered, a negative value is returned.
strncpy notes:
As you can see, NULL bytes are very important, now if we read the important part of strncpy, absorb this information:
If we have a buffer of 100 characters, and are copying to a buffer of 100 characters, it will NOT be NULL terminated,
this can arise in errors later on.
On the other hand, if we have a buffer of 99 characters, and are copying to a buffer of 100 characters, that extra n bytes
will be NULL terminated.
fprintf and sprintf notes:
We know that the man page is talking about the return value, but it gives us the information we need:
Upon successful return, these functions return the number of characters printed (not includ-
ing the trailing '\0' used to end output to strings).
This is telling us, that they use a NULL byte to terminate their output strings or to determine the end of their
strings.
From these three functions and our new knowledge, lets write up a program that will:
Accept two user inputs, copy one input to a buffer, and print out the copied buffer.
//--------------vuln to adjacent memory overwrite: DSR
#include <stdio.h>
#include <string.h>
int main(int argc, char **argv)
{
char copy_buff[263];
char exploit_string[1024];
char vuln_array[256];
if(argc <= 2){
printf("Usage: %s <name> <message>.\n", argv[0]);
exit(0);}
strncpy(vuln_array, argv[1], sizeof(vuln_array));
strncpy(exploit_string, argv[2], sizeof(exploit_string));
sprintf(copy_buff, "MSG: %s\n", vuln_array);
printf("%s\n", copy_buff);
return(0);
}
//-------------------------------------------end of example.
(The large buffers seem to get rid of junk between arrays. [those junk characters are usually pointers to something])
This looks seemingly harmless, I mean we are using strncpy, so our buffers arent going to overflow.
We can safely use the sprintf function without worry because strncpy did all the checking we needed.
Uh OH, armed with what we know, we can exploit this to our advantage.
Knowing that strncpy will copy all of the buffer to the very last byte, we can essentially write 256 characters into
argv[1] in turn overwriting where a NULL byte should be placed, and leaving the sprintf function vulnerable to an overflow.
Our stack knowledge must come into play arounnnnnnnd.... NOW!
If we have two character arrays:
char buffer1[] = "hello";
char buffer2[] = "goodbye;
When it is compiled, the order of the buffers should look a little something like:
[Upper memory/top of stack]
buffer2[0] = 'g';
buffer2[1] = 'o';
buffer2[2] = 'o';
buffer2[3] = 'd';
buffer2[4] = 'b';
buffer2[5] = 'y';
buffer2[6] = 'e';
buffer2[7] = '\0'; <-- NULL BYTE
buffer1[0] = 'h';
buffer1[1] = 'e';
buffer1[2] = 'l';
buffer1[3] = 'l';
buffer1[4] = 'o';
buffer1[5] = '\0'; <-- NULL BYTE
... etc
The stack grows down, so we can be safe that our goodbye buffer will always be above hello.
Hence, when we are overwriting buffer2's NULL byte, the next set of data it will be reading from is buffer1.
So if we have a look at our vulnerable programs declaration of arrays:
char exploit_string[1024];
char vuln_array[256];
We are overwriting vuln_array's NULL byte leading us directly into exploit_string array, how fortunate for us ;)
Im just going to take a detour here to show you how we figured out the ordering of variables on the stack:
//------------example1.c-------------
#include <stdio.h>
int main(void)
{
char buffer1[] = "hello";
char buffer2[] = "goodbye";
char buffer3[] = "greetings";
printf("buffer1: %p\n", buffer1);
printf("buffer2: %p\n", buffer2);
printf("buffer3: %p\n", buffer3);
return(0);
}
//----------------------------------
mercy@cia:~$ gcc example1.c -o example1
mercy@cia:~$ ./example1
buffer1: 0xbffffad0
buffer2: 0xbffffac8
buffer3: 0xbffffab0
mercy@cia:~$ gdb -q example1
(gdb) disass main
....
0x80483b6 <main+142>: leave
(gdb) break *0x80483b6
(gdb) r
Starting program: /home/mercy/example1
buffer1: 0xbffffad0
buffer2: 0xbffffac8
buffer3: 0xbffffab0
Breakpoint 1, 0x080483b6 in main ()
(gdb) x/s 0xbffffab0 <-- BUFFER3
0xbffffab0: "greetings"
(gdb)
0xbffffaba: "\022"
(gdb)
0xbffffabc: "
页:
[1]
