32-битный шелл-код вызывает ошибку сегментации при попытке положить sh "/ bin // sh" в стек - PullRequest
1 голос
/ 05 апреля 2020

Я следую учебному курсу opensecurity «Эксплойты 1». В настоящее время я пытаюсь использовать простую программу c с некоторым шелл-кодом в 32-битной системе linux, используя переполнение буфера. Программа c:

void main(int argc, char **argv)
{
    char buf[64];
    strcpy(buf,argv[1]);
}

Я скомпилировал программу с помощью команды "t cc -g -o basic_vuln basic_vuln. c". Затем я запрограммировал следующий шелл-код.

section .text

global _start

_start:
xor eax, eax
xor ebx, ebx
xor ecx, ecx
xor edx, edx

mov al, 11

push ebx
push 0x68732f2f
push 0x6e69622f
mov ebx, esp

int 0x80

Я скомпилировал его, набрав «nasm -f elf shell.asm; ld -o shell shell.o». Когда я пытаюсь выполнить «оболочку» самостоятельно, она работает, и я получаю оболочку. Затем я разобрал программу с помощью objdump, написал файл perl, который печатает коды операций, а затем перенаправил вывод указанного файла perl вместе с 39 инструкциями nop перед шеллкодом в файл с именем "шеллкод", так что полезная нагрузка теперь длиной 64 байта, заполняя буфер. Затем я открыл программу c в gdb и выбрал адрес в середине nop-салазок, который будет новым адресом возврата (0xbffff540). Я добавил адрес в файл «шеллкод» вместе с 4 байтами, чтобы перезаписать сохраненный указатель кадра. Код шелл-кода выглядит следующим образом:

shellcode

Теперь, когда я пытаюсь запустить этот шелл-код в gdb в программе c, он вызывает ошибку сегментации по адресу 0xbffff575, который указывает на определенный момент в моем шелл-коде, 0x62, который является символом "b" в "/ bin / sh". Что может вызвать это?

Вот мой кадр стека, подтверждающий, что выбранный мной адрес возврата действительно возвращается к середине салазок.

enter image description here

Курс предоставляет шелл-код, который работает в gdb в программе c: workingshellcode

1 Ответ

4 голосов
/ 05 апреля 2020

После того, как main вернется в ваш шелл-код, ESP, вероятно, будет указывать чуть выше этого буфера. И EIP указывает на начало этого; вот что означает возврат в него.

Пара push инструкций может изменить машинный код в конце буфера, приводя к SIGILL с EIP, указывающим на байт, который вы только что выдвинули.

Вероятно, самое простое решение - от add esp, -128 до go до вашего буфера. Или sub esp, -128 до go выше по стеку. (-128 - это наибольшее значение 8-битного немедленного значения, которое вы можете использовать, избегая введения нулей в машинном коде с помощью sub esp, 128 или 1024. Если вы хотите переместить стек дальше, вы, конечно, можете построить большее значение в регистр.)

Я не проверял это предположение, но вы можете подтвердить его в GDB, войдя в свой шелл-код одним нажатием si с конца main до пошаговые инструкции.

Используйте disas после каждой инструкции, чтобы увидеть разборку. Или используйте layout reg. См. Нижнюю часть { ссылка } для получения дополнительных советов по отладке GDB.


Данное решение является более сложным, поскольку оно, по-видимому, устанавливает фактический массив argv вместо простой передачи значения NULL. указатели для char **argv и char **envp. (Который в Linux обрабатывается так же, как действительные указатели для пустых массивов с нулевым символом в конце: http://man7.org/linux/man-pages/man2/execve.2.html#NOTES).

Но ключевое отличие состоит в том, что он использует jmp / call / pop чтобы получить указатель на строку уже в памяти. Это только один слот стека, а не три . (Конец его полезной нагрузки перед адресом возврата - данные, а не инструкции, но он потерпит неудачу по-другому, если он сделает слишком много нажатий и перезапишет строку вместо простого хранения терминатора 0. call переходит назад до выдвинутый адрес возврата фактически изменяет буфер, но если он перезапишет что-нибудь ближе к концу, он все равно сломается.)


@ Маргарет рассмотрела это более подробно, и заметил, что это всего лишь 3-й пу sh, который ломает что-либо . Это имеет смысл: первые два предположительно перезаписывают часть полезной нагрузки, которая содержала новый адрес возврата и сохраненное значение EBP. И так уж получилось, что компилятор поместил буфер main в смежный с этим.

Если вы действительно использовали tcc, а не gcc, это, вероятно, не удивительно. G CC выровнял бы его на 16 и, вероятно, по той или иной причине оставил бы пробел между буфером и вершиной стекового кадра.

...