Elgamal study 'n reversing

Data

by Evilcry

 

11/08/2005

UIC's Home Page

Published by Quequero

Where ones sees a limit

Grandissimo evil! Ottimo tutorial come al solito, sformattato, chiaro e semplice da capire come nel tuo stile ;p

the others sees an opporunity

Prepare for what you cannot see, expect the unexpected, a waiting game waiting to see......

WebSite: www.cryptorev.da.ru / http://abcd6.interfree.it
E-mail: [email protected] / [email protected]
IRC frequentato   irc.azzurranet.org   #cryptorev #crack-it - irc.efnet.nl #cracking4newbies #cryptology #win32asm
 

 

Difficoltà

NewBies () Intermedio (X) Avanzato () Master ()

 

 

Elgamal study 'n reversing
Written by Evilcry.

Introduzione

In questo tutorial andremo a studiare e successivamente reversare l'algoritmo Elgamal.

Tools usati

URL o FTP del programma

Libreria MIRACL

DiscreteLogarithmCalculator

Notizie sul programma

Troverete (crackme + keygen) nell' allegato.

Essay

 

Elgamal an introduction

L'algoritmo Elgamal appartiene alla categoria di cifrari a chiave asimmetrica applicati alla crittografia a chiave pubblica, cioè a tutta quella categoria di crittosistemi in cui il mittente ed il ricevente del messaggio non devono conoscere la stessa chiave. Infatti usano 2 coppie di chiavi ciascuno: una chiave pubblica ed una privata. A questo genere di cifrari appartiene anche RSA, e da alcune analisi risulta che Elgamal e RSA hanno una sicurezza simile per lunghezze di chiavi equivalenti, ma in termini di velocità Elgamal risulta essere il più lento, ed è quest'ultimo il principale motivo per cui RSA è maggiormente utilizzato. Elgamal basa la sua sicurezza problema di Diffie-Hellman il quale a sua volta basa la propria sicurezza sulla complessità computazionale del calcolo del logaritmo discreto, quindi se riusciamo a risolvere efficientemente il logaritmo discreto, Elgamal potrà essere brekkato facilmente. Per maggior chiarezza ho inserito una piccola spiegazione del logaritmo discreto:

Problema del logaritmo discreto

In aritmetica finita, la potenza di un numero si definisce come:

ab = x mod n

e come per l'aritmetica ordinaria possiamo stabilire un'operazione inversa rispetto all'esponente, cioè il logaritmo, che nel campo dell'aritmetica finita si definisce come

b = loga x mod n

Quest'ultimo viene chiamato logaritmo discreto. Se il calcolo della potenza è semplice, il calcolo del logaritmo è computazionalmente molto complesso, può avere più soluzioni o anche nessuna. Ad esempio in un'aritmetica di ordine 7 si avrebbe

20 = 1
21 = 2
22 = 4
23 = 1
24 = 2
25 = 4
26 = 1

perciò se prendiamo il log24 è 2 ma anche 5, non esistono però log23, log25, log26. Anche se non è stato ancora dimostrato, si pensa che la difficoltà computazionale del logaritmo discreto è dello stesso ordine di quella della fattorizzazione.

Elgamal viene fondamentalmente utilizzato in due campi, quello della cifratura pura e semplice, ed essendo poi un algoritmo a chiave pubblica asimmetrico (come già detto utilizza chiavi diverse per codifica e decodifica), trova applicazione come sistema di firma digitale.

L'algoritmo

La procedura di encrypting/decrypting può essere riassunta in tre fasi:

Nella prima fase, vengono generati due parametri (detti di dominio) che serviranno alla costruzione delle chiavi:

Il generatore appartiene al campo di Galois GF(p), cioè il campo degli interi modulo p, con p numero primo, ma ciò non toglie che il nostro generatore può appartenere ad altri gruppi definiti su altri campi, come i polinomi GF(2m) o il campo dei punti appartenenti a curve ellittiche oppure alle funzioni Lucas (polinomi speciali nel campo intero). E' importante ricordarci del fatto che il nostro generatore può avere varie origini, perchè potremmo avere delle varianti dell'Elgamal stesso.

Passiamo ora alla generazione delle chiavi, operazione strettamente dipendente dai parametri di dominio (p,g):

Passiamo ora al processo di Firma di un messaggio M :

Siamo alla fase finale, la Verifica della nostra firma, è importante ricordare bene la formula della verifica, perchè molto spesso, è proprio grazie a questa formula che riusciamo a capire se e come è implementanto Elgamal, nel programma che stiamo reversando:

La firma è valida se (ya)(ab) mod (p) = gM mod (p)

Ci potremmo pure limitare a conoscere questa formula, ma siamo qui per imparare e quindi capire, ergo cerchiamo di dimostrare ciò che abbiamo scritto :)

Dato che y ed a si possono scrivere come y = gx e a = g^k, possiamo riscrivere la nostra formula di verifica, in funzione di g:

(g^x)^a * (g^k)^b = g^M = g^(x * a) * g^(k * b) = g^M(mod p)

come vedete ci troviamo di fronte ad una forma esponenziale, e nessuno ci vieta di riscriverla in forma logaritmica:

x * a + k * b = m (mod p - 1)

se adesso andiamo a sostituire alla b, la sua relativa forma esplicita otterremo

x * a + k * (M - (x * a))/ k = M da cui

x * a + (M - x * a) = M

x * a + M - x * a = M

ed infine come per magia M=M :)

Vantaggi/svantaggi nell'uso di Elgamal

Oltre la lentezza (relativa ad RSA) un altro svantaggio di Elgamal è dovuto alla dimensione della firma digitale, che è il doppio della dimensione del modulo (problema che può essere ovviato se adottiamo la variante di Schnorr).

La variante più famosa dell'Elgamal è l'algoritmo di Schnorr, il quale differisce dal nostro algoritmo essenzialmente per l’introduzione di una funzione di hash che associa a ciascun messaggio ed a ciascuna chiave un intero in un intervallo di ampiezza predefinita. Il vantaggio principale che questo algoritmo presenta è la possibilità di scegliere la dimensione della firma selezionando l'intervallo del codominio della funzione di hash.

Elgamal strategies of attack

Come abbiamo precedentemente detto, la sicurezza di Elgamal si basa sul problema del logaritmo discreto, quindi com'è logico intuire, in caso di attacco, la prima prima (e quasi unica) strategia sarà quella di risolvere il logaritmo discreto per determinare alcuni valori incogniti, attraverso i quali potremo successivamente...avere ciò che ci interessa...Come sapete y (la nostra chiave pubblica) è definita come:

y= g^x

dove x è la chiave privata, di conseguenza, se dovessimo "attaccare" una signature, la prima cosa da fare è applicare il problema del logaritmo discreto proprio ad y, per conoscere x. Ottenuta la x possiamo procedere a determinare a e b, per poi esplicitare la formula della signature in base a cosa ci serve.

DLP solving techiques

Alla fine ho aggiunto anche un paragrafetto sulle principali tecniche per risolvere i logaritmi discreti. Attualmente esistono due grosse categorie di algoritmi per la risoluzione dei logaritmi discreti:

Anche se il primo metodo è più veloce del primo, l'algoritmo più usato in questi casi è il metodo Pollard's rho che appartiene alla categoria degli algoritmi per la ricerca di collisioni. Quest'algoritmo produce una lunga serie di numeri che graficamente si rappresentano la lettera greca rho (formata da un cerchietto e da na coda), lo scopo è capire dove il cerchietto s'interseca la coda. Il Pollard's rho method lavora a passi, dove n rappresenta la grandezza del gruppo da risolvere. Altro algoritmo usato per il DLP è il Pohlig-Hellman, leggermente meno performante rispetto al primo.

Elgamal Reversing

Bene!, eccoci arrivati alla parte più interessante, quella incui andremo a reversare un programma che implementa Elgamal. Come target ho scelto il pDriLl's Crypto Keygenme 3. Il crackme implementa il sistema di firma digitale (quindi dobbiamo ricordarci la formula che abbiamo visto prima), e lo fa attraverso l'uso delle librerie MIRACL. Come al solito, mano ad IDA e vediamo di trovare la routine di protezione:

00401057 mov ebx, ds:GetDlgItemTextA ;una call ebx equivarrà a chiamare la GetDlgItemText
0040105D push 1Fh
0040105F push offset String ; 0040D720 sarà conterrà il Nome
00401064 push 3E8h
00401069 push edi
0040106A call ebx ;GetDlgItemTextA
0040106C mov esi, eax
0040106E cmp esi, 1
00401071 jge short loc_401092 ;Se è stato inserito il Nome, salta, altrimenti dai segnala l'errore
00401073 push 0
00401075 push offset aInputError
0040107A push offset aYouHaveToTypeI
0040107F push EDI
00401080 call ds:MessageBoxA
...

Se la prima edit non è vuota, passa a prelevare i valori contenuti nelle altre due EditBox. Se anche gli altri campi sono validi (non nulli) arriviamo qui:

004010F8 cmp esi, 1Eh ;In esi abbiamo la lunghezza del nome inserito
004010FB mov eax, esi
004010FD jge short LOC_401124 ;se il nome inserito è più lungo o uguale a 1Eh (30d) salta, altrimenti procedi con la routine di "riempimento"
004010FF mov edx, 1Eh
00401104 lea EDI, String[esi]
0040110A sub edx, esi
0040110C mov eax, 2A2A2A2Ah ;2Ah rappresenta l'asterisco '*'
00401111 mov ecx, edx
00401113 mov ebx, ecx
00401115 shr ecx, 2
00401118 repe stosd
0040111A mov ecx, ebx
0040111C and ecx, 3
0040111F repe stosb
00401121 Lea eax, [edx+esi]
00401124:
00401124 mov String[eax], 0
0040112B mov EDI, offset String

La routine è abbastanza banale, se il nome che abbiamo inserito è più piccolo di 30 caratteri, lo completa con una serie di asterischi, fino a raggiungere 30 di lunghezza. Ad esempio evilcry diventerà "evilcry***********************" Andiamo avanti con l'analisi:

0040113F mov dword ptr [ebp+238h], 3Ch

Occhio a questa istruzione che ci servirà in seguito, che equivale a scrivere mip->IOBASE=60, serve cioè a stabilire con quale base (nel nostro caso base 60) verranno storati i nostri due serial.

...
0040114E push 0 ;Valore d'inizializzazione
00401150 mov [esp+2Ch+var_4], eax
00401154 call sub_401850 ;Inizializza un Bignumber
00401159 push 0
0040115B mov [esp+30h+var_10], eax
0040115F call sub_401850 ;Inizializza un Bignumber
00401164 push 0
00401166 mov esi, eax
00401168 call sub_401850 ;Inizializza un Bignumber
0040116D push 0
0040116F mov ebx, eax
00401171 call sub_401850 ;Inizializza un Bignumber

...

Anche questo pezzo di codice è semplice da capire, se conoscete come funzionano le librerie MIRACL, in pratica vengono inizializzati una serie di BigNumber, una particolare struttura adatta a contenere numeri molto grossi, eccone la definizione:

typedef struct {
int Size;

char bytes[24]; } big_num;

Per poter visualizzare il BigNum dobbiamo fare AddressBigNum+0Ch.

00401199 mov edi, offset byte_40D820 ;mette in edi l'indirizzo del nostro Serial1
0040119E or ecx, 0FFFFFFFFh
004011A1 xor eax, eax
004011A3 add esp, 20h
004011A6 xor edx, edx
004011A8 repne scasb
004011AA not ecx
004011AC dec ecx
004011AD test ecx, ecx
004011AF jle short loc_4011E3
004011B1 loc_4011B1:
004011B1 mov al, byte_40D820[edx] ;Mette in al un char alla volta, del nostro Serial1
004011B7 cmp al, 41h ;Se è una controlla che sia maiuscola
004011B9 jl short loc_4011BF
004011BB cmp al, 5Ah ;dalla 'A' alla 'Z'
004011BD jle short loc_4011CF
004011BF
004011BF cmp al, 61h
004011C1 jl short loc_4011C7
004011C3 cmp al, 7Ah
004011C5 jle short loc_4011CF ;dalla 'a' alla 'z'
004011C7
004011C7 cmp al, 30h
004011C9 jl short loc_40122C
004011CB cmp al, 39h
004011CD jg short loc_40122C ;da 0 a 9
004011CF
004011CF mov edi, offset byte_40D820
004011D4 or ecx, 0FFFFFFFFh
004011D7 xor eax, eax
004011D9 inc edx
004011DA repne scasb
004011DC not ecx
004011DE dec ecx
004011DF cmp edx, ecx
004011E1 jl short loc_4011B1

Quest'altra semplice routine serve a controllare il formato del primo serial (Seial1) che abbiamo inserito, che può essere composto da numeri e lettere upper/lower case. Lo stesso controllo avviene per il secondo serial (Serial2) e stavolta la routine da 4011E3 a 401228. Se entrambi i seriali rispettano le regole che abbiamo appena visto, possiamo procedere con la prossima routine:

00401272 push offset Serial1
00401277 push ebp ;BigNum Var
00401278 call sub_404220 ;instr
0040127D mov edx, [esp+2Ch+arg_4]
00401281 push offset Serial2
00401286 push edx ;BigNum Var
00401287 call sub_404220 ;instr

La cosa migliore adesso, è vedere quali sono i cambiamenti in ebp prima e dopo la call senza entrarci ( Zen way docet by Fravia ;)). Notiamo subito che la call altro no fa che ricopiare i nostri serial in base 60 nella variabile BigNum puntata da ebp e successivamente da edx, in altre parole viene usata l'istruzione instr, la quale appartiene al gruppo di funzioni che gestiscono l'Input/Output di MIRACL (mrio1.c se avete voglia di studiarvela meglio)

0040128C mov eax, [esp+28h] ;eax punta alla locazione di una variabile BigNum
00401290 mov ecx, [esp+2Ch] ;ecx contiene 1Eh (la lunghezza del nostro nome)
00401294 push ebx
00401295 push offset Nome
0040129A push ecx
0040129B mov dword ptr [eax+238h], 10h ;Base di conversione, in questo caso esadecimale
004012A5 call sub_4040B0

Come possiamo vedere la call a 4040B0 accetta come argomenti una lunghezza un puntarore ed un big, se andiamo a controllare il manuale MIRACL vediamo che questo corrisponde alla funzione

void bytes_to_big(int len,char *ptr,big x)

Converte cioè una stringa di bytes in BigNum attuando un cambiamento di base (esadecimale nel nostro caso).

004012AA push offset aAc2db4fec8c629 ;"AC2DB4FEC8C62992DB4F"
004012AF push esi
004012B0 call Instr ;converte AC2DB4FEC8C62992DB4F in BigNum
004012B5 push ebx ;Il nostro Nome sottoforma di BigNum
004012B6 push esi ;AC2DB4FEC8C62992DB4F
004012B7 push 2
004012B9 push ebx ;Il nostro Nome sottoforma di BigNum
004012BA call sub_4039B0 ;call da studiare

Sulla prima call c'è poco da dire, una stringa viene trasformata in BigNum, la seconda funzione invece vediamo che accetta 4 argomenti, 3 big ed un intero. Facendo una piccola ricerca notiamo che l'unica funzione del genere è power, la quale effettua l'elevamento a potenza (nel campo dell'aritmetica modulare sempre) di un BigNum con un intero, nel nostro caso:

Nome ^ 2 mod AC2DB4FEC8C62992DB4F = Risultato1

Il risultato si trova in EBX+0Ch (ricordate AddressBig+0Ch)

004012BF push offset aB54f430648c6b2 ;"B54F430648C6B2A10FFB"
004012C4 push esi
004012C5 call Instr
004012CA mov edx, [esp+50h]
004012CE push offset a2e0c2db4fec8c6 ;"2E0C2DB4FEC8C6299A0C"
004012D3 push edx
004012D4 call Instr
004012D9 mov edi, [esp+64h]
004012DD add esp, 44h
004012E0 push offset a4e0f2acad51c4c ;"4E0F2ACAD51C4CCDFB51"
004012E5 push edi

004012E6 call Instr

Altre 3 stringhe vengono convertite in BigNum:

004012EB mov eax, [esp+18h] ;BigNum vuoto
004012EF mov ecx, [esp+1Ch]
004012F3 push eax ;In questo BigNum andrà il risultato della call che avverà fra poco
004012F4 push esi ;B54F430648C6B2A10FFB
004012F5 push ebx ;Risultato1
004012F6 push ecx ;2E0C2DB4FEC8C6299A0C
004012F7 call sub_403550

Come vedete, questa call sembra essere simile alla power, con l'unica differenza che gli argomenti qui sono tutti Big, andiamo perciò a guardarci il manuale e scopriamo che si tratta della funzione powmod. La powmod funziona essenzialmente come la power, soltanto che l'esponente in questo caso è un Big (vi consiglio di dare un'occhiata a mrpower.c, e studiarvi un pò come funziona, è implementata davvero bene!).

2E0C2DB4FEC8C6299A0C ^ Risultato1 mod B54F430648C6B2A10FFB = Risultato2

004012FC mov edx, [esp+48h] ;BigNum vuoto
00401300 mov eax, [esp+44h] ;Serial2
00401304 push edx
00401305 push esi ;B54F430648C6B2A10FFB
00401306 push eax ;Serial2
00401307 push ebp ;Serial1
00401308 push ebp ;Serial1
00401309 push edi ;4E0F2ACAD51C4CCDFB51

0040130A call sub_4034B0

Stesse osservazioni di prima (5 BigNum come argomento di una funzione) e scopriamo immediatamente che si tratta della funzione mad (acronimo di multiply add and divide), alla fine avremo:

(4E0F2ACAD51C4CCDFB51 ^ Serial1) * (Serial1 ^ Serial2) mod B54F430648C6B2A10FFB = Risultato3

E finalmente siamo arrivati all'ultima porzione di codice da analizzare, dopo della quale vi sarà tutto più chiaro

0040130F mov ecx, [esp+60h]
00401313 mov edx, [esp+40h]
00401317 push ecx ;Risultato3
00401318 push edx ;Risultato2
00401319 call sub_4025F0 ;Confronta i due risultati
0040131E add esp, 38h
00401321 test eax, eax
00401323 push 0 ; uType
00401325 jnz short loc_401338 ;se sono uguali il crackme è risolto
00401327 mov eax, [esp+28h+hDlg]
0040132B push offset aGoood ;"GOOOD!!!"
00401330 push offset aYourKeyIsGood_ ;"Your key is good."
00401335 push eax
00401336 jmp short loc_401347

Cominciamo a ragionare su un modello di astrazione più elevato, così il reverse si ridurrà ad una serie di congetture matematiche. Perchè il crackme sia risolto, abbiamo detto che Risultato2 = Risultato3, partendo da questa osservazione esplicitiamo un pò Risultato2 e Risultato3, fino ad ottenere

(4E0F2ACAD51C4CCDFB51 ^ Serial1) * (Serial1 ^ Serial2) mod B54F430648C6B2A10FFB = 2E0C2DB4FEC8C6299A0C ^ (Nome ^ 2 mod AC2DB4FEC8C62992DB4F) mod B54F430648C6B2A10FFB

e già questa forma dovrebbe ricordarvi qualcosa..passiamo adesso a rinominare i BigNum che compaiono nella nostra formula

y= 4E0F2ACAD51C4CCDFB51
p= B54F430648C6B2A10FFB
g= 2E0C2DB4FEC8C6299A0C
M= Nome ^ 2 mod AC2DB4FEC8C62992DB4F
a= Serial1
b= Serial2

Riscriviamo ancora una volta la formula che ci siamo ricavati ed otteniamo..

(y^a)(a^b) mod (p) = g^M mod (p)

è proprio la signature di Elgamal! :)

Le nostre incognite sono a e b, definite come

a= g^k mod (p)

b= M=(xa+kb) mod (p-1)

Ci serve la x, che possiamo ricavare dalla y (definita come y=g^x mod p), risolvendone il logaritmo discreto. Fra i link utili all'inizio, vi ho postato quello di un DLP solver, inserita la nostra y, il risultato che ci verrà restituito sarà proprio la x che cercavamo:

x= 299376145767585197811667 in esadecimale -> 3F6536A02CD18F3B67D3

Se facciamo attenzione, notiamo che per avere a ci manca ancora una cosa, la k, che però essendo un numero random lo potremo scegliere a nostra discrezione. La scelta più logica è usare k=1 ...e mi pare :). Infine per conoscere Serial2

Serial2= M-x*serial1 mod (p-1)

Non ci resta che convertire Serial1 e Serial2 in base 60 ed il gioco è fatto :)

Come avete visto, il problema del logaritmo discreto gioca un ruolo chiave nella sicurezza di Elgamal, e quello che abbiamo fatto noi, trovando i due Serial, è un attacco crittografico ad Elgamal in piena regola, dove abbiamo sfruttato il fatto che i parametri usati dal crackme erano piccoli, tanto da poter giustificare la risoluzione del logaritmo discreto.

 

Note finali

Salutoni a: Quequero, AndreaGeddon, Ironspark, LonelyWolf, ZaiRoN,Littleluk ,Ntoskrnl. Anticrack: Bon_Scott, Zero, Hell_Master, bLaCk-eye, Stanley_White

Disclaimer

Noi reversiamo al solo scopo informativo e di miglioramento del linguaggio Assembly.

Capitoooooooo????? Bhè credo di si ;))))