Mais um blog inútil.

Cracking

Dezembro 26, 2011

Como não ganhar um iPad 2

Arquivado em: coding, cracking, drama, fail, useless — falso @ 03:09

Boas noites,

Venho hoje aqui blogar a minha tristeza ao saber que a password do concurso para ganhar um iPad 2 no Pplware é alta string manhosa.

Vou começar então a falar da minha jornada para tentar ganhar isto…

Comecei por andar a procurar qual era o software que usava a extensão “xcon”, um amigo meu descobriu entretanto que era o Conceal, um software todo manhoso em .NET.
Experimentei umas passwords básicas no programa só para ver se advinhava antes de fazer alguma coisa mais complexa, mas o programa era tão manhoso que se usasse uma password invalida ele crashava, então desisti dessa aproximação.

Falei com um amigo meu todo cromo da criptografia e ele automagicamente disse-me que aquilo eram blocos de TripleDES CBC.

Caso os senhores do Pplware não saibam, bruteforcar TripleDes não é assim pêra doce, citando a Wikipedia (está sempre correcta :-P):

“Deep Crack was designed by Cryptography Research, Inc., Advanced Wireless Technologies and the EFF. …  Advanced Wireless Technologies built 1856 custom ASICDES chips housed on 29 circuit boards of 64 chips each. The boards were then fitted in six cabinets and mounted in a Sun-4/470 chassis. … The entire machine was capable of testing over 90 billion keys per second. It would take about 9 days to test every possible key at that rate. On average, the correct key would be found in half that time.

Entretanto, eu sem sabendo que era alta string manhosa, e pensando que a password poderia ser o titulo de algum dos produtos entre 50 e 100€, fiz um scriptzinho que ia la parsar o site do OfficeLan e sacar de la os títulos todos (faz uso do phpQuery):

<head>
  <meta http-equiv="content-type" content="text/html; charset=utf-8" />
</head>
<?php

require('phpQuery.php');

// batota, tem o total hardcoded
for($start = 0; $start <= 120; $start = $start + 20) {
	$url = 'http://shop.officelan.pt/pesquisa.html?start='.$start;
	$fields = array(
		'yagendoo_price_min'=>urlencode('50'),
		'yagendoo_price_max'=>urlencode('100'),
		'option'=>urlencode('com_yagendoo_vmsearch'),
	);
	$fields_string = "";
	foreach($fields as $key=>$value) { $fields_string .= $key.'='.$value.'&'; }
	rtrim($fields_string,'&');

	$ch = curl_init();
	curl_setopt($ch,CURLOPT_URL,$url);
	curl_setopt($ch,CURLOPT_POST,count($fields));
	curl_setopt($ch,CURLOPT_POSTFIELDS,$fields_string);
	curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

	$result = curl_exec($ch);

	curl_close($ch);

	$doc = phpQuery::newDocument($result);
	phpQuery::selectDocument($doc);

	foreach(pq('span.yagendoo_searchresult_title a') as $product) {
			echo $product->nodeValue;
			echo '<br/>';
	}
}

E com o resultado fui experimentar o seguinte:

<?php

$produtos = array(
  'MikroTik RouterBOARD 411U (Level 4) - RB411U - RB/411U',
  'MikroTik RouterBOARD 411AR (Level 4) - RB411AR - RB/411AR',
  ...
  'ANTENA INTERLINE SECTORIAL 12dBi/2.4GHz Mimo',
  'ANTENA INTERLINE SECTORIAL 12dBi/2.4GHz',
  'Presente', 'Presente_Natal', 'Presente_Natal.zip',
  'OfficeLan', 'pplware', 'natal',
);

$string = file_get_contents('nome_produto_offiLan.xcon');
foreach($produtos as $key) {
	$output = mcrypt_decrypt( MCRYPT_3DES , $key , $string , 'cbc');
	echo '<h4>'.$key.'</h4>';
	echo '<pre>'.$output.'</pre>';
}

Mas sem grandes resultados… Então foi que me disseram que o TripleDES CBC alem de uma key na cifra usa também outra variável, que é o IV (Initialization vector), que pelo que entendi, são dados usados para “inicializar” a cifra.
Então o tal programa tinha de usar algum algoritmo para “gerar” um IV a partir da nossa password ou então usava algum valor fixo, decidi tentar descobrir.

Andei a procura de técnicas sobre reversing a cenas .NET e achei alta programinha hacker, .NET Reflector.
Neste belo software, abre-se um executável .NET e ele escreve mais ou menos o codigo desse executável em belíssimo código C#, só os nomes de algumas funções e variáveis é que se perdem…

Então abri o executável do Conceal, e andei la a vasculhar, e no Form2 achei lá uma função chamada tdes_decrypt() e vi que era a função desejada, mas que a key e o IV eram calculados noutro lado, então com outra feature bonita deste programa, fiz Analyze nessa função e vi que era chamada pela func2(), e BINGO nessa achei o algoritmo que era usado para a key e o IV.

Decidi então criar um novo projecto C# no Visual Studio, onde iria utilizar o código do programa gerado pelo Reflector, mas podia-lhe alimentar um array gigante de passwords (a lista de produtos anterior). E com poucas dificuldades consegui mete-lo a funcionar, mas rapidamente vi que nenhuma das passwords que estava tentar usar era a correcta.
Sem saber mais o que tentar, rapidamente desisti…

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Security.Cryptography;
using System.IO;

namespace unconceal
{
    class Program
    {
        public static void tdes_decrypt(String inputfile, String outputfile, byte[] key, byte[] IV)
        {
            long bytecount = 0L;
            long fread_len = 0L;
            int numbytes = 0;
            byte[] mybuffer = new byte[0xf4241]; 
            TripleDESCryptoServiceProvider tdesProvider;
            CryptoStream cryptStream;

            FileStream freader = new FileStream(inputfile, FileMode.Open, FileAccess.ReadWrite);
            FileStream fwriter = new FileStream(outputfile, FileMode.OpenOrCreate, FileAccess.Write);

            fread_len = freader.Length;

            tdesProvider = new TripleDESCryptoServiceProvider();
            tdesProvider.Key = key;
            tdesProvider.IV = IV;
            tdesProvider.Padding = PaddingMode.Zeros;

            cryptStream = new CryptoStream(fwriter, tdesProvider.CreateDecryptor(), CryptoStreamMode.Write);
            int num = 0;
            num = inputfile.Length - 1;

            while (bytecount < fread_len)
            {
                numbytes = freader.Read(mybuffer, 0, 0xf4240);
                cryptStream.Write(mybuffer, 0, numbytes);
                bytecount += numbytes;
            }

            while (bytecount % 8 != 0)
            {
                cryptStream.WriteByte(0);
                bytecount++;
            }

            freader.Close();
            cryptStream.Close();
        }

        static void Main(string[] args)
        {
            List<string> passwords = new List<string>(new string[]
	        {
                "MikroTik RouterBOARD 411U (Level 4) - RB411U - RB/411U",
                "MikroTik RouterBOARD 411AR (Level 4) - RB411AR - RB/411AR",
                ...
                "ANTENA INTERLINE SECTORIAL 12dBi/2.4GHz Mimo",
                "ANTENA INTERLINE SECTORIAL 12dBi/2.4GHz",
                "Presente",
                "Presente_Natal",
                "Presente_Natal.zip",
                "OfficeLan",
                "pplware",
                "natal",
                "Natal",
                "iPad2",
                "Natal 2011",
                "PPLWARE.COM",
                "EBA428ECA16691133FA946FED56AF824E8527BB6",
                "_F2Liz12!" // password correct, mas só agora :(
        	});

            int x = 0;
            foreach (String for_keyf1 in passwords)
            {
                String password = for_keyf1.ToString();

                byte[] bytes = new byte[0x2710];
                int charIndex = 0;
                int length = for_keyf1.Length;
                byte[] buffer2 = new byte[length + 1];
                int index = 0;
                int num4 = 0;
                int num7 = length - 1;
                for (index = 0; index <= num7; index++)
                {
                    num4 += 3;
                    buffer2[index] = (byte)(Convert.ToInt64(for_keyf1[index]) + (index + num4));
                    num4--;
                }
                new ASCIIEncoding().GetBytes(Encoding.ASCII.GetString(buffer2), charIndex, Encoding.ASCII.GetString(buffer2).Length, bytes, charIndex);
                byte[] buffer3 = new SHA1CryptoServiceProvider().ComputeHash(bytes);

                byte[] key = new byte[24];
                key[0] = buffer3[2];
                key[1] = buffer3[6];
                key[2] = buffer3[0x12];
                key[3] = buffer3[0x10];
                key[4] = buffer3[0x13];
                key[5] = buffer3[1];
                key[6] = buffer3[9];
                key[7] = buffer3[7];
                key[8] = buffer3[14];
                key[9] = buffer3[3];
                key[10] = buffer3[8];
                key[11] = buffer3[0x11];
                key[12] = buffer3[10];
                key[13] = buffer3[15];
                key[14] = buffer3[0];
                key[15] = buffer3[11];
                key[0x10] = buffer3[9];
                key[0x11] = buffer3[4];
                key[0x12] = buffer3[0x12];
                key[0x13] = buffer3[5];
                key[20] = buffer3[11];
                key[0x15] = buffer3[2];
                key[0x16] = buffer3[0x13];
                key[0x17] = buffer3[0];

                byte[] iv = new byte[8];
                iv[0] = buffer3[2];
                iv[1] = buffer3[6];
                iv[2] = buffer3[0x12];
                iv[3] = buffer3[0x10];
                iv[4] = buffer3[0x13];
                iv[5] = buffer3[1];
                iv[6] = buffer3[9];
                iv[7] = buffer3[7];

                tdes_decrypt(@"C:\Users\falso\Documents\Visual Studio 2010\Projects\unconceal\unconceal\nome_produto_offiLan.xcon",
                    @"C:\Users\falso\Documents\Visual Studio 2010\Projects\unconceal\unconceal\" + x.ToString() + ".txt",
                    key, iv);

                string text = System.IO.File.ReadAllText(@"C:\Users\falso\Documents\Visual Studio 2010\Projects\unconceal\unconceal\" + x.ToString() + ".txt");
                System.Console.WriteLine("String = {0}", text);
                x++;
            }
        }
    }
}

Hoje dia 26 de Dezembro, lembrei-me de ir ver qual era afinal a password do concurso, e foi então que descobri que era “_F2Liz12!”, adicionei essa string ao meu programinha em C# e não é que funcionou?

Download do projecto unconceal (belo nome :-P) para Visual Studio, para caso alguém esteja interessado em brincar mais com isto. Não esquecer os caminhos que estão hardcoded ao chamar a função tdes_decrypt().

Acho que para a próxima os senhores do Pplware deviam fazer concursos mais bem pensados, onde ganhe quem acha o resultado final, e não quem escolhe um produto à sorte que esteja no intervalo de preços dito inicialmente (dor de corno).

Um abraço e até à próxima!

Maio 21, 2010

Cracking 102

Arquivado em: assembly, cracking, fail, useless — falso @ 20:21

Ora viva amigos!
Há uns dias lembrei-me do mítico Championship Manager Itália 94/95, e das múltiplas tentativas que fazia para o conseguir jogar devido à protecção anti-copia, que basicamente pedia o resultado do jogo X da pagina Y do manual.

Então lá eu ficava montes de tempo a experimentar combinações ate conseguir jogar. Nessa altura sonhava em conseguir crackar aquilo, mas skills de cracking nesse tempo eram quase nulos. Decidi então tentar hoje.

Abri o CM.EXE num dissassembler e procurei por “Please enter the result”, e vi onde essa string era referenciada, fui parar a uma função que me parecia que era onde era feito o check dos resultados, mas parecia um bocado complexa portanto decidi tentar outra aproximação, fui ver onde esta função do check era chamada e…

seg001:0054                         loc_18FE4:                              ; CODE XREF: _main+47j
seg001:0054 9A 25 00 E4 61                          call    sub_61E65       ; Função do check
seg001:0059
seg001:0059                         loc_18FE9:                              ; CODE XREF: _main+99j
seg001:0059 1E                                      push    ds
seg001:005A 68 18 01                                push    offset aChampionshipMa ; "Championship Manager Italia"
seg001:005D 9A F9 48 83 1A                          call    sub_1F129       ; Call Procedure
seg001:0062 1E                                      push    ds
seg001:0063 68 35 01                                push    offset aHelpNewGameCon ; "*Help|New Game|Continue Season|Quick St"...

Vi que o código a seguir ao CALL parecia ser o menu inicial do jogo então alterei os 5 bytes do CALL para 90 (NOP - No OPeration) e não é que foi logo? A parte do check já nem aparece, vai logo directamente para o menu inicial!
Melhor que isto só mesmo o Benfica ser campeão, oh wait…

Agosto 25, 2009

printf("oi lindinhos!\n");

Arquivado em: assembly, coding, cracking, useless — charlie-lindinho @ 20:06

há mil dias li um post do *xorl* intitulado de “Funny C programming interview questions”, achei uma certa piada ao raio do post e resolvi escrever aqui sobre isso.

no post o xorl apresenta a solução para uma das questões colocadas neste *site*, no qual nos é questionado se é possível fazer com que a variável “i” apresente outro valor, senão “2”, apenas adicionando código à função foo().

#include <stdio.h>
#include <stdlib.h>

void foo(void);

int main (void)
{
	int i=2;
	foo();
	printf ("%d\n",i);
	return EXIT_SUCCESS;
}

void foo(void)
{
	// add code so that printf above prints different value of i (i.e. not 2)
} 

a primeira coisa que me ocorreu (em mil milésimos de segundo) foi modificar directamente o valor de “i” através da função “printf” e então fiz o seguinte :

#include <stdio.h>
#include <stdlib.h>

void foo(void);

int main(void)
{
	int i = 2;
	foo();
	printf("%d\n", i);
	return EXIT_SUCCESS;
}

void foo(void)
{
	unsigned long addr;
	__asm("movl %%ebp, %0" : : "m"(addr));
	printf("%1$n", addr += 24);
}

o que fiz aqui foi calcular o endereço da variável “i” apartir do base pointer (EBP) e fazer com que a funçao printf escrevesse 0 bytes no endereço da mesma através do especificador de formato “%n”.

bash-3.1# gcc -o printf printf.c
bash-3.1# ./printf
0
bash-3.1# 

ainda assim existe outra solução óbvia da qual me lembrei também, que consiste em fechar o file descriptor utilizado pela função printf ( stdout ) depois da função foo().

void foo(void)
{
	fprintf(stderr, "%d\n", 0);
	close(1);
}

queria dedicar este post inútil ao cavalo que morreu hoje atropelado no brasil.

Agosto 21, 2009

oi lindinhos do craking

Arquivado em: coding, cracking, useless — charlie-lindinho @ 11:07

aqui há tempos ( há mil dias ) prometi ao david publicar umas merdas sobre anti-debugging
e ao pedro enviar um crackme, mas em vez disso ( e porque sou uma merda )
resolvi apresentar aqui um exemplo de fácil compreensão e a devida explicação.

aqui vai disto :

root@inferno:[~/dev/cracking]# gdb crackme
(no debugging symbols found)
(gdb) set disassembly-flavor intel
(gdb) set write
(gdb) show write
Writing into executable and core files is on.
(gdb) file crackme
Reading symbols from /root/dev/cracking/crackme…(no debugging symbols found)…done.

(gdb) disas _start
Dump of assembler code for function _start:
0x08048060 <_start+0>: pop eax
0x08048061 <_start+1>: dec eax
0x08048062 <_start+2>: and eax,eax
0x08048064 <_start+4>: je 0x8048080

o valor de argc é copiado para o registo EAX, em seguida este é decrementado,
pois se o crackme for executado sem argumentos o valor de argc será 1.

em seguida é realizada a operaçao lógica AND com o propósito de verificar
se o valor que reside em EAX ( argc ) é zero, caso seja, a função exit é então executada.

0x08048066 <_start+6>: mov bl,0x3
0x08048068 <_start+8>: inc eax
0x08048069 <_start+9>: and eax,ebx
0x0804806b <_start+11>: jne 0x8048080
End of assembler dump.

aqui é atribuido o valor 3 ao registo EBX
e o registo EAX, ao ser incrementado, contém agora o valor real de argc.

por fim é novamente realizada a operação lógica AND com os registos EAX e EBX.
se estes forem diferentes a função exit é executada, caso contrário
( se o numero de argumentos corresponder a 3 ) passamos à função done.

(gdb) disas done
Dump of assembler code for function done:
0x0804806d <done+0>: xor ebx,ebx
0x0804806f <done+2>: push 0xa
0x08048071 <done+4>: push 0x656e6f64
0x08048076 <done+9>: mov al,0x4
0x08048078 <done+11>: mov bl,0x1
0x0804807a <done+13>: mov dl,0x5
0x0804807c <done+15>: mov ecx,esp
0x0804807e <done+17>: int 0x80
End of assembler dump.
(gdb) disas exit
Dump of assembler code for function exit:
0x08048080 <exit+0>: push $0x1
0x08048082 <exit+2>: pop %eax
0x08048083 <exit+3>: int $0x80
End of assembler dump.

(gdb) r 1 2 3
Starting program: /root/dev/cracking/crackme 1 2 3
(no debugging symbols found)
done

Program exited with code 01.

(gdb) x/4b _start+4
0x8048064 <_start+4>: 0x74 0x1a 0xb3 0x03
(gdb) p 0x1a
$1 = 26

( onde 0x74 é o opcode da instrução JE e 0x1a, 26 em decimal. )

ou seja, para efectuar o “je done” em vez de “je exit”
basta-nos calcular a distância em bytes entre a instrução JE e a função done, que é de 7 bytes.

(gdb) p 11-4
$2 = 7

uma vez que temos permissão para escrever no executável,
vamos então modificar um byte no segmento de código do mesmo
e posteriormente executá-lo sem argumentos :

(gdb) set *0x08048064 = 0x03b30774

(gdb) x/4b 0x08048064
0x8048064 <_start+4>: 0x74 0x07 0xb3 0x03
(gdb) x/i 0x08048064
0x8048064 <_start+4>: je 0x804806d

(gdb) set write off
(gdb) file crackme
Reading symbols from /root/dev/cracking/crackme…(no debugging symbols found)…done.
(gdb) run
Starting program: /root/dev/cracking/crackme
(no debugging symbols found)
done

Program exited with code 01.
(gdb) q
root@inferno:[~/dev/cracking]#

como podemos verificar, foi-nos apresentada a mensagem “done”, cumprindo assim o objectivo deste crackme.
deixo-vos aqui uma pequena merda que fiz em hepatite C para facilitar ainda mais a tarefa.

agora vou beber mil cervejas para esquecer que escrevi isto e vou dormir.

[ crackme-patch.c ]

#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>FILE *fp;

int main(int argc, char **argv){

unsigned int offset;
char *file = argv[1];

struct stat st;

if(stat(file, &st))
return EXIT_FAILURE;

fp = fopen(file, "r+");
offset = st.st_size;

while (offset) {

fseek(fp, offset, SEEK_SET);

if (fgetc(fp) == 0x1a)
patch(offset);

offset--;

}

fclose(fp);

return EXIT_SUCCESS;
}

int patch(int num){

printf("patching ... ");

fseek(fp, num, SEEK_SET);
fputc(0x07, fp);

printf("done.\n");

}

root@inferno:[~/dev/cracking]# nasm -f elf crackme.asm && ld -o crackme crackme.o
root@inferno:[~/dev/cracking]# ./crackme
root@inferno:[~/dev/cracking]# gcc -o crackme-patch crackme-patch.c
root@inferno:[~/dev/cracking]# ./crackme-patch ./crackme
patching ... done.
root@inferno:[~/dev/cracking]# ./crackme
done
root@inferno:[~/dev/cracking]#

PS: Este post foi feito plo MaDiNfO, já que o o charlie é uma merda e nem login consegue fazer com o seu proprio nick... (firefail dramas)

Julho 31, 2009

kgme1

Arquivado em: assembly, cracking, linux, useless — falso @ 01:32

Ora viva!

Já há uns tempos que tinha falado com o Dongs para fazer um KeygenMe para eu tentar keygennar porque nunca o tinha feito. Ele então fez um e eu pus mãos à obra.

[ kgme.tar.bz2 ]

$ ./kgme
Name: falso
Serial: 12345
Bad serial!

Abri então o ficheiro no IDA e andei a procurar por “Bad serial!”, e então que achei. Vai aqui um dump comentado:

0804830C	movzx   ecx, byte ptr [esi] ; ESI contem o "User"
0804830C                                ; e ECX contem o primeiro char do "User"
0804830F	test    ecx, ecx        ; Se (ECX != 0x0) ZF=0 ELSE ZF=1
08048311	jz      short loc_8048333 ; Se ZF=1 Salta pro drama
08048313	lea     edx, [ebp-10Fh] ; EDX fica com "User" sem o primeiro char
08048319	mov     ebx, 0DEADh
0804831E	db      66h
0804831E	nop                     ; No Operation
08048320
08048320 loc_8048320:
08048320	mov     eax, ebx        ; EAX passa a tar 0xDEAD tambem
08048322	shl     eax, 5          ; Shift Logical Left 5 vezes
08048325	add     eax, ecx        ; Adiciona o valor do char do "User" (ECX) a EAX
08048327	movzx   ecx, byte ptr [edx] ; le o proximo char pra ECX
0804832A	add     edx, 1          ; Add
0804832D	add     ebx, eax        ; Add
0804832F	test    ecx, ecx        ; Logical Compare
08048331	jnz     short loc_8048320 ; Jump if Not Zero (ZF=0)
08048333
08048333 loc_8048333:
08048333	xor     edx, edx        ; Logical Exclusive OR
08048335	mov     eax, offset aBadSerial ; "Bad serial!"
0804833A	cmp     [ebp-10h], ebx  ; Compare Two Operands
0804833D	setz    dl              ; Set Byte if Zero (ZF=1)
08048340	xor     eax, offset aGoodSerial ; "Good serial!"
08048345	neg     edx             ; Two's Complement Negation
08048347	and     eax, edx        ; Logical AND
08048349	xor     eax, offset aBadSerial ; "Bad serial!"

Então o funcionamento da coisa é mais ou menos o seguinte:

/\* inicialização(?) */
\* Le o Name para o registo ESI
\* Mete o primeiro char do Name no registo ECX
\* EDX fica com o Name sem o primeiro char
\* EBX fica com o valor 0xDEAD

/\* ciclo central */
\* Copia o valor de EBX para EAX
\* Faz um shift left 5 vezes a EAX
\* Adiciona o valor do char em ECX a EAX
\* Le o proximo char do Name para ECX
\* Adiciona 1 a EDX
\* Adiciona EAX a EBX
\* Se ECX nao for diferente de 0 salta para o inicio do ciclo

Vou tentar dar um exemplo:

User: Jim

/\* inicialização */
\* ESI = Jim
\* ECX = J (0x4a)
\* EDX = im
\* EBX = 0xdead

( valores dos registos )
-- ECX = J | EDX = im | EBX = 0xdead –

/\* ciclo central */
( Primeiro ciclo - J )
\* EAX = EBX
-- ECX = J | EDX = im | EBX = 0xdead | EAX = 0xdead –
\* shift left 5 vezes a EAX
-- ECX = J | EDX = im | EBX = 0xdead | EAX = 0x1bd5a0 –
\* adiciona o valor do char em ECX (J - 0x4a) a EAX
-- ECX = J | EDX = im | EBX = 0xdead | EAX = 0x1bd5ea –
\* Le o proximo char do Name pra ECX
-- ECX = i | EDX = im | EBX = 0xdead | EAX = 0x1bd5ea –
\* Adiciona 1 a EDX
-- ECX = i | EDX = m | EBX = 0xdead | EAX = 0x1bd5ea –
\* Adiciona EAX a EBX
-- ECX = i | EDX = m | EBX = 0x1cb497 | EAX = 0x1bd5ea –
\* Se ECX é diferente de 0 então do inicio de novo!

( Segundo ciclo - i )
\* EAX = EBX
-- ECX = i | EDX = m | EBX = 0x1cb497 | EAX = 0x1cb497 –
\* shift left 5 vezes a EAX
-- ECX = i | EDX = m | EBX = 0x1cb497 | EAX = 0x39692e0 –
\* adiciona o valor do char em ECX (i - 0x69) a EAX
-- ECX = i | EDX = m | EBX = 0x1cb497 | EAX = 0x3969349 –
\* Le o proximo char do Name pra ECX
-- ECX = m | EDX = m | EBX = 0x1cb497 | EAX = 0x3969349 –
\* Adiciona 1 a EDX (fica limpo)
-- ECX = m | EBX = 0x1cb497 | EAX = 0x3969349 –
\* Adiciona EAX a EBX
-- ECX = m | EBX = 0x3b347e0 | EAX = 0x3969349 –
\* Se ECX é diferente de 0 então do inicio de novo!

( Terceiro ciclo - m )
\* EAX = EBX
-- ECX = m | EBX = 0x3b347e0 | EAX = 0x3b347e0 –
\* shift left 5 vezes a EAX
-- ECX = m | EBX = 0x3b347e0 | EAX = 0x7668fc00 –
\* adiciona o valor do char em ECX (m - 0x6d) a EAX
-- ECX = m | EBX = 0x3b347e0 | EAX = 0x7668fc6d –
\* Le o proximo char do Name pra ECX (fica limpo)
-- EBX = 0x3b347e0 | EAX = 0x7668fc6d –
\* Adiciona 1 a EDX (continua limpo)
-- EBX = 0x3b347e0 | EAX = 0x7668fc6d –
\* Adiciona EAX a EBX
-- EBX = 0x7a1c444d | EAX = 0x7668fc6d –
\* ECX é zero, então temos a serial em EBX

$ kgme
Name: Jim
Serial: 7a1c444d
Good serial!

Fiz uma implementação em C++ do keygen, que a única coisa que usa de c++ é o cout mas mesmo assim, aqui vai:

//============================================================================
// Name        : lolkg.cpp
// Author      : falso
// Version     : 0.1-pre1-beta2-rc4
// Copyright   : WTFPL
// Description : Keygen para o kgme do dongs
//============================================================================

#include <iostream>
using namespace std;

int main() {

	char name[50];

	cout << "|         |    |\n";
	cout << "|    ,---.|    |__/ ,---.\n";
	cout << "|    |   ||    |  \\ |   |\n";
	cout << "`---'`---'`---'`   ``---|\n";
	cout << "                    `---'\n";

	cout << "Name: " << flush;
	cin >> name;

	int ebx = 0xdead;
	int eax = ebx;
	int ecx = name[0];

	int i = 1;

	while (ecx != 0x0) {
		eax = ebx;
		eax = eax << 5;
		eax = eax + ecx;
		ecx = name[i];
		i++;
		ebx = ebx + eax;
	}

	printf("Serial: %x\n",ebx);

	return 0;
}

E já agora, uma em MIPS assembly pro PCSpim:

# this asm can never fail
#
# lolkg
.data
us: .asciiz "User: "
u: .space 50

.text
.globl main
main:

# print user..
li $v0,4
la $a0,us
syscall

# get string
li $v0,8
la $a0,u
syscall

# variavel inicial do algoritmo
li $s0,0xdead # s0 - ebx
move $s1,$s0 # s1 - eax

la $s5,u # tmp
#apanha o primeiro char do Username
lb $s2,0($s5) # s2 - ecx

_while:
move $s1,$s0
sll $s1,$s1,5
add $s1,$s1,$s2

addi $s5,$s5,1
lb $s2,0($s5) # s2 - ecx

addu $s0,$s0,$s1
bne $s2,0xa,_while

# imprime o serial, mas em decimal ;-(
# nao sei como imprimir em hex, pois o serial é em HEX
li $v0,1
move $a0,$s0
syscall

#sai
li $v0,10
syscall

Espero que todo este conhecimento inútil sirva para alguém, nem que seja pra despertar um bichozinho pequenino pro cracking! Fiquem bem, e crackem muito!

Junho 6, 2009

Cracking 101

Arquivado em: assembly, cracking, useless — falso @ 00:53

Ora viva!

O software há dias estava a tentar descobrir como se cracka um simples strncmp(3), então eu fiquei com ideias de criar um breve *curso* de cracking para os iniciantes.
Começamos com o seguinte sores:

#include <stdio.h>

int main() {
        char loljews[10];

        printf("--> ");
        scanf("%s",&loljews);
        if(strncmp(loljews,"morte",5)==0) {
                printf(" OK ;-)\n");
        } else {
                printf(" FAiLED!\n");
        }

        return 1;
}

O código julgo que é auto-explicativo. O que se pretende é, depois de ter o programa compilado alterar o executável de forma a que aceite qualquer palavra sem ser “morte” e diga OK.
Então corri o programa no gdb e fiz “disassemble main”

Dump of assembler code for function main:
0x08048464 <main+0>:    push   %ebp
0x08048465 <main+1>:    mov    %esp,%ebp
0x08048467 <main+3>:    and    $0xfffffff0,%esp
0x0804846a <main+6>:    sub    $0x20,%esp
0x0804846d <main+9>:    mov    $0x8048590,%eax
0x08048472 <main+14>:   mov    %eax,(%esp)
0x08048475 <main+17>:   call   0x8048368 <printf@plt> # print "-->"
0x0804847a <main+22>:   mov    $0x8048595,%eax
0x0804847f <main+27>:   lea    0x16(%esp),%edx
0x08048483 <main+31>:   mov    %edx,0x4(%esp)
0x08048487 <main+35>:   mov    %eax,(%esp)
0x0804848a <main+38>:   call   0x8048378 <__isoc99_scanf@plt>
0x0804848f <main+43>:   movl   $0x5,0x8(%esp)
0x08048497 <main+51>:   movl   $0x8048598,0x4(%esp)
0x0804849f <main+59>:   lea    0x16(%esp),%eax
0x080484a3 <main+63>:   mov    %eax,(%esp)
0x080484a6 <main+66>:   call   0x8048398 <strncmp@plt> # faz o call ao strncmp
0x080484ab <main+71>:   test   %eax,%eax
0x080484ad <main+73>:   jne    0x80484bd <main+89> # Se não é igual, salta pra bad_boy
0x080484af <main+75>:   movl   $0x804859e,(%esp) # good_boy
0x080484b6 <main+82>:   call   0x8048388 <puts@plt> # print "OK"
0x080484bb <main+87>:   jmp    0x80484c9 <main+101> # salta para o fim
0x080484bd <main+89>:   movl   $0x80485a6,(%esp) # bad_boy
0x080484c4 <main+96>:   call   0x8048388 <puts@plt> # print "failed"
0x080484c9 <main+101>:  mov    $0x1,%eax # fim
0x080484ce <main+106>:  leave
0x080484cf <main+107>:  ret

Primeiro uma lista de instruções normalmente usadas para o cracking e os seus opcodes em hexadecimal.

JNE - Jump if Not Equal - 75
JE - Jump if Equal - 74
JMP - Jump - EB
NOP - No Operation - 90
JA - Jump if Above - 77
JB - Jump if Below - 72
JNA - Jump if Not Above - 76
JLE - Jump if Less or Equal - 7E
JL - Jump if Less - 7C

O que se pretende então é alterar ali aquele JNE em <main+73>. Mas para o quê? A primeira ideia seria inverter o sentido do jump, em vez de ser JNE passava a ser JE. Mas assim ficávamos com um problema, ao metermos “morte” iamos parar ao “failed”.
Então o que podemos fazer perguntam vocês?

Para explicar primeiro precisei de instalar um hex editor para Linux. O biew pareceu-me bem. Abri o executável nele e andei à procura do naco de código acima (do gdb).

000004A6:E8EDFEFFFF                     calln     file:00000398
000004AB:85C0                           test      eax,eax                      
000004AD:750E                           jne       file:000004BD
000004AF:C704249E850408                 mov       [esp],0804859E
000004B6:E8CDFEFFFF                     calln     file:00000388
000004BB:EB0C                           jmps      file:000004C9
000004BD:C70424A6850408                 mov       [esp],080485A6
000004C4:E8BFFEFFFF                     calln     file:00000388
000004C9:B801000000                     mov       eax,00000001                 
000004CE:C9                             leave

O que eu fiz foi alterar os os bytes do JNE e da posição para onde salta “75 0E”, para dois NOPs (No Operation - Não faz nada) “90 90”. Assim ele nunca faz o teste, e segue sempre em frente para o good_guy e depois sai.

Espero que isto tenha alguma utilidade para alguém! Para a próxima talvez haja um com algo mais complexo tipo Username com Serial derivada do Username ou algo do género.

Até la, fiquem bem e crackem muito ;-)

Junho 2, 2009

AES-256 debilitado

Arquivado em: cracking, drama, serious-business — dcoder @ 16:29

Surgiu um artigo recentemente, apresentado na Eurocrypt 2009, que afirmava que o AES-256 não é uma cifra ideal. O que é que isto significa? Significa que a cifra não é uma permutação aleatória de bits, i.e. que é possível, com um esforço menor do que testar todas as chaves possíveis, distinguir a saída de uma stream cifrada por AES de uma sequência aleatória de bytes.

Isto tem várias consequências importantes. Por exemplo, ao criar uma função de hashing recorrendo ao modo Davies-Meyer e à cifra AES-256, é possível encontrar colisões em menos de 2^(n/2) compressões — este facto é importante visto que este modo é usado em todas as hashes mais difundidas (e.g. MD5, SHA1, SHA2) e pode ser provado que é seguro, desde que a cifra seja segura. O artigo mostra que é possível encontrar q pseudo-colisões desta forma em q.2^67 operações. Utilizar AES-256 em modo Davies-Meyer estará, portanto, fora de questão.

Existem outras implicações desta distinção: dadas chaves relacionadas suficientes (2^35), conseguimos recuperar completamente uma delas em tempo 2^120. Este resultado é pouco útil na prática, mas mais uma vez debilita a cifra dado que não mantém a sua alegada segurança de 2^256.

O artigo em questão pode agora ser encontrado aqui.

Maio 4, 2009

RIP fravia+

Arquivado em: cracking, drama — falso @ 14:30

A noticia chegou-me pelo IRC, também já estava mais que sabido, ele próprio já tinha escrito no site dele que não ia durar mais que umas semanas. Pelo que a Wikipedia diz foi ontem, dia 3 de Maio 2009.
É uma grande perda para comunidade de RE de todo o mundo e não só, pois ele não se dedicava só a essa arte. Para mim, ele e o tKc foram como um senseis do cracking (que não domino assim tanto) que me meteram o bichinho, de ver como as coisas funcionam _under the hood_.
Vou actualizar de novo o mirror que tenho site dele em http://fravia.blol.org para não se perder caso o servidor onde o site dele ta alojado na suíça vá com os porcos.

Outubro 5, 2008

Tributo ao Fjalar Ravia

Arquivado em: cracking, serious-business — falso @ 23:54

Estava eu nas internets à procura de info sobre ARM para crackar umas cenas de windows mobile, quando vou parar à homepage do Searchlores do Fravia, e descubri que este deus do cracking está com o cancaro e com altas tratamentos e provavelmente ja nao vai actualizar mais a pagina dele.

I have been diagnosed with two different tumors at the same time, and after months of therapy I don’t really know what my chances of survival are. Fate believes to be stronger than me. We’ll see. While I undergo operations and chemotherapies, and learn the deep meaning of the term “patient”, my site won’t be updated

Eu com medinho que toda a informação de cracking que existe no site dele desapareça, fiz um mirror.

http://fravia.blol.org

Abril 17, 2008

Cracking X-Chat -- part ii

Arquivado em: coding, cracking, drama, fail, useless — dcoder @ 23:21

Aparentemente saiu uma nova versão do xcrap, 2.8.7a. Eu reparei nisto e lembrei-me que houve um post do falso ha uns tempos que falava de como crackar opensores. Infelizmente, não tenho muito tempo por isso vou ser sucinto.

O leitor assíduo facilmente vai descompactar o executável (é uma versão antiga do UPX) e encontrar a função de interesse (sub_4018CD).

Aqui encontramos o algoritmo de verificação, que consiste essencialmente em:

Hash = SHA-1(Linha3|Linha4|Linha4)

E = 0x25F86508483EFD

N = 0xB5BA27D856CCBE6B61CFE96A387D8E265A65897510AE91212634A7397432D1B2407604CAFA9DC77EF29A87B86D938748E0C4921D46C3AC4BCE7E00EECDFCF782DBD0D44C46C9057724CCF7DEDF36924E4683721FF55EDC570C4C71927887D67C1B1488A33E0B3F64160701B2390C3B3F278490B22DE9906A65B9DFBDF4E838870EFD5851DC0B2C94E444E0B1D2DAAA5C6060D3976E170BB8692111E26D178871008AE42EE250A856D2354102B57F560420AC9F89D004AF761341764FBEDD194A27AF6F34B9C0E3A48013734C6CBD3C216CFAD9B1F3DFDB76FE8519C78E3E95F3C4B39006111DE983C88D72CD4613D4CB852D36244D7B8D4AB15C740415382735

GoodSignature = 00 01 FF FF … (tem de ter 256 bytes (2048 bit) … FF FF 00 30 21 30 09 06 05 2B 0E 03 02 1A 05 00 04 14 | Hash

if (Linha2 ^ E (mod N) == GoodSignature) return GOOD else return BAD

OK, então temos basicamente uma assinatura digital baseada em RSA com 2048 bits. Não existem exploits óbvias (padding, expoente baixo, …) que se possam aproveitar; portanto, para fazer um keygen temos de alterar a chave pública. Escolham uma e substituam no sítio apropriado (public_key_n). Para gerar uma chave válida, temos então:

void generate_key(void)

{

char LInha1[] = “# Designed and implemented exclusively for the lulz”;

char Linha2[1024];

char Linha3[] = “Some Jew”;

char Linha4[] = “Crap”;

char Linha5[] = “More crap”;

HCRYPTPROV hProv;

HCRYPTHASH hHash;

unsigned char appendage[] = {00, 30, 21, 30, 09, 06, 05, 2B, 0E, 03, 02, 1A, 05, 00, 04, 14};

unsigned char good_signature[256];

unsigned char sha[32];

unsigned long tmp = 20;

mpz_t n, d, c;

memset(Linha2, 0, sizeof(Linha2)*sizeof(Linha2[0]));

CryptAcquireContext(&hProv, 0, 0, 1, 0xF0000000);
CryptCreateHash(hProv, 0x00008004, 0, 0, &hHash);
CryptHashData(hHash, Line3, strlen(Line3), 0);
CryptHashData(hHash, Line4, strlen(Line4), 0);
CryptHashData(hHash, Line5, strlen(Line5), 0);
CryptGetHashParam(hHash, 2, sha, &tmp, 0);
CryptDestroyHash(hHash);
CryptReleaseContext(hProv, 0);

memset(good_signature, 0xFF, 256);

memcpy(good_signature+220, appendage, sizeof(appendage));

memcpy(good_signature+236, sha, 20);

good_signature[0] = 0;

good_signature[1] = 1;

mpz_init_set_str(n, “9AFF449090074D691910719D0B384FDA86FAB987938E74CB6E6A91BE6086A8E11BDBD2EF7C1F3761EEBC3AB171F2FB9A79BD8A3CFBAD54A707F39FB8E804A0F4874447BE66550E9D444C496D251FF2402DC8DBAD7352124633F5CAF43A3971362B4466F28AAB1C2A1E81F36B8EE5E6284DD9645E500083B0B9102D559A57A52F0E831F7B39B630DC9B479E3914F34F33363A2075F372E650B94D230528A998D1613C097D78C1C66AE647E0DCF9590E3CA012C3A26614F851AE520163699044F6E8F71B8EDA7091DFDB4745FE27A806EF56E6B7B7175B7859B1725ACF6A03CC941DFED8773AA02DF350C3C0479744411B7F1CD625F5BF4F76E38DD42AC4901A89”, 16);

mpz_init_set_str(d, “36C70BF3DDBD70026346284E9E40E0B1637DD2FF8506F959772CBCEE7F2613A8697D8B822C6849753541DFAECA891A50C0F515E42F1BC8DFF2F48452BA27D29602E572DC9676512F1631AEF655C8F37C03C9E9E5E532CABE4ABD0E0495FA1556AC484D2F5F6E8AF08F934C80CC8D0369215FA2E5F73C0648509867BE61B766C716D84934F76699FAD81EC04E78E88CCC592D59B183361F35B2F3A0F2FEDC17F94F73831111984E5AB2AFCCC019090E4A48AB6CFE249066EFA6D02A4A9EA8369E60EF45A2CC921AE66C52CA1D273EB0355BAC9FD7598258FF94ED311100E732D22224B3744C3ABBB6BA4995781B8427D2BBF605488AC20E19483C10894283506D”, 16);

mpz_init(c);

mpz_powm(c, c, d, n);

printf("%s\n", Linha1);

gmp_printf("%Zd\n", c);

printf("%s\n", Linha3);

printf("%s\n", Linha4);

printf("%s\n", Linha5);

mpz_clear(n);

mpz_clear(d);

mpz_clear(c);

}

Disclaimer: este código foi feito de cabeça e nem sequer o tentei compilar. Se não funcionar, DESCUBRAM porquê!

Note-se que o autor implementou duas verificações para prevenir a alteração do executável (e assim também, da chave).

Primeira:

UPX0:004081EE loc_4081EE: ; CODE XREF: sub_4081BA+45j
UPX0:004081EE movzx edi, ds:public_key_n[ecx]
UPX0:004081F5 add edi, edx
UPX0:004081F7 shl edi, 1
UPX0:004081F9 inc ecx
UPX0:004081FA cmp ecx, 40h
UPX0:004081FD mov edx, edi
UPX0:004081FF jb short loc_4081EE
UPX0:00408201 cmp edx, 0B3A690A6h
UPX0:00408207 pop edi
UPX0:00408208 jz short loc_40822B

Segunda:

UPX0:00425A7A loc_425A7A: ; CODE XREF: sub_425A14+74j
UPX0:00425A7A movzx edx, byte ptr [eax]
UPX0:00425A7D imul ecx, 1Fh
UPX0:00425A80 add ecx, edx
UPX0:00425A82 dec eax
UPX0:00425A83 cmp eax, offset sub_401000
UPX0:00425A88 jnz short loc_425A7A
UPX0:00425A8A cmp ecx, 1D9AB667h
UPX0:00425A90 jz short locret_425A98

Certifiquem-se que corrigem isto quando alterarem o executável.

Já agora, a IDB comentada.

Um bem haja para todos.