¿Por qué mi programa de prueba shellcode x86 segfault?

7

Estoy tratando de aprender cómo crear shellcode, mi objetivo es llamar a execve() . Escribí el código en ensamblador y funciona perfectamente, no hay ningún método de direccionamiento absoluto o de bytes nulos. El código funciona bien, pero cuando copio los códigos de operación en un programa c y lo pruebo para ver si funciona, devuelve un error de segmentación. ¿Cuál es el problema?

ASM:

section .text     
global _start
_start:
    jmp xxx

xx:
    mov al,11
    pop ebx
    xor ecx,ecx
    xor edx,edx
    int 0x80
    mov al,1
    xor bl,bl
    int 0x80

section .data
xxx:
    call xx
path db "/bin/sh"

CÓDIGO C:

char shellcode[]={"\xb0\x0b\x5b\x31\xc9\x31\xd2\xcd\x80\xb0\x01\x30\xdb\xcd\x80"};

int main(){
    void (*ptr) (void) = &shellcode;
    ptr();
    return 0;
}
    
pregunta tropz 11.08.2018 - 15:49
fuente

1 respuesta

19

Veo múltiples problemas con tu shellcode. En primer lugar vamos a depurar su código. Compilé el código C que contiene su shellcode, lo ejecuto con gdb y paso hasta la primera llamada al sistema ( int 0x80 )

[----------------------------------registers-----------------------------------]
EAX: 0x5655700b --> 0xde3050f7 
EBX: 0x5655550c (<main+35>: mov    eax,0x0)
ECX: 0x0 
EDX: 0x0 
ESI: 0xf7f9fe24 --> 0x1d6d2c 
EDI: 0xf7f9fe24 --> 0x1d6d2c 
EBP: 0xffffd9d8 --> 0x0 
ESP: 0xffffd9c0 --> 0xf7fe5ae0 (<_dl_fini>: push   ebp)
EIP: 0x5655701f --> 0x1b080cd
EFLAGS: 0x246 (carry PARITY adjust ZERO sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
   0x5655701a <shellcode+2>:    pop    ebx
   0x5655701b <shellcode+3>:    xor    ecx,ecx
   0x5655701d <shellcode+5>:    xor    edx,edx
=> 0x5655701f <shellcode+7>:    int    0x80
   0x56557021 <shellcode+9>:    mov    al,0x1
   0x56557023 <shellcode+11>:   xor    bl,bl
   0x56557025 <shellcode+13>:   int    0x80
   0x56557027 <shellcode+15>:   add    BYTE PTR [eax],al
[------------------------------------stack-------------------------------------]
0000| 0xffffd9c0 --> 0xf7fe5ae0 (<_dl_fini>:    push   ebp)
0004| 0xffffd9c4 --> 0x0 
0008| 0xffffd9c8 (")UUV0pUV$617$617")
0012| 0xffffd9cc --> 0x56557018 --> 0x315b0bb0 
0016| 0xffffd9d0 --> 0xf7f9fe24 --> 0x1d6d2c 
0020| 0xffffd9d4 --> 0xf7f9fe24 --> 0x1d6d2c 
0024| 0xffffd9d8 --> 0x0 
0028| 0xffffd9dc --> 0xf7de3141 (<__libc_start_main+241>:   add    esp,0x10)
[------------------------------------------------------------------------------]

Aquí podemos ver algunos problemas:

  • El registro EAX no está establecido en 0xb. Esto se debe a que su código de shell no borra los valores en el registro y en su lugar solo establece el byte inferior con la instrucción mov al, 0xb
  • El registro EBX debería apuntar al char* con el archivo que está intentando ejecutar (generalmente es "/bin/sh" ), en su lugar, apunta a una posición de memoria aleatoria, en este caso en el main función.
  • El registro ECX debe apuntar a la matriz de char* que indica el comando completo que desea ejecutar. En la mayoría de los códigos de shell que he visto, es solo ["/bin/sh", 0] , pero es posible que desee utilizar algo diferente, como ["/path/to/binary","-argument1",..., 0] , en este caso, debe crear esa matriz en la memoria. En su shellcode ECX se establece en 0x0
  • El registro EDX representa el entorno para la ejecución del binario. Puede echar un vistazo a la página execve (3) para comprender un poco más cómo se usa, pero para nuestro propósito aquí, está bien tener un valor NULL en él

Ahora, ¿cómo podemos arreglarlo? Bueno, en primer lugar tendremos que apuntar EBX a una cadena "/ bin / sh \ 0" en una parte de la memoria a la que podemos acceder, es decir, la pila. Y tenemos que hacer eso en nuestro shellcode. Podemos hacerlo con el siguiente gadget:

xor eax, eax //Clear the eax register so we have a null byte to end our string
push eax
push "n/sh" //The string needs to be written "backwards"
push "//bi" //The double "/" is to avoid null bytes in our shellcode
mov ebx, esp //esp is pointing to "//bin/sh
xor eax, eax 
push eax
push "n/sh"
push "//bi"
mov ebx, esp
push eax // Remember it's still 0 from our previous xor eax, eax
push ebx // Push it so ESP points to EBX
mov ecx, esp // move ESP to ECX, the result is ECX -> EBX -> "//bin/sh
section .text
global _start
_start:
    jmp trampoline

shellcode:
    xor eax, eax
    push eax
    push "n/sh"
    push "//bi"
    mov ebx, esp
    push eax
    push ebx
    mov ecx, esp
    mov al,11
    int 0x80

section .data
trampoline:
    call shellcode
"
", so we need to move that pointer to ebx

Entonces necesitamos apuntar ECX a una matriz de char* como ["/bin/sh", 0] . Ya tenemos una parte de ella en EBX , así que continuando con nuestro código de shell podemos hacer lo siguiente:

char shellcode[] = "\x31\xc0\x50\x68\x6e\x2f\x73\x68\x68\x2f\x2f\x62\x69\x89\xe3\x50\x53\x89\xe1\xb0\x0b\xcd\x80";
int main(){
    (*(void(*)())shellcode)();
    return 0;
}

Finalmente, deberíamos establecer el registro AL en 0xb y hacer el syscall. Así que nuestro código de shell final debería tener este aspecto:

$ ./shellcode.o 
sh-4.4$ 

Podemos compilarlo nasm nasm -o shellcode.bin -f elf32 -O0 shellcode.nasm , extraer los códigos de operación y colocarlos en el código C para probarlo:

[----------------------------------registers-----------------------------------]
EAX: 0x5655700b --> 0xde3050f7 
EBX: 0x5655550c (<main+35>: mov    eax,0x0)
ECX: 0x0 
EDX: 0x0 
ESI: 0xf7f9fe24 --> 0x1d6d2c 
EDI: 0xf7f9fe24 --> 0x1d6d2c 
EBP: 0xffffd9d8 --> 0x0 
ESP: 0xffffd9c0 --> 0xf7fe5ae0 (<_dl_fini>: push   ebp)
EIP: 0x5655701f --> 0x1b080cd
EFLAGS: 0x246 (carry PARITY adjust ZERO sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
   0x5655701a <shellcode+2>:    pop    ebx
   0x5655701b <shellcode+3>:    xor    ecx,ecx
   0x5655701d <shellcode+5>:    xor    edx,edx
=> 0x5655701f <shellcode+7>:    int    0x80
   0x56557021 <shellcode+9>:    mov    al,0x1
   0x56557023 <shellcode+11>:   xor    bl,bl
   0x56557025 <shellcode+13>:   int    0x80
   0x56557027 <shellcode+15>:   add    BYTE PTR [eax],al
[------------------------------------stack-------------------------------------]
0000| 0xffffd9c0 --> 0xf7fe5ae0 (<_dl_fini>:    push   ebp)
0004| 0xffffd9c4 --> 0x0 
0008| 0xffffd9c8 (")UUV0pUV$617$617")
0012| 0xffffd9cc --> 0x56557018 --> 0x315b0bb0 
0016| 0xffffd9d0 --> 0xf7f9fe24 --> 0x1d6d2c 
0020| 0xffffd9d4 --> 0xf7f9fe24 --> 0x1d6d2c 
0024| 0xffffd9d8 --> 0x0 
0028| 0xffffd9dc --> 0xf7de3141 (<__libc_start_main+241>:   add    esp,0x10)
[------------------------------------------------------------------------------]

Compílalo con tu compilador favorito, yo uso gcc gcc -o shellcode.o -fno-stack-protector -z execstack -m32 shellcode.c . Y ejecútalo:

xor eax, eax //Clear the eax register so we have a null byte to end our string
push eax
push "n/sh" //The string needs to be written "backwards"
push "//bi" //The double "/" is to avoid null bytes in our shellcode
mov ebx, esp //esp is pointing to "//bin/sh
xor eax, eax 
push eax
push "n/sh"
push "//bi"
mov ebx, esp
push eax // Remember it's still 0 from our previous xor eax, eax
push ebx // Push it so ESP points to EBX
mov ecx, esp // move ESP to ECX, the result is ECX -> EBX -> "//bin/sh
section .text
global _start
_start:
    jmp trampoline

shellcode:
    xor eax, eax
    push eax
    push "n/sh"
    push "//bi"
    mov ebx, esp
    push eax
    push ebx
    mov ecx, esp
    mov al,11
    int 0x80

section .data
trampoline:
    call shellcode
"
", so we need to move that pointer to ebx
    
respondido por el Mr. E 11.08.2018 - 17:13
fuente

Lea otras preguntas en las etiquetas