jueves, 26 de diciembre de 2013

El timo del h4ckc0nt3st en GSICKMINDS 2013



EDIT a finales de 2015: Por fin, en Octubre de este año, hemos recibido un correo de los actuales organizadores de GSICKMINDS dándonos la razón en nuestro enfado y disculpándose por lo ocurrido.
Han quedado además de pedir una disculpa pública, que creo que pondrán los próximos días en alguna de sus webs (y enlazaré desde aquí)
Además, y realmente esto es algo que no hacía falta pero se agradece, han considerado como ganadores a los dos equipos que estábamos en los primeros puestos y nos han dado el premio a cada uno, pADAwan (de la gente que luego fundamos la asociación de Eurasia) y ka0labs, por nuestra parte hemos decidido donar el premio a la asociación Eurasia y aún dará para un par de charlas este año :)
La verdad es que es un detalle, y dada la difusión que tuvo esto en su día me veía obligado a rectificar en el mismo sitio donde expresé mis quejas . ¡Más vale tarde que nunca!



----------------------------------------

Ya ha pasado un tiempo prudencial desde que decidí ir con mis alumnos a este evento organizado por el grupo GSIC (Grupo de Seguridad de la Información) de la Universidade de A Coruña, y este tiempo me ha valido para dos cosas, por una parte para ver objetivamente los hechos sin el calentón lógico de esos días y por otra para esperar respuesta a algunas cuestiones por parte de la organización o la universidad, en definitiva, no ha valido para nada. 

Por eso, después de este tiempo voy a publicar esto por aquí, que es la carta al rector que escribí informando de lo ocurrido y que es un resumen perfecto de la super-organización de uno de los supuestos eventos punteros de seguridad en este país, que encaja a la perfección en la mentalidad españistaní que reina por aquí últimamente.

También aprovecho para publicar los writeups de las challenges (que por cierto no eran nada fáciles, no es que hayamos ido de paseo) que algunos estaban esperando, porque me parece que si esperamos por la organización vamos apañados y ha pasado ya un tiempo razonable.

Aquí la prueba 2

Aquí la prueba 4 

Aquí la prueba 6

Y al tema, el correo: 

---


Estimado Señor Rector:


Me pongo en contacto con usted para ponerle al corriente de unos hechos que creo pueden ser de su interés, y que paso a detallar a continuación.


En el transcurso de nuestra participación en GSICKMINDS 2013 organizado por el Grupo de seguridad de la información de A Coruña en el Aula Magna de la Fac. de Informática de A Coruña los días 24,25 y 26 y en donde la FIC es el principal patrocinador y organizador. según anuncian en la información del evento


"GSICKMINDS es un evento organizado por el Grupo de seguridad de la Información de A Coruña (GSIC), nacido en 2009 bajo el patrocinio de la Facultad de Informática de A Coruña y la Universidad de A Coruña, con objeto de fomentar el desarrollo de sistemas de información seguros y proponer soluciones que salvaguarden la integridad y confidencialidad de los activos digitales."


hemos observado y sufrido una falta absoluta de organización y transparencia



En la página web del evento se anuncia una actividad llamada "h4ckc0nt3st" cuya descripción es la siguiente




----
La competición de seguridad y hacking creada por Miguel Gesteiro que celebramos en GSICKMINDS por tercer año consecutivo. Si te gustan las emociones fuertes te lo pasarás en grande porque habrá desafíos de todas las clases: criptografía, ingeniería inversa, programación, esteganografía, forense, análisis de red...


Para participar necesitas:


Apuntarte (tu equipo o tú solo) en el portal web que indicaremos DURANTE LA PRESENTACIÓN DE LA JORNADAS el jueves 24.
Curiosidad por saber cómo funcionan las cosas (¡espíritu hacker!)
Un ordenador (aunque NO para todos los desafíos... ;)
Uno de los objetivos de la actividad es promover la Seguridad Informática de forma práctica y desde un punto de vista ofensivo (tendrás que comportarte como lo haría un atacante real...). Pero nunca estarás solo: podrás hacernos todas las preguntas
que quieras y te daremos pistas.


Happy hacking!


Horario: desde las 10:00 del jueves 24, hasta las 12:00 del sábado 26 (24 h non-stop)
Fechas: jueves 24 , viernes 25 y sábado 26
Lugar: on-line (en la red WiFi de las jornadas)
Plazas: ilimitadas
Precio: gratuita
Premios: 1º 150€ | 2º 75€ | 3º 35€
----


Como formo parte de un grupo interesado en estos temas, y además imparto clase de seguridad informática en un centro privado, decido después de ver la descripción en la página apuntarme con mis alumnos reservando en los respectivos trabajos unas horas para poder asistir al evento.


El Jueves 24 a las 10:00 empezamos el evento, con los siguientes contratiempos:


- El evento empezó a las 18:00 después de numerosos retrasos, lo que es inaceptable, y aún lo es más que no se ofreciera ningún tipo de explicación convincente o al menos una disculpa por parte de la organización después de 8 horas de retraso en las que nos limitamos a esperar en el hall de la facultad.


- Cuando el evento empezó, se nos facilitó una web con unas normas internas del concurso que entre otras cosas especificaban que los no inscritos en las jornadas como asistentes podían participar pero sin derecho a premio, norma que entiendo debería ser anunciada en alguno de los múltiples anuncios previos al evento, y no con posteridad, cuando ya los equipos se han desplazado y apuntado.


- Decidimos continuar a pesar de estar participando ya sin derecho a premio. Dentro del esquema lógico del concurso, había 5 pruebas iniciales que fuimos resolviendo a medida que pasaba el tiempo .A falta de dos horas, se anunció la última prueba, algo del todo irregular y desequilibrante. A pesar de esto, resolvimos ya fuera de tiempo dicha prueba.


- Una de las pruebas, consistente en abrir un candado, es anulada después de que varios equipos la completaran con éxito, entre esos equipos el nuestro.


- Cuando el concurso acaba, a las 12:00 del sábado, detectamos que algún equipo todavía está realizando las pruebas y desde la organización nos contestan que "acabará a las 13:00 porque la hora del ordenador que usamos está una hora retrasado"



A pesar de esto, nuestro equipo ganó el evento con una diferencia de 20 puntos sobre el segundo, y no solo eso, sino que escribimos informes de cómo realizamos las pruebas que reenviamos a la organización, incluso hicimos la prueba del sábado a las 10:00 aunque fuera de tiempo, pero cuando asistimos a la conferencia de clausura, donde se iba a realizar la entrega de premios, no hubo ni una sola mención al concurso excepto que había "dudas razonables" en alguno de los puestos, y tenían que pasar un proceso de revisión, que anunciarían en twitter los ganadores.


Tras varios días y múltiples preguntas en twitter, no hemos obtenido ninguna respuesta por parte de la organización.


Después de estos hechos creemos que la imagen que ha dado la organización del evento ha sido totalmente irrespetuosa con los participantes, opaca y nos genera cierta desconfianza el método utilizado para ir poniendo trabas a determinados equipos en base a reglas no anunciadas con anterioridad.


Nosotros no hacemos esto por dinero, de hecho sólo el desplazamiento de las cinco personas de mi equipo desde Ferrol a Coruña durante tres días, más gastos, no hubiera compensado los 150€ que se anunciaban como premio, nosotros participábamos por poner a prueba los conocimientos adquiridos y por el reconocimiento que implica ganar un evento de estas características, pero el no tener ni una simple mención después de haber ganado es una terrible falta de respeto.


Por eso nos hemos puesto en contacto con usted, porque creemos que los actos que se organicen en la Universidad, con su patrocinio y aprobación, deberían tener cierto grado de control y unos mínimos de calidad que en esta ocasión no se han alcanzado.


Atentamente


David Carracedo Martínez


---

La respuesta llegó bastante tiempo después, desentendiéndose del asunto, porque la UDC pone las instalaciones pero GSIC es un grupo de alumnos y ex-alumnos no relacionado directamente, etc, etc..

Por otra parte, hemos intentado contactar con la organización por twitter, por mail, y las respuestas han sido de lo más graciosas, porque vereis, después de quedar primeros en un concurso de seguridad, que te contesten esto:


estamos analizando la máquina recién llegada: 20GB. Nos va a llevar un poquito :)

¿20 GB de logs de qué? si las pruebas consistían principalmente en binarios, te los descargabas y los hacías en tu propia máquina, ¿que tienen que revisar?, luego dicen que 

¡El server! :) tenemos que revisar logs para ver que no alteramos el ranking al introducir los puntos de

¿El ranking genera 20 GB de logs ? si éramos unos 20 equipos participantes y solo había que entrar ahí para descargar las pruebas y meter los códigos de las puntuaciones, ¿cuántas peticiones puede haber en este log?, 200, 20000, 200000... 20GB???!!

Y nada, después de eso han dejado de contestar básicamente, así que supongo que hasta el año que viene, cuando vuelvan a pedir instalaciones y fondos no se les verá el pelo, así que enhorabuena a la organización por un acto tan desastroso.

---
Edit: Añado algo de información sobre el evento porque claro, en la carta al rector no me metí mucho en el tema técnico porque supuse que no le resultaría muy interesante, así que explico un poco.

Un CTF es una competición de seguridad informática que consiste en capturar una "bandera" (de ahí el nombre, capture the flag) que suele ser una palabra clave escondida detrás de algún tipo de protección que hay que saltarse.

Este en concreto es un CTF de tipo jeopardy, en el que se apuntan equipos y tienen entre 1 y 3 días normalmente para realizar pruebas de varios tipos en distintas categorías que suelen ser (puede variar) network, crypto, forensics, reversing, binary, web, stego, programación, a veces preguntas tipo trivia .. de todo un poco, las pruebas dan puntos según su nivel de dificultad y más o menos es un standard que las pruebas más fáciles den 100 y las más dificiles 400, pero repito, esto depende del concurso y puede variar.

Se puede ver que hay una comunidad bastante activa a nivel internacional en sitios como ctftime.org que intenta llevar un control y un ranking sobre los eventos.

Dicho esto, desde el punto de vista técnico el concurso fue un desastre también, ¿por qué? pues..




----
La competición de seguridad y hacking creada por Miguel Gesteiro que celebramos en GSICKMINDS por tercer año consecutivo. Si te gustan las emociones fuertes te lo pasarás en grande porque habrá desafíos de todas las clases: criptografía, ingeniería inversa, programación, esteganografía, forense, análisis de red...
---

Lo normal, sin embargo cuando abrimos las pruebas nos encontramos 5 pruebas con el siguiente contenido:

Prueba 1: Una pregunta chorra: "de que color es el logo de la organización?"
Prueba 2: Reversing de un binario en arm
Prueba 3: Crypto, una prueba de criptografía mortal puesta por kachakil del equipo int3pids, aún no la hemos descifrado a día de hoy (primero estaba cifrado con XOR, de ahí sacabas un rar, ahí unos números y luego una locura :D divertido, pero no se pudo hacer)
Prueba 4: Reversing de otro binario!!! este demencial (ver el writeup arriba)
Prueba 5: Abrir un candado. Si, un candado físico, porque había un taller sobre eso en las jornadas.. sería divertido también, pero cuando 3 o 4 ya lo habíamos abierto el que organizaba la prueba dijo "uy.. es que es demasiado fácil, así que anulada, la repetimos con otro", y nos puso otro candado que no pudimos abrir, sin embargo la prueba la hizo más gente que la primera vez y no la volvieron a anular.

El CTF duraba 3 días, 72 horas.. y estas fueron las pruebas (decían que iban a poner más, pero nanai), cuando quedaban 3 o 4 horas para finalizar ponen..

Prueba 6: Reversing de otro binario!! que obviamente nadie pudo hacer a tiempo, porque era también bastante difícil, pero que hicimos un rato después de acabar, por cabezonada, y porque el último día no teníamos pensado ir, pero al ver que habían puesto una nueva prueba vía twitter, arrancamos el coche y nos desplazamos a Coruña otra vez, para asegurar el #1 en el ranking, cuando llegamos allí y vimos el panorama, ya nos dimos cuenta de que dificilmente se podría haber hecho a tiempo.


Total, que de las pruebas que prometían nada, la gente que no hace reversing fue de paseo, cada vez que hablábamos con alguien de la organización nos decía "tranquiiilos ahora mismo subimos otra prueba", pero nada, un fracaso total.

Quería añadir esto más que nada para la gente que ya sabe de que va el mundillo este, y para animar a los que no lo saben a que hagan cosas de estas.. fuera de españa :D hay un montón de CTF muy buenos a lo largo del año!









Writeup prueba 2 - h4ckc0nt3st - GSICKMINDS 2013



IWANTTOBELIEVE

El binario está compilado para ARM, se ha usado una Rpi para ejecutarlo.

Cuando se ejecuta, mira el contenido de /proc/cpuinfo y busca el string "MU-TH-R 182", si no lo encuentra, el programa finaliza.


Si se crea un chroot y en el se cambia el contenido de /proc/cpuinfo para que contenga ese texto, el programa continua y saca este texto.


Your token has been written, read it now before it expires.
To validate the token you will need to enter it uppercase with no white spaces.
Thanks.
Temporal token expired.


Si lo ejecutamos con strace, podemos ver que escibe algo en /tmp y luego lo borra


nanosleep({5, 0}, {4, 3936093})         = ? ERESTART_RESTARTBLOCK (To be restarted)
--- SIGALRM (Alarm clock) @ 0 (0) ---
unlink("/tmp/.muthr_tmp_token")         = 0
write(1, "Temporal token expired.\n", 24Temporal token expired.


Así que ejecutamos gdb para pararlo antes de que borre el fichero:

- break on __libc_start_main
run
- break on unlink
continue


file /tmp/.muthr_tmp_token


.muthr_tmp_token: GIF image data, version 87a, 200 x 250


IWANTTOBELIEVE


token.gif



Writeup prueba 6 - h4ckc0nt3st - GSICKMINDS 2013




El fichero omgdos es un ejecutable ELF 32bit Linux

$ file omgdos
omgdos: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.32, BuildID[sha1]=0x9b85ac423ba72581a5c483f8a1ed077004bde96e, stripped

El traceado revela llamadas de sistema a vm86, se usa el modo 8086 virtual :


# strace ./omgdos
...
execve("./omgdos", ["./omgdos"], [/* 21 vars */]) = 0
[ Process PID=7405 runs in 32 bit mode. ]
mmap2(NULL, 1048576, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0
...
vm86(0x1, 0x804a780, 0x8048db2, 0, 0xfffc) = -1 ENOSYS (Function not implemented)

Para la ejecución en el modo virtual 8086 el programa host tiene que mmap-ear todo el código y los datos usados a la memoria y configurar handlers para los callbacks. La memoria esta en :


if ( alloc_mem(1048576u) == -1 )
 {
   v2 = __errno_location();
   v3 = strerror(*v2);
   fprintf(stderr, "%s: cannot mmap: %s\n", *(_DWORD *)a2, v3);
   exit(1);
 }
 sub_8048E3C(0, 31744u);
 sub_8048EDB(105u);
 v4 = sub_8048940(0, 0x7C00u);
 emulate((int)code, 671, v4, "C:\\WINDOWS\\RUNDLL32.EXE");

Para la interacción con el programa host se usa la interrupción 105 (0x69), el handler se implementa en


.text:08049016 handler         proc near               ; CODE XREF: sub_8048BFF+114#p
.text:08049016

Funciones:

0 – exit
1 – imprimir a stdout
2 – leer de stdin


El modo real se puede obtener interrumpiendo en la llamada de sistema VM86_INIT y volcando la reserva de memoria con la función alloc_mem():


# gdb -q ./omgdos
Reading symbols from /home/user/coruna/omg/omgdos...(no debugging symbols found)...done.
gdb-peda$ catch syscall vm86
Catchpoint 1 (syscall 'vm86' [166])
gdb-peda$ r
Catchpoint 1 (call to syscall vm86), 0xf7fdf430 in __kernel_vsyscall ()
gdb-peda$ generate-core-file core
Saved corefile core

Después se puede extraer el core:

$ cat extract_core
#!/usr/bin/python

f = open('core').read()
open('dmp', 'w').write(f[1600:1600+1000000])

Code is loaded on address 0x7C00 (like MBR):

seg000:7C00                 mov     sp, offset stack
seg000:7C03                 mov     ax, offset aDimeQuICenIElP ; "Dime, -+qu+¬ cen+¬ el pen+¦ltimo d+¡a e"...
seg000:7C06                 call    print
seg000:7C09                 mov     ax, 2
seg000:7C0C                 mov     bx, offset input
seg000:7C0F                 mov     cx, 100
seg000:7C12                 int     69h
seg000:7C14                 mov     ax, offset input
seg000:7C17                 call    near ptr decrypt_compare+1
seg000:7C1A                 test    ax, ax
seg000:7C1C                 jnz     short loc_7C23
seg000:7C1E                 mov     ax, offset winner ; "\x1B[1m-íS+¡!\x1B[0m -+C+¦mo lo has sab"...
seg000:7C21                 jmp     short loc_7C26
seg000:7C23 loc_7C23:                               ; CODE XREF: seg000:7C1C#j
seg000:7C23                 mov     ax, offset nope ; "Pffff pero qu+¬ me est+ís contando.\n"
seg000:7C26
seg000:7C26 loc_7C26:                               ; CODE XREF: seg000:7C21#j
seg000:7C26                 call    print
seg000:7C29                 mov     ax, 0
seg000:7C2C                 int     69h

Imprime la cadena
Dime, ¿qué cené el penúltimo día en el festival de Ortigueira?
comprueba la entrada de usuario, y si acierta imprime
¿Cómo lo has sabido? Venga, pon la solución en la web y luego búscame para explicarme cómo demonios lo has hecho.
y en error
Pffff pero qué me estás contando.

Rutina de descifrado:


seg000:7C2E aLgimijhflyUMns db 'Lgimijhfly+u-mnsp}' ; DATA XREF: seg000:7C47#o
seg000:7C40                 db  7Fh ;  
seg000:7C41 ; ---------------------------------------------------------------------------
seg000:7C41                 jnz     short near ptr aDimeQuICenIElP+24h
seg000:7C43
seg000:7C43 decrypt_compare:                        ; CODE XREF: seg000:7C17#p
seg000:7C43                 cmp     [bp+57h], dl
seg000:7C46                 push    cx
seg000:7C47                 mov     si, offset aLgimijhflyUMns ; "Lgimijhfly+u-mnsp}"
seg000:7C4A                 mov     di, ax
seg000:7C4C                 xor     cx, cx
seg000:7C4E
seg000:7C4E loc_7C4E:                               ; CODE XREF: seg000:7C60#j
seg000:7C4E                 inc     cx
seg000:7C4F                 cmp     cx, 16h
seg000:7C52                 jz      short loc_7C67
seg000:7C54                 mov     al, [di]
seg000:7C56                 xor     al, cl
seg000:7C58                 mov     ah, [si]
seg000:7C5A                 cmp     al, ah
seg000:7C5C                 jnz     short loc_7C62
seg000:7C5E                 inc     si
seg000:7C5F                 inc     di
seg000:7C60                 jmp     short loc_7C4E

Hace un XOR con la posición de cada caracter y lo compara con un string prefijado. La entrada se puede calcular:


t='4C67696D696A68666C792B752D6D6E73707D7F756138'.decode('hex')
s=''
for i in xrange(len(t)):
s += chr(ord(t[i])^(i+1))
print s

Así que la respuesta correcta es “Mejillones y cacaolat.”


Writeup prueba 4 (rompeme.exe) - h4ckc0nt3st - GSICKMINDS 2013




Usamos el IDA Pro para el análisis de RompeMe.exe, primero para encontrar la rutina de checkeo de licencia,

wsprintfA(String1, "%08X.lic", v4);
result = GetFileAttributesA(String1);
if ( result != -1 )
{
result = (DWORD)CreateFileA(String1, 0x80000000, 1u, 0, 3u, 0x20u, 0);
if ( result != -1 )
{
v2 = (void *)result;
v3 = check_license((HANDLE)result);

El nombre de archivo depende de parametros de hardware como CPUID, MAC de la tarjeta, y número de serie de disco. Para la máquina de prueba se encontró usando SysInternals ProcMon y el API Monitor:


En la máquina de prueba la licencia era AAF2FC0D.lic .
El contenido de la licencia se comprueba con la rutina:

int __stdcall check_license(HANDLE hFile)
{
HLOCAL v1; // ST20_4@1
int v2; // ST1C_4@1
DWORD NumberOfBytesRead; // [sp+4h] [bp-Ch]@1
DWORD FileSystemFlags; // [sp+8h] [bp-8h]@1
DWORD VolumeSerialNumber; // [sp+Ch] [bp-4h]@1

GetVolumeInformationA(0, 0, 0, &VolumeSerialNumber, 0, &FileSystemFlags, 0, 0);
ReadFile(hFile, &license_file_content, 0x100u, &NumberOfBytesRead, 0);
v1 = decrypt_license((char *)&license_file_content, (int)&word_404149);
wsprintfA(String1, "%08X", VolumeSerialNumber);
v2 = (*(_DWORD *)&String1[4] ^ *((_DWORD *)v1 + 1)) + (*(_DWORD *)String1 ^ *(_DWORD *)v1) == 0;
LocalFree(v1);
return v2;
}

Donde la función de descifrado de la licencia es

HLOCAL __stdcall decrypt_license(char *content, int mac)
{
HLOCAL result; // eax@1
signed int i; // ecx@2
char v4; // bl@4
__int16 mac1; // [sp+4h] [bp-Ch]@1
__int16 mac0; // [sp+6h] [bp-Ah]@1
char v7; // [sp+Dh] [bp-3h]@1
__int16 mac2; // [sp+Eh] [bp-2h]@1

mac0 = *(_WORD *)mac;
mac1 = *(_WORD *)(mac + 2);
mac2 = *(_WORD *)(mac + 4);
v7 = *content;
result = LocalAlloc(0x40u, (unsigned __int8)*content + 1);
if ( result )
{
for ( i = 1; (char)i <= v7; ++i )
{
v4 = HIBYTE(mac2) ^ content[i];
*((char *)result + i - 1) = v4;
mac2 = mac1 + mac0 * (mac2 + (unsigned __int8)v4);
}
}
return result;
}

El fichero de licencia contiene cifrado el número de serie de disco, la clave es la dirección MAC. Para probar que esto es asi hemos escrito un generador de números de serie:

$ cat lic.py
#!/usr/bin/python

from struct import unpack

mac = '00-0C-29-BE-4B-F7' # get via ipconfig /all
vol = '501B-3C9D' # get via dir

m0, m1, m2 = unpack('3H', mac.replace('-','').decode('hex'))
content = vol.replace('-', '')

result = ''
for c in content:
#print hex(m2)
c = ord(c)
tmp = (m2/256) ^ c
tmp = tmp % 256
result += chr(tmp)
m2 = m1 + m0 * (m2 + c) # tmp
m2 %= 65536

print result.encode('hex')
open('AAF2FC0D.lic','w').write(chr(len(result)) + result)

$ ./lic.py ; hexdump -C AAF2FC0D.lic
c28edbb4f14df712

En nuestro caso la licencia válida es –

$ hexdump -C AAF2FC0D.lic
00000000 08 c2 8e db b4 f1 4d f7 12 |......M..|
00000009

En el caso de la validación web, los pares de usuario/comprobación de licencia puede ser desactivado por completo, porque no se usa para validaciones futuras, así que deshabilitando la comprobación podríamos generar una clave. Pero podemos generar licencias sin ningún parcheado del binario

Para la validación de claves de usuario, RompeMe.exe crea un proceso desde el ejecutable almacenado en la sección de recursos, pide al nuevo proceso que calcule la validación de la clave de usuario, calcula la validación del nombre de usuario, los compara y si los dos coinciden, prueba superada.

Creación de este nuevo proceso:

v4 = GetTickCount();
wsprintfA(ApplicationName, "%08X", v4);
result = CreateFileA(ApplicationName, 0x40000000u, 2u, 0, 2u, 0x20u, 0);
hFile = result;
if ( result != (void *)-1 )
{
WriteFile(hFile, lpBuffer, nNumberOfBytesToWrite, &NumberOfBytesWritten, 0);
CloseHandle(hFile);
StartupInfo.cb = 68;
GetStartupInfoA(&StartupInfo);
wsprintfA(&CommandLine, "%08X", hw_parameters);
CreateProcessA(ApplicationName, &CommandLine, 0, 0, 0, 0, 0, 0, &StartupInfo, &ProcessInformation);
WaitForSingleObject(ProcessInformation.hProcess, 0xFFFFFFFF);
if ( results_received == 1 )
check_results(results);
else
nop();
result = (void *)DeleteFileA(ApplicationName);

El binario de este proceso puede ser obtenido simplemente arrancando el proceso principal en el debugger y copiando el fichero temporal del directorio de trabajo

$ ls -la 00739347
-rwxr-xr-x 1 user user 10240 Oct 24 22:17 00739347

$ sha256sum 00739347
41b2928d4ad584f8b975d051f6e53fe6c3231d786db8be1faaa96c21fde4cbc7 00739347

Después de arrancar, el binario del proceso auxiliar pide al proceso principal parametros:

LRESULT __stdcall StartAddress(LPARAM lParam)
{
return SendMessageA(dword_40400C, 1034u, 0, lParam);
}

El proceso principal responde con un handler del control de dialogo para la clave

case 1034:
PostMessageA(a4, 1035u, wParam, hwnd);
break;

El proceso auxiliar calcula el código de validación y lo devuelve al proceso principal

if ( a2 == 1035 )
{
hWnd = a3;
dword_404014 = a4;
if ( (char *)a4 + (_DWORD)a3 )
solve_quadratic_equation(lpParameter, dword_40400C);


SendMessageA(::hWnd, 13u, 260u, (LPARAM)sz);
SendMessageA(dword_404014, 13u, 260u, (LPARAM)&unk_40411C);
if ( !unk_40411C
|| (v4 = lstrlenA(sz), CharUpperBuffA(sz, v4), v4 < 6)
|| v4 > 6
|| (LOBYTE(v2) = is_hexstring(sz), v2) )
{
result = SendMessageA(hWnd, 16u, 0, 0);
}
else
{
v5 = *(_WORD *)sz;
v6 = 0;
w0 = (char)hex2word(&v5);
v5 = *(_WORD *)&sz[2];
v6 = 0;
w1 = (char)hex2word(&v5);
v5 = *(_WORD *)&sz[4];
v6 = 0;
w2 = (char)hex2word(&v5);
result = check(w0, w1, w2, hWnd, a2);
}
return result;

El algoritmo del código de validación:

La clave debe ser de longitud 6 (hex), cada byte es un parámetro de una ecuación cuadrática

aa * x^2 + bb*x + cc, donde aabbcc es la clave introducida por el usuario

El binario auxiliar lo calcula y manda de vuelta una de las raices de la ecuación

det = (signed int)((double)w1 * (double)w1 + (double)-4 * (double)w0 * (double)w2);
if ( det < 0 )
{
result = SendMessageA(a4, 16u, 0, 0);
}
else
{
PostMessageA(hWnd, 1036u, (signed int)((sqrt((double)det) + (double)w1 * (double)-1) / (double)w0 / (double)2), 0);
result = SendMessageA(a4, 0x10u, 0, 0);
}

El binario principal calcula el valor medio del valor ASCII de las letras del nombre de usuario, y lo compara con el valor de validación del binario auxiliar

.text:004029B4 check_results proc near ; CODE XREF: StartAddress+151#p
.text:004029B4
.text:004029B4 lParam = dword ptr -104h
.text:004029B4 arg_0 = dword ptr 8
.text:004029B4
.text:004029B4 push ebp
.text:004029B5 mov ebp, esp
.text:004029B7 add esp, 0FFFFFEFCh
.text:004029BD lea eax, [ebp+lParam]
.text:004029C3 push eax ; lParam
.text:004029C4 push 104h ; wParam
.text:004029C9 push 0Dh ; Msg
.text:004029CB push username ; hWnd
.text:004029D1 call SendMessageA
.text:004029D6 lea edi, [ebp+lParam]
.text:004029DC xor eax, eax
.text:004029DE mov al, [edi]
.text:004029E0 or al, al
.text:004029E2 jz short loc_402A0B
.text:004029E4 xor edx, edx
.text:004029E6
.text:004029E6 loc_4029E6: ; CODE XREF: check_results+39#j
.text:004029E6 add edx, eax
.text:004029E8 inc edi
.text:004029E9 mov al, [edi]
.text:004029EB cmp al, 0
.text:004029ED jnz short loc_4029E6
.text:004029EF lea esi, [ebp+lParam]
.text:004029F5 sub edi, esi
.text:004029F7 mov eax, edx
.text:004029F9 xor edx, edx
.text:004029FB idiv edi
.text:004029FD xor eax, [ebp+arg_0]
.text:00402A00 or eax, eax
.text:00402A02 jnz short loc_402A0B
.text:00402A04 call winner

Así que la generación de claves es trivial, usaremos la ecuación cuadrática

(x-N)*(x-1)=0
x^2 - (N+1)*x + N = 0

donde N = sum(name)/len(name), entonces la clave será la concatenación de los valores

key = 01 || (255 – N) || N

Generador de claves final:

$ cat kg.py
#!/usr/bin/python

from sys import argv

name = 'pADAwan'
if len(argv) > 1:
name = argv[-1]

sum = 0
for c in name:
sum += ord(c)
avg = sum / len(name)

key = '01%02x%02x' % (255-avg, avg)

print 'Name:', name
print 'Key:', key

Clave valida para nosotros

$ ./kg.py
Name: pADAwan
Key: 01a55a