Serial Fishing

3, março, 2009 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:

False Disassembly

20, janeiro, 2009 3 comentários

Olá galera.! Como meu primeiro post no blog irei falar sobre um método que irá dificultar o disassembly de um binário.

O método cria um Falso Disassembly, que irá confundir os disassemblers gerando outras instruções diferente da que irá executar colocando outros bytes juntos com o opcode correto. O código abaixo é um exemplo de um simples exit sem esse método.

1
2
3
4
5
6
7
8
.section .text
.globl _start
_start:
          xor %eax,%eax
          push %eax
          mov $0x1,%al
          push %eax
          int $0x80
c0d3labs# objdump -d normal_exit

normal_exit:     file format elf32-i386-freebsd

Disassembly of section .text:

08048074 :
 8048074:       31 c0                   xor    %eax,%eax
 8048076:       50                      push   %eax
 8048077:       b0 01                   mov    $0x1,%al
 8048079:       50                      push   %eax
 804807a:       cd 80                   int    $0x80
c0d3labs#

Agora, no exemplo abaixo, quando o disassembler for mostrar o código, irá juntar os opcodes “\xc0\xc9″ com “\x31″, que é o começo do “xor %eax,%eax”, que faz a instrução “ror $0×31,%cl”(opcode “\xc0\xc9\x31″). Isso aconteçe porque a instrução “ror”(opcode “\xc0″) recebe dois argumentos. Com isso o “\xc0″, que é o final do “xor %eax,%eax”, é ignorado e passa a ser a proxima instrução.

A chave principal dessa idéia é alterar o entry point. No caso iremos aumentar em dois para cair diretamente no “\x31\xc0″, no caso executar o “xor %eax,%eax” e, assim, ignorar o “\xc0\xc9″ que foi inserido só para confundir o disassembly.

E, em seguida, executar o “jmp . + 4″ (opcode “\xeb\x02″), que é para pular do endereço atual mais quatro. Isso para ignorar o “\xc9\xc0″ que foi inserido junto com “\xb0\x01″ para o mesmo propósito, e cair diretamente no “mov $0×1,%al”(opcode “\xb0\x01″) e assim seguir adiante.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
.section .text
.globl _start
_start:
         .byte 0xc0
         .byte 0xc9
         xor %eax,%eax
         push %eax
         jmp . + 4
         .byte 0xc0
         .byte 0xc9
         mov $0x1,%al
         jmp . + 4
         .byte 0xc9
         .byte 0xc0
         push %eax
         jmp . + 4
         .byte 0xc0
         .byte 0xc9
         int $0x80
c0d3labs# objdump -d false_disassembly

false_disassembly:     file format elf32-i386-freebsd

Disassembly of section .text:

08048074 <_start>:
8048074:       c0 c9 31                ror    $0x31,%cl
8048077:       c0 50 eb 02             rclb   $0x2,0xffffffeb(%eax)
804807b:       c0 c9 b0                ror    $0xb0,%cl
804807e:       01 eb                   add    %ebp,%ebx
8048080:       02 c9                   add    %cl,%cl
8048082:       c0 50 eb 02             rclb   $0x2,0xffffffeb(%eax)
8048086:       c0 c9 cd                ror    $0xcd,%cl
8048089:       80                      .byte 0x80
c0d3labs#

Para alterar o entry point, você pode mudar manualmente via qualquer editor hexadecimal no offset 0×18 ou usar esse code.

Todos os testes foram feito em um FreeBSD versão 6.2.

Até mais!

`hacknroll`

Categories: Sem categoria Tags:

return! Sim ou Não?

9, janeiro, 2009 11 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 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: