Ошибка сегментации в C shellcode x64

Вопрос или проблема

Я изучаю разработку шелкода на 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) в вашем случае может быть вызвана несколькими причинами, связанными с реализацией вашего тестового кода и сборкой вашей шел-кодовой программы. Давайте подробно рассмотрим вашу ситуацию и возможные решения проблемы.

Анализ и возможные ошибки

  1. Корректность кода:
    Ваш код шелл-кода выглядит корректным, но убедитесь, что все байты корректно скомпилированы в массив unsigned char code[]. Ваша строка в tester.c соответствует тому, что вы получили из ассемблерного кода.

  2. Сборка и компиляция:
    Убедитесь, что вы используете следующие команды для компиляции tester.c:

    gcc -m64 -z execstack -fno-stack-protector -o tester tester.c

    Эти параметры отключают защиту стека и гарантируют, что стек исполняемых файлов является исполняемым.

  3. Данный код работает только в 64-битной системе:
    Убедитесь, что вы действительно используете 64-битную версию Ubuntu. Команда uname -m должна возвращать x86_64.

  4. Randomization Address Space Layout (ASLR):
    Если вы не отключили ASLR, выполнение шелл-кода может быть невозможно, потому что адреса в памяти будут случайными. Вы уже применили команду setarch для его отключения. Важно убедиться, что она действительно исполняется:

    setarch $(uname -m) -R ./tester
  5. Отладка:
    Чтобы более детально понять, где происходит сбой, можно использовать отладчик gdb. Запустите программу в gdb:

    gdb ./tester

    Затем введите команду run, чтобы запустить программу. Когда произойдёт сегментационная ошибка, вы сможете использовать команду bt, чтобы получить информацию о стеке и месте сбоя.

  6. Проблемы со стеком:
    Убедитесь, что ваш шелл-код в 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, а затем копируем туда наш шелл-код перед его выполнением. Это должно помочь избежать проблем с доступом к памяти.

Заключение

Эта проблема может быть вызвана множеством факторов, от ошибок в коде до проблем с памятью и сборкой. Следуйте приведённым рекомендациям и убедитесь, что все шаги выполнены корректно, чтобы устранить сегментационные ошибки при выполнении вашего шелл-кода.

Оцените материал
Добавить комментарий

Капча загружается...