I'm studying buffer overflow and solving some wargames.
There was a problem that all of the stack memory above the buffer is set to 0 except return address of main, which will be:
buffer
[0000000...][RET][000000...]
and I can overwrite that RET.
So I found some hints for solving this problem.
It was to use LD_PRELOAD.
Some people said that LD_PRELOAD's value is in somewhere of stack not only in environment variable area of stack.
So I set LD_PRELOAD and search it and found it using gdb.
$ export LD_PRELOAD=/home/coffee/test.so
$ gdb -q abcde
(gdb) b main
Breakpoint 1 at 0x8048476
(gdb) r
Starting program: /home/coffee/abcde
Breakpoint 1, 0x8048476 in main ()
(gdb) x/s 0xbffff6df
0xbffff6df: "@èC\001@/home/coffee/test.so"
(gdb) x/s 0xbffffc59
0xbffffc59: "LD_PRELOAD=/home/coffee/test.so"
(gdb) q
The program is running. Exit anyway? (y or n) y
$
So there is!
Now I know that LD_PRELOAD's value is on stack below the buffer and now I can exploit!
But I wonder why LD_PRELOAD is loaded on that memory address.
The value is also on environment variable area of the stack!
What is the purpose of this?
Thanks.
Code to explore the stack layout:
#include <inttypes.h>
#include <stdio.h>
// POSIX 2008 declares environ in <unistd.h> (Mac OS X doesn't)
extern char **environ;
static void dump_list(const char *tag, char **list)
{
char **ptr = list;
while (*ptr)
{
printf("%s[%d] 0x%.16" PRIXPTR ": %s\n",
tag, (ptr - list), (uintptr_t)*ptr, *ptr);
ptr++;
}
printf("%s[%d] 0x%.16" PRIXPTR "\n",
tag, (ptr - list), (uintptr_t)*ptr);
}
int main(int argc, char **argv, char **envp)
{
printf("%d\n", argc);
printf("argv 0x%.16" PRIXPTR "\n", (uintptr_t)argv);
printf("argv[argc+1] 0x%.16" PRIXPTR "\n", (uintptr_t)(argv+argc+1));
printf("envp 0x%.16" PRIXPTR "\n", (uintptr_t)envp);
printf("environ 0x%.16" PRIXPTR "\n", (uintptr_t)environ);
dump_list("argv", argv);
dump_list("envp", envp);
return(0);
}
With the program compiled as x
, I ran it with a sanitized environment:
$ env -i HOME=$HOME PATH=$HOME/bin:/bin:/usr/bin LANG=$LANG TERM=$TERM ./x a bb ccc
4
argv 0x00007FFF62074EC0
argv[argc+1] 0x00007FFF62074EE8
envp 0x00007FFF62074EE8
environ 0x00007FFF62074EE8
argv[0] 0x00007FFF62074F38: ./x
argv[1] 0x00007FFF62074F3C: a
argv[2] 0x00007FFF62074F3E: bb
argv[3] 0x00007FFF62074F41: ccc
argv[4] 0x0000000000000000
envp[0] 0x00007FFF62074F45: HOME=/Users/jleffler
envp[1] 0x00007FFF62074F5A: PATH=/Users/jleffler/bin:/bin:/usr/bin
envp[2] 0x00007FFF62074F81: LANG=en_US.UTF-8
envp[3] 0x00007FFF62074F92: TERM=xterm-color
envp[4] 0x0000000000000000
$
If you study that carefully, you'll see that the argv
argument to main()
is the start of a series of pointers to strings further up the stack; the envp
(optional third argument to main()
on POSIX machines) is the same as the global variable environ
and argv[argc+1]
, and is also the start of a series of pointers to strings further up the stack; and the strings pointed at by the argv
and envp
pointers follow the two arrays.
This is the layout on Mac OS X (10.7.5 if it matters, which it probably doesn't), but I'm tolerably sure you'd find the same layout on other Unix-like systems.