Como não ganhar um iPad 2
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!


