Удаленный системный вызов mmap с использованием ptrace (Linux, C)

Я застрял с этой проблемой в течение нескольких дней, и до сих пор не удалось ее исправить. По сути, я хочу сделать удаленный системный вызов от программы злоумышленника к цели. Но прежде чем показывать код, я думаю, было бы неплохо представить ход моих мыслей, так как проблема на данном этапе может заключаться в чем угодно. Я делаю этот удаленный системный вызов с помощью следующих шагов:

  1. Разберите файл /proc/‹process_id›/maps, чтобы получить исполняемый регион.
  2. Сохраните данные в исполняемой области и напишите собственный буфер, который выполняет системный вызов.
  3. Сохраните старые регистры и настройте новые, чтобы сделать системный вызов
  4. Запишите новые регистры и продолжите выполнение
  5. После системного вызова целевая программа прервется, что позволит мне получить вывод mmap, восстановить старые регистры и, следовательно, восстановить старый поток выполнения.

Я использую свою библиотеку памяти для анализа файлов mmap, получения идентификатора процесса и информации о процессе и т. Д. Насколько я понимаю, он работает правильно. В любом случае, вот источник: https://github.com/rdbo/libmem

И код, который я использую для вызова:

mem_voidptr_t allocate_ex(mem_process_t process, mem_size_t size, mem_alloc_t allocation)
{
    mem_voidptr_t alloc_addr = (mem_voidptr_t)MEM_BAD_RETURN;
    if(!mem_process_is_valid(&process)) return alloc_addr;
    int status;
    int mmap_syscall = __NR_mmap;
    struct user_regs_struct old_regs, regs;
    mem_byte_t injection_buf[] = 
    {
        0x0f, 0x05, //syscall
        0xcc        //int3
    };


    //Parse /proc/<process.pid>/maps to get executable region

    char path_buffer[64];
    snprintf(path_buffer, sizeof(path_buffer), "/proc/%i/maps", process.pid);
    int fd = open(path_buffer, O_RDONLY);
    if(fd == -1) return alloc_addr;

    int read_check = 0;
    mem_size_t file_size = 0;
    mem_string_t file_buffer = mem_string_init();

    for(char c; (read_check = read(fd, &c, 1)) != -1 && read_check != 0; file_size++)
    {
        mem_string_resize(&file_buffer, file_size);
        mem_string_c_set(&file_buffer,  file_size, c);
    }

    mem_size_t   injection_address_pos, injection_address_end;
    mem_string_t injection_address_str = mem_string_init();
    mem_voidptr_t injection_address = (mem_voidptr_t)MEM_BAD_RETURN;

    injection_address_pos = mem_string_find(&file_buffer, "r-xp", 0);
    injection_address_pos = mem_string_rfind(&file_buffer, "\n", injection_address_pos);
    if(injection_address_pos == file_buffer.npos) return alloc_addr;

    injection_address_end = mem_string_find(&file_buffer, "-", injection_address_pos);
    injection_address_str = mem_string_substr(&file_buffer, injection_address_pos, injection_address_end);
    injection_address = (mem_voidptr_t)strtoull(mem_string_c_str(&injection_address_str), NULL, 16);
    if(injection_address == (mem_voidptr_t)MEM_BAD_RETURN || injection_address == (mem_voidptr_t)0)
        return alloc_addr;

    printf("Injection address: %p\n", injection_address);

    //Store the old data at 'injection_address' and write the injection buffer to it

    mem_byte_t old_data[sizeof(injection_buf)];
    mem_ex_read(process, injection_address, (mem_voidptr_t)old_data, sizeof(old_data));
    mem_ex_write(process, injection_address, (mem_voidptr_t)injection_buf, sizeof(injection_buf));

    //Attach to process and store current registers

    ptrace(PTRACE_ATTACH, process.pid, NULL, NULL);
    ptrace(PTRACE_GETREGS, process.pid, NULL, &old_regs);
    memcpy(&regs, &old_regs, sizeof(regs));

    //Setup syscall registers

    regs.rax = mmap_syscall;          //syscall number
    regs.rdi = 0;                     //address        (arg0)
    regs.rsi = size;                  //length         (arg1)
    regs.rdx = allocation.protection; //protection     (arg2)
    regs.r10 = allocation.type;       //flags          (arg3)
    regs.r8  = -1;                    //fd             (arg4)
    regs.r9  = 0;                     //offset         (arg5)

    regs.rip = (unsigned long long)injection_address; //next instruction to execute

    //Call mmap on external process

    ptrace(PTRACE_SETREGS, process.pid, NULL, &regs);
    ptrace(PTRACE_CONT, process.pid, NULL, NULL);
    waitpid(process.pid, &status, WSTOPPED);

    //Get the registers after syscall to store the return of mmap

    ptrace(PTRACE_GETREGS, process.pid, NULL, &regs);
    alloc_addr = (mem_voidptr_t)regs.rax; //store the return of mmap

    //Restore the original buffer at 'injection_address'

    mem_ex_write(process, injection_address, (mem_voidptr_t)old_data, sizeof(old_data));

    //Continue the original execution

    ptrace(PTRACE_SETREGS, process.pid, NULL, &old_regs);
    ptrace(PTRACE_CONT, process.pid, NULL, NULL);

    //Return allocation address, if valid
    if((mem_uintptr_t)alloc_addr >= (mem_uintptr_t)-2048)
        alloc_addr = (mem_voidptr_t)MEM_BAD_RETURN;
    return alloc_addr;
}

и основная функция атакующей программы:

int main()
{
    mem_pid_t pid = mem_ex_get_pid(mem_string_new("target"));
    mem_process_t process = mem_ex_get_process(pid);

    int buffer = 10;
    mem_alloc_t allocation = mem_alloc_init();
    allocation.protection = PROT_READ | PROT_WRITE;
    allocation.type       = MAP_ANON  | MAP_PRIVATE;
    mem_voidptr_t alloc_addr = allocate_ex(process, sizeof(buffer), allocation);
    printf("Allocation Address: %p\n", alloc_addr);
    if(alloc_addr == (mem_voidptr_t)MEM_BAD_RETURN)
    {
        printf("Invalid allocation\n");
        return -1;
    }

    //Check if worked by reading/writing to that buffer
    int read_buffer = 0;
    mem_ex_write(process, alloc_addr, &buffer, sizeof(buffer));
    mem_ex_read(process, alloc_addr, &read_buffer, sizeof(read_buffer));
    printf("Read buffer: %i\n", read_buffer);
    if(read_buffer == buffer)
        printf("Success!\n");

    return 0;   
}

Целевая программа:

int main()
{
    printf("Waiting for injection\n");
    while(1);
}

Вывод программы злоумышленника:

Injection address: 0x55f6e104a000
Allocation Address: (nil)
Read buffer: 0

и в целевой программе возникает ошибка сегментации. Исполняемый регион действителен (я проверял вручную) и процесс тоже действителен. Кроме того, у меня возникают проблемы с отладкой целевой программы, очевидно, GDB не позволяет ptrace выполнять свою работу из программы злоумышленника. Запуск Arch Linux. Обе программы скомпилированы с clang (x64). Любые идеи?


person rdbo    schedule 11.08.2020    source источник


Ответы (1)


Оказывается, проблема заключалась в том, что я читал/записывал память, используя process_vm_read и process_vm_write. Я заставил его работать, изменив метод чтения/записи на данные ptrace PEEK/POKE. Фиксированный код (включен в мою библиотеку памяти):

mem_voidptr_t injection_address;
    struct user_regs_struct old_regs, regs;
    int status;

    const mem_byte_t injection_buffer[] = 
    {
        0x0f, 0x05, //syscall
        0xcc        //int3 (SIGTRAP)
    };

    mem_byte_t old_data[sizeof(injection_buffer)];

    //Find injection address

    char path_buffer[64];
    snprintf(path_buffer, sizeof(path_buffer), "/proc/%i/maps", process.pid);
    int fd = open(path_buffer, O_RDONLY);
    if(fd == -1) return alloc_addr;

    int read_check = 0;
    mem_size_t file_size = 0;
    mem_string_t file_buffer = mem_string_init();

    for(char c; (read_check = read(fd, &c, 1)) != -1 && read_check != 0; file_size++)
    {
        mem_string_resize(&file_buffer, file_size);
        mem_string_c_set(&file_buffer,  file_size, c);
    }

    mem_size_t   injection_address_pos, injection_address_end;
    mem_string_t injection_address_str = mem_string_init();
    injection_address = (mem_voidptr_t)MEM_BAD_RETURN;

    injection_address_pos = mem_string_find(&file_buffer, "r-xp", 0);
    injection_address_pos = mem_string_rfind(&file_buffer, "\n", injection_address_pos);
    if(injection_address_pos == file_buffer.npos) return alloc_addr;

    injection_address_end = mem_string_find(&file_buffer, "-", injection_address_pos);
    injection_address_str = mem_string_substr(&file_buffer, injection_address_pos, injection_address_end);
    injection_address = (mem_voidptr_t)strtoull(mem_string_c_str(&injection_address_str), NULL, 16);
    if(injection_address == (mem_voidptr_t)MEM_BAD_RETURN || injection_address == (mem_voidptr_t)0) return alloc_addr;

    //Inject
    ptrace(PTRACE_ATTACH, process.pid, NULL, NULL);

    //Store data at injection_address
    for(mem_size_t i = 0; i < sizeof(injection_buffer); i++)
        ((mem_byte_t*)old_data)[i] = (mem_byte_t)ptrace(PTRACE_PEEKDATA, process.pid, injection_address + i, NULL);

    //Write injection buffer to injection address
    for(mem_size_t i = 0; i < sizeof(injection_buffer); i++)
        ptrace(PTRACE_POKEDATA, process.pid, injection_address + i, ((mem_byte_t*)injection_buffer)[i]);

    ptrace(PTRACE_GETREGS, process.pid, NULL, &old_regs);
    regs = old_regs;

    regs.rax = __NR_mmap;                        //syscall number
    regs.rdi = (mem_uintptr_t)0;                 //arg0 (void* address)
    regs.rsi = (mem_uintptr_t)size;              //arg1 (size_t size)
    regs.rdx = (mem_uintptr_t)protection;        //arg2 (int protection)
    regs.r10 = MAP_PRIVATE | MAP_ANON;           //arg3 (int flags)
    regs.r8  = -1;                               //arg4 (int fd)
    regs.r9  = 0;                                //arg5 (off_t offset)
    regs.rip = (mem_uintptr_t)injection_address; //next instruction

    ptrace(PTRACE_SETREGS, process.pid, NULL, &regs);
    ptrace(PTRACE_CONT, process.pid, NULL, NULL);
    waitpid(process.pid, &status, WSTOPPED);
    ptrace(PTRACE_GETREGS, process.pid, NULL, &regs);
    alloc_addr = (mem_voidptr_t)regs.rax;

    //Restore old execution
    ptrace(PTRACE_SETREGS, process.pid, NULL, &old_regs);

    for(mem_size_t i = 0; i < sizeof(injection_buffer); i++)
        ptrace(PTRACE_POKEDATA, process.pid, injection_address + i, ((mem_byte_t*)old_data)[i]);

    //ptrace(PTRACE_CONT, process.pid, NULL, NULL);
    ptrace(PTRACE_DETACH, process.pid, NULL, NULL);

    if(alloc_addr == (mem_voidptr_t)__NR_mmap)
        alloc_addr = (mem_voidptr_t)MEM_BAD_RETURN;

    return alloc_addr;
person rdbo    schedule 14.08.2020