GNU/Linux >> Linux Esercitazione >  >> Linux

Linux Shellcode Ciao, mondo!

Come accennato da BSH, il tuo shellcode non contiene i byte del messaggio. Salto allo MESSAGE label e chiamando GOBACK routine appena prima di definire il msg byte è stata una buona mossa in quanto l'indirizzo di msg sarebbe stato in cima allo stack come indirizzo di ritorno che poteva essere saltato a ecx , dove è memorizzato l'indirizzo di msg.

Ma sia il codice tuo che quello di BSH hanno una leggera limitazione. Contiene NULL bytes ( \x00 ) che verrebbe considerato come fine della stringa quando dereferenziato dal puntatore a funzione.

C'è un modo intelligente per aggirare questo problema. I valori che memorizzi in eax, ebx and edx sono abbastanza piccoli da poter essere scritti direttamente nei nibble inferiori dei rispettivi registri in una volta sola accedendo a al, bl and dl rispettivamente. Il nibble superiore può contenere un valore spazzatura in modo che possa essere xored.

b8 04 00 00 00 ------ mov $0x4,%eax


diventa

b0 04          ------ mov $0x4,%al
31 c0          ------ xor    %eax,%eax


A differenza del set di istruzioni precedente, il nuovo set di istruzioni non contiene alcun byte NULL.

Quindi, il programma finale ha questo aspetto :

global _start

section .text

_start:
jmp message

proc:
    xor eax, eax
    mov al, 0x04
    xor ebx, ebx
    mov bl, 0x01
    pop ecx
    xor edx, edx
    mov dl, 0x16
    int 0x80

    xor eax, eax
    mov al, 0x01
    xor ebx, ebx
    mov bl, 0x01   ; return 1
    int 0x80

message:
    call proc
    msg db " y0u sp34k 1337 ? "

section .data

Assemblaggio e collegamento :

$ nasm -f elf hello.asm -o hello.o
$ ld -s -m elf_i386 hello.o -o hello
$ ./hello
 y0u sp34k 1337 ? $ 

Ora estrai lo shellcode dal binario hello :

$ for i in `objdump -d hello | tr '\t' ' ' | tr ' ' '\n' | egrep '^[0-9a-f]{2}$' ` ; do echo -n "\\x$i" ; done

uscita:

\xeb\x19\x31\xc0\xb0\x04\x31\xdb\xb3\x01\x59\x31\xd2\xb2\x12\xcd\x80\x31\xc0\xb0\x01\x31\xdb\xb3\x01\xcd\x80\xe8\xe2\xff\xff\xff\x20\x79\x30\x75\x20\x73\x70\x33\x34\x6b\x20\x31\x33\x33\x37\x20\x3f\x20

Ora possiamo avere il nostro programma driver per lanciare lo shellcode.

#include <stdio.h>

char shellcode[] = "\xeb\x19\x31\xc0\xb0\x04\x31\xdb"
                   "\xb3\x01\x59\x31\xd2\xb2\x12\xcd"
                   "\x80\x31\xc0\xb0\x01\x31\xdb\xb3"
                   "\x01\xcd\x80\xe8\xe2\xff\xff\xff"
                   "\x20\x79\x30\x75\x20\x73\x70\x33"
                   "\x34\x6b\x20\x31\x33\x33\x37\x20"
                   "\x3f\x20";


int main(int argc, char **argv) {
    (*(void(*)())shellcode)();
    return 0;
}

Esistono alcune funzionalità di sicurezza nei compilatori moderni come la protezione NX che impedisce l'esecuzione di codice nel segmento di dati o nello stack. Quindi dovremmo specificare esplicitamente il compilatore per disabilitarli.

$ gcc -g -Wall -fno-stack-protector -z execstack launcher.c -o launcher

Ora il launcher può essere invocato per lanciare lo shellcode.

$ ./launcher
 y0u sp34k 1337 ? $ 

Per codici shell più complessi, ci sarebbe un altro ostacolo. I moderni kernel Linux hanno ASLR o Address Space Layout Randomization Potrebbe essere necessario disabilitarlo prima di iniettare lo shellcode, specialmente quando si tratta di overflow del buffer.

[email protected]:~# echo 0 > /proc/sys/kernel/randomize_va_space 

Quando inserisci questo shellcode, non sai cosa c'è a message :

mov ecx, message

nel processo iniettato, può essere qualsiasi cosa ma non sarà "Hello world!\r\n" poiché è nella sezione dati mentre stai scaricando solo la sezione testo. Puoi vedere che il tuo shellcode non ha "Hello world!\r\n" :

"\xb8\x04\x00\x00\x00"
"\xbb\x01\x00\x00\x00"
"\xb9\x00\x00\x00\x00"
"\xba\x0f\x00\x00\x00"
"\xcd\x80\xb8\x01\x00"
"\x00\x00\xbb\x00\x00"
"\x00\x00\xcd\x80";

Questo è un problema comune nello sviluppo di shellcode, il modo per aggirarlo è questo:

global _start

section .text

_start:
    jmp MESSAGE      ; 1) lets jump to MESSAGE

GOBACK:
    mov eax, 0x4
    mov ebx, 0x1
    pop ecx          ; 3) we are poping into `ecx`, now we have the
                     ; address of "Hello, World!\r\n" 
    mov edx, 0xF
    int 0x80

    mov eax, 0x1
    mov ebx, 0x0
    int 0x80

MESSAGE:
    call GOBACK       ; 2) we are going back, since we used `call`, that means
                      ; the return address, which is in this case the address 
                      ; of "Hello, World!\r\n", is pushed into the stack.
    db "Hello, World!", 0dh, 0ah

section .data

Ora scarica la sezione di testo:

$ nasm -f elf shellcode.asm
$ ld shellcode.o -o shellcode
$ ./shellcode 
Hello, World!
$ objdump -d shellcode

shellcode:     file format elf32-i386


Disassembly of section .text:

08048060 <_start>:
 8048060:   e9 1e 00 00 00   jmp    8048083 <MESSAGE>

08048065 <GOBACK>:
 8048065:   b8 04 00 00 00   mov    $0x4,%eax
 804806a:   bb 01 00 00 00   mov    $0x1,%ebx
 804806f:   59               pop    %ecx
 8048070:   ba 0f 00 00 00   mov    $0xf,%edx
 8048075:   cd 80            int    $0x80
 8048077:   b8 01 00 00 00   mov    $0x1,%eax
 804807c:   bb 00 00 00 00   mov    $0x0,%ebx
 8048081:   cd 80            int    $0x80

08048083 <MESSAGE>:
 8048083:   e8 dd ff ff ff   call   8048065 <GOBACK>
 8048088:   48               dec    %eax                    <-+
 8048089:   65               gs                               |
 804808a:   6c               insb   (%dx),%es:(%edi)          |
 804808b:   6c               insb   (%dx),%es:(%edi)          |
 804808c:   6f               outsl  %ds:(%esi),(%dx)          |
 804808d:   2c 20            sub    $0x20,%al                 |
 804808f:   57               push   %edi                      |
 8048090:   6f               outsl  %ds:(%esi),(%dx)          |
 8048091:   72 6c            jb     80480ff <MESSAGE+0x7c>    |
 8048093:   64               fs                               |
 8048094:   21               .byte 0x21                       |
 8048095:   0d               .byte 0xd                        |
 8048096:   0a               .byte 0xa                      <-+

$

Le righe che ho contrassegnato sono le nostre "Hello, World!\r\n" stringa:

$ printf "\x48\x65\x6c\x6c\x6f\x2c\x20\x57\x6f\x72\x6c\x64\x21\x0d\x0a"
Hello, World!

$ 

Quindi il nostro wrapper C sarà:

char code[] = 

    "\xe9\x1e\x00\x00\x00"  //          jmp    (relative) <MESSAGE>
    "\xb8\x04\x00\x00\x00"  //          mov    $0x4,%eax
    "\xbb\x01\x00\x00\x00"  //          mov    $0x1,%ebx
    "\x59"                  //          pop    %ecx
    "\xba\x0f\x00\x00\x00"  //          mov    $0xf,%edx
    "\xcd\x80"              //          int    $0x80
    "\xb8\x01\x00\x00\x00"  //          mov    $0x1,%eax
    "\xbb\x00\x00\x00\x00"  //          mov    $0x0,%ebx
    "\xcd\x80"              //          int    $0x80
    "\xe8\xdd\xff\xff\xff"  //          call   (relative) <GOBACK>
    "Hello wolrd!\r\n";     // OR       "\x48\x65\x6c\x6c\x6f\x2c\x20\x57"
                            //          "\x6f\x72\x6c\x64\x21\x0d\x0a"


int main(int argc, char **argv)
{
    (*(void(*)())code)();

    return 0;
}

Proviamolo, usando -z execstack per abilitare read-implies-exec (a livello di processo, nonostante "stack" nel nome) in modo da poter eseguire il codice in .data o .rodata sezioni:

$ gcc -m32 test.c -z execstack -o test
$ ./test 
Hello wolrd!

Funziona. (-m32 è necessario anche sui sistemi a 64 bit. Il int $0x80 L'ABI a 32 bit non funziona con indirizzi a 64 bit come .rodata in un eseguibile PIE. Inoltre, il codice macchina è stato assemblato per 32 bit. Succede che la stessa sequenza di byte venga decodificata in istruzioni equivalenti in modalità a 64 bit, ma non è sempre così.)

GNU moderno ld inserisce .rodata in un segmento separato da .text , quindi può essere non eseguibile. Prima era sufficiente usare const char code[] inserire codice eseguibile in una pagina di dati di sola lettura. Almeno per lo shellcode che non vuole modificarsi.


Linux
  1. Smalltalk Hello World Esempio:come scrivere ed eseguire il programma Smalltalk su sistema operativo Linux

  2. Esempio Fortran Hello World:come scrivere ed eseguire il programma Fortran su sistema operativo Linux

  3. Esiste un equivalente di .Net FileSystemWatcher nel mondo Linux?

  4. Come aggiungere il mio software a un pacchetto Buildroot Linux?

  5. Come posso montare un'unità di rete WD MyBook World in Linux?

Iniziare con il comando cat di Linux

Comando W in Linux

Un ragazzo Windows in un mondo Linux:VS Code e SSH remoto

Un ragazzo Windows in un mondo Linux:configurazione del comando SSH in Linux

Un ragazzo di Windows in un mondo Linux:utenti e autorizzazioni di file

Un ragazzo Windows in un mondo Linux:YaST e il desktop