Come scrivere un buffer-overflow exploit in GCC, Windows XP x86?
-
23-09-2019 - |
Domanda
void function(int a, int b, int c) {
char buffer1[5];
char buffer2[10];
int *ret;
ret = buffer1 + 12;
(*ret) += 8;//why is it 8??
}
void main() {
int x;
x = 0;
function(1,2,3);
x = 1;
printf("%d\n",x);
}
È possibile che questo demo è da qui:
http://insecure.org/stf/smashstack.html
Ma non funziona qui:
D:\test>gcc -Wall -Wextra hw.cpp && a.exe
hw.cpp: In function `void function(int, int, int)':
hw.cpp:6: warning: unused variable 'buffer2'
hw.cpp: At global scope:
hw.cpp:4: warning: unused parameter 'a'
hw.cpp:4: warning: unused parameter 'b'
hw.cpp:4: warning: unused parameter 'c'
1
E non capisco perché è 8 se l'autore pensa:
Un po 'di matematica ci dice la distanza è 8 byte.
Il mio gdb discarica come richiesto:
Dump of assembler code for function main:
0x004012ee <main+0>: push %ebp
0x004012ef <main+1>: mov %esp,%ebp
0x004012f1 <main+3>: sub $0x18,%esp
0x004012f4 <main+6>: and $0xfffffff0,%esp
0x004012f7 <main+9>: mov $0x0,%eax
0x004012fc <main+14>: add $0xf,%eax
0x004012ff <main+17>: add $0xf,%eax
0x00401302 <main+20>: shr $0x4,%eax
0x00401305 <main+23>: shl $0x4,%eax
0x00401308 <main+26>: mov %eax,0xfffffff8(%ebp)
0x0040130b <main+29>: mov 0xfffffff8(%ebp),%eax
0x0040130e <main+32>: call 0x401b00 <_alloca>
0x00401313 <main+37>: call 0x4017b0 <__main>
0x00401318 <main+42>: movl $0x0,0xfffffffc(%ebp)
0x0040131f <main+49>: movl $0x3,0x8(%esp)
0x00401327 <main+57>: movl $0x2,0x4(%esp)
0x0040132f <main+65>: movl $0x1,(%esp)
0x00401336 <main+72>: call 0x4012d0 <function>
0x0040133b <main+77>: movl $0x1,0xfffffffc(%ebp)
0x00401342 <main+84>: mov 0xfffffffc(%ebp),%eax
0x00401345 <main+87>: mov %eax,0x4(%esp)
0x00401349 <main+91>: movl $0x403000,(%esp)
0x00401350 <main+98>: call 0x401b60 <printf>
0x00401355 <main+103>: leave
0x00401356 <main+104>: ret
0x00401357 <main+105>: nop
0x00401358 <main+106>: add %al,(%eax)
0x0040135a <main+108>: add %al,(%eax)
0x0040135c <main+110>: add %al,(%eax)
0x0040135e <main+112>: add %al,(%eax)
End of assembler dump.
Dump of assembler code for function function:
0x004012d0 <function+0>: push %ebp
0x004012d1 <function+1>: mov %esp,%ebp
0x004012d3 <function+3>: sub $0x38,%esp
0x004012d6 <function+6>: lea 0xffffffe8(%ebp),%eax
0x004012d9 <function+9>: add $0xc,%eax
0x004012dc <function+12>: mov %eax,0xffffffd4(%ebp)
0x004012df <function+15>: mov 0xffffffd4(%ebp),%edx
0x004012e2 <function+18>: mov 0xffffffd4(%ebp),%eax
0x004012e5 <function+21>: movzbl (%eax),%eax
0x004012e8 <function+24>: add $0x5,%al
0x004012ea <function+26>: mov %al,(%edx)
0x004012ec <function+28>: leave
0x004012ed <function+29>: ret
Nel mio caso la distanza dovrebbe essere -? = 5, giusto Ma non sembra lavorare ..
Perché esigenze function
56 byte per le variabili locali? (sub $0x38,%esp
)
Soluzione
joveha sottolineato , il valore di PEI salvato nello stack (indirizzo di ritorno) dall'istruzione call
deve essere incrementato di 7 byte (0x00401342
- 0x0040133b
= 7 ) per saltare l'istruzione x = 1;
(movl $0x1,0xfffffffc(%ebp)
).
Lei ha ragione che 56 byte sono riservati per le variabili locali (sub $0x38,%esp
), in modo che il pezzo mancante è il numero di byte buffer1
passato sullo stack è l'EIP salvato.
Un po 'di codice di test e assemblaggio in linea mi dice che il valore di magia è 28 per la mia prova. Non posso fornire una risposta definitiva sul perché è 28, ma vorrei assumere il compilatore è l'aggiunta di imbottitura e / o di canarini pila .
Il seguente codice è stato compilato utilizzando GCC 3.4.5 (MinGW) e testato su Windows XP SP3 (x86).
unsigned long get_ebp() {
__asm__("pop %ebp\n\t"
"movl %ebp,%eax\n\t"
"push %ebp\n\t");
}
void function(int a, int b, int c) {
char buffer1[5];
char buffer2[10];
int *ret;
/* distance in bytes from buffer1 to return address on the stack */
printf("test %d\n", ((get_ebp() + 4) - (unsigned long)&buffer1));
ret = (int *)(buffer1 + 28);
(*ret) += 7;
}
void main() {
int x;
x = 0;
function(1,2,3);
x = 1;
printf("%d\n",x);
}
ho potuto avere gdb altrettanto facilmente utilizzato per determinare questo valore.
(compilato w / -g
per includere i simboli di debug)
(gdb) break function
...
(gdb) run
...
(gdb) p $ebp
$1 = (void *) 0x22ff28
(gdb) p &buffer1
$2 = (char (*)[5]) 0x22ff10
(gdb) quit
(0x22ff28
+ 4) - 0x22ff10
= 28
(valore EBP + dimensione della parola) - indirizzo del buffer1 = numero di byte
Oltre a Smashing La Pila per divertimento e profitto , vorrei suggerire la lettura di alcune delle gli articoli che ho citato in mia risposta ad una precedente interrogazione di vostro e / o altro materiale sull'argomento. Avere una buona comprensione di come esattamente questo tipo di exploit opere dovrebbe aiutare a scrivere codice più sicuro .
Altri suggerimenti
E 'difficile prevedere cosa buffer1 + 12
fa veramente. Il compilatore può mettere buffer1
e buffer2
in qualsiasi posizione sulla pila che desidera, andando anche al punto di non risparmiare spazio per buffer2
a tutti. L'unico modo per sapere davvero dove buffer1
va è quello di guardare l'output del compilatore assembler, e c'è una buona possibilità che sarebbe saltato in giro con diverse impostazioni di ottimizzazione o di differenti versioni dello stesso compilatore.
Non testare il codice sulla mia macchina ancora, ma vi ho preso l'allineamento di memoria in considerazione? Prova a smontare il codice con gcc. Penso che un codice assembly può darvi una maggiore comprensione del codice. : -)
Questo codice stampa i 1 e su OpenBSD e FreeBSD, e dà un errore di segmentazione su Linux.
Questo tipo di exploit dipende fortemente sia l'insieme di istruzioni della particolare macchina, e le convenzioni di chiamata del sistema operativo e compilatore. Tutto ciò che riguarda il layout dello stack è definito dall'implementazione, non il linguaggio C. L'articolo si presuppone Linux su x86, ma sembra che si sta usando Windows, e il sistema potrebbe essere a 64 bit, anche se è possibile passare gcc a 32-bit con -m32
.
I parametri si dovrà modificare sono 12, che è l'offset dalla punta dello stack per l'indirizzo di ritorno, e 8, che è il numero di byte di main
si vuole saltare. Come dice l'articolo, è possibile utilizzare gdb per ispezionare lo smontaggio della funzione per vedere (a) fino a che punto lo stack viene spinto quando si chiama function
, e (b) gli offset di byte delle istruzioni in main
.
La parte +8 byte è di quanto vuole l'EIP salvato al incrementato con. Il PEI è stato salvato in modo che il programma potrebbe tornare all'ultima assegnazione dopo la function
è fatto -. Ora vuole saltare con l'aggiunta di 8 byte per l'EIP salvato
Quindi, tutto cerca di è "saltare" il
x = 1;
Nel tuo caso il PEI salvato punterà 0x0040133b
, la prima istruzione dopo ritorna function
. Per saltare l'assegnazione è necessario fare il punto EIP salvato 0x00401342
. Ecco 7 byte.
E 'davvero un "pasticcio con RET EIP", piuttosto che un esempio di buffer overflow.
E per quanto riguarda i 56 byte per le variabili locali va, che potrebbe essere qualsiasi cosa il tuo compilatore esce con imbottitura come, canarini pila, ecc.
Modifica:
Questo dimostra quanto sia difficile fare buffer overflow esempi in C. L'offset di 12 da buffer1
assume un certo stile imbottitura e compilare le opzioni. GCC sarà lieto di inserire canarini pila al giorno d'oggi (che diventa una variabile locale che "protegge" l'EIP salvato) a meno che non gli si dice di non farlo. Inoltre, il nuovo indirizzo che vuole saltare (le istruzioni di avvio per la chiamata printf
) ha davvero essere risolto manualmente dal gruppo. Nel suo caso, il suo machie, con il suo sistema operativo, con il suo compilatore, in quel giorno .... è stato 8.
Si sta compilando un programma C con il compilatore C ++. Rinominare hw.cpp per hw.c e lo troverete compilerà.