systemadmin.es » Seguridad » cripto02 de la Campus Party Valencia 2010

cripto02 de la Campus Party Valencia 2010

A principios de agosto se publicaron unos niveles de la Campus Party de Valencia 2010 que no se resolvieron a tiempo:

Por lo que me miré el fichero para Linux, sin poder llegar a resolverlo.

Vemos que es un ELF de 32 bits:

$ file cripto02
cripto02: ELF 32-bit LSB executable, Intel 80386, version 1 (GNU/Linux), statically linked, stripped

Pero no podemos sacar nada directamente con objdump:

$ objdump -d ./cripto02

./cripto02:     file format elf32-i386

Ni con gdb:

$ gdb -q ./cripto02
Reading symbols from /home/jprats/cripto02...
warning: no loadable sections found in added symbol-file /home/jprats/cripto02
(no debugging symbols found)...done.
(gdb)

Ni con readelf:

$ readelf -a cripto02
ELF Header:
  Magic:   7f 45 4c 46 01 01 01 03 00 00 00 00 00 00 00 00
  Class:                             ELF32
  Data:                              2's complement, little endian
  Version:                           1 (current)
  OS/ABI:                            UNIX - Linux
  ABI Version:                       0
  Type:                              EXEC (Executable file)
  Machine:                           Intel 80386
  Version:                           0x1
  Entry point address:               0xc3a270
  Start of program headers:          52 (bytes into file)
  Start of section headers:          0 (bytes into file)
  Flags:                             0x0
  Size of this header:               52 (bytes)
  Size of program headers:           32 (bytes)
  Number of program headers:         2
  Size of section headers:           40 (bytes)
  Number of section headers:         0
  Section header string table index: 0

There are no sections in this file.

There are no sections in this file.

Program Headers:
  Type           Offset   VirtAddr   PhysAddr   FileSiz MemSiz  Flg Align
  LOAD           0x000000 0x00c01000 0x00c01000 0x39a7c 0x39a7c R E 0x1000
  LOAD           0x000328 0x080cc328 0x080cc328 0x00000 0x00000 RW  0x1000

There is no dynamic section in this file.

There are no relocations in this file.

There are no unwind sections in this file.

No version information found in this file.

Tal como indican:

Low level reversing might not be the fastest way to solve this one although it can help.

Mas vale concentrarse en otra cosa, por lo que podemos intentar ver algo con strace:

$ strace -s 128 -fF ./cripto02
execve("./cripto02", ["./cripto02"], [/* 58 vars */]) = 0
old_mmap(0xc3b000, 4096, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0xbffff1a80808e020) = 0xc3b000
readlink("/proc/self/exe", "/home/jprats/cripto02", 4096) = 21
old_mmap(0x8048000, 525507, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0xbfffd6d00808e020) = 0x8048000
mprotect(0x8048000, 525504, PROT_READ|PROT_EXEC) = 0
old_mmap(0x80c9000, 5931, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0xbfffd6d00808e020) = 0x80c9000
mprotect(0x80c9000, 5928, PROT_READ|PROT_WRITE) = 0
old_mmap(0x80cb000, 4904, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0xbfffd6d00808e020) = 0x80cb000
brk(0x80cd000)                          = 0x80cd000
munmap(0xc01000, 241664)                = 0
uname({sys="Linux", node="croscat.systemadmin.es", ...}) = 0
brk(0)                                  = 0x80cd000
brk(0x80cdcd0)                          = 0x80cdcd0
set_thread_area({entry_number:-1 -> 6, base_addr:0x80cd830, limit:1048575, seg_32bit:1, contents:0, read_exec_only:0, limit_in_pages:1, seg_not_present:0, useable:1}) = 0
brk(0x80eecd0)                          = 0x80eecd0
brk(0x80ef000)                          = 0x80ef000
clone(Process 8025 attached
child_stack=0, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x80cd898) = 8025
[pid  8025] clone( <unfinished ...>
[pid  8024] fstat64(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 20), ...}) = 0
Process 8026 attached
[pid  8025] <... clone resumed> child_stack=0, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x80cd898) = 8026
[pid  8024] mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x111000
[pid  8024] write(1, "If you still want to try: \n", 27If you still want to try:
) = 27
[pid  8024] fstat64(0, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 20), ...}) = 0
[pid  8024] mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x112000
[pid  8024] read(0,  <unfinished ...>
[pid  8025] clone( <unfinished ...>
[pid  8026] exit_group(0)               = ?
Process 8026 detached
Process 8027 attached (waiting for parent)
Process 8027 resumed (parent 8025 ready)
[pid  8025] <... clone resumed> child_stack=0, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x80cd898) = 8027
[pid  8025] --- SIGCHLD (Child exited) @ 0 (0) ---
[pid  8027] getuid32()                  = 500
[pid  8025] exit_group(0)               = ?
Process 8025 detached
[pid  8027] exit_group(0)               = ?
Process 8027 detached
<... read resumed> 0x112000, 1024)      = ? ERESTARTSYS (To be restarted)
--- SIGCHLD (Child exited) @ 0 (0) ---
read(0, sasa
"sasa\n", 1024)                 = 5
write(1, "\n", 1
)                       = 1
write(1, "Not the password\n", 17Not the password
)      = 17
exit_group(0)                           = ?
[jprats@croscat ~]$

Entre las llamadas podemos ver un getuid32():

[pid  8027] getuid32()                  = 500

Por lo que podemos ver que ocurre si tenemos otro id, por probar podemos ver que ocurre con uid=0 (ejecutar como root):

# strace -s 128 -Ff ./cripto02
execve("./cripto02", ["./cripto02"], [/* 21 vars */]) = 0
[ Process PID=22711 runs in 32 bit mode. ]
old_mmap(0xc3b000, 4096, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0xc3b000) = 0xc3b000
readlink("/proc/self/exe", "/home/jprats/cripto02", 4096) = 21
old_mmap(0x8048000, 525507, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x8048000
mprotect(0x8048000, 525504, PROT_READ|PROT_EXEC) = 0
old_mmap(0x80c9000, 5931, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0x80000) = 0x80c9000
mprotect(0x80c9000, 5928, PROT_READ|PROT_WRITE) = 0
old_mmap(0x80cb000, 4904, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x80cb000
brk(0x80cd000)                          = 0x80cd000
munmap(0xc01000, 241664)                = 0
uname({sys="Linux", node="stargate.systemadmin.es", ...}) = 0
brk(0)                                  = 0x80cd000
brk(0x80cdcd0)                          = 0x80cdcd0
set_thread_area(0xffcbaa3c)             = 0
brk(0x80eecd0)                          = 0x80eecd0
brk(0x80ef000)                          = 0x80ef000
clone(Process 22712 attached (waiting for parent)
Process 22712 resumed (parent 22711 ready)
child_stack=0, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0) = 22712
[pid 22712] clone( <unfinished ...>
[pid 22711] fstat64(1, Process 22713 attached
 <unfinished ...>
[pid 22712] <... clone resumed> child_stack=0, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0) = 22713
[pid 22711] <... fstat64 resumed> {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 0), ...}) = 0
[pid 22712] clone( <unfinished ...>
[pid 22711] mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0 <unfinished ...>
[pid 22713] exit_group(0)               = ?
Process 22713 detached
Process 22715 attached
[pid 22712] <... clone resumed> child_stack=0, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0) = 22715
[pid 22711] <... mmap2 resumed> )       = 0xfffffffff7f8e000
[pid 22712] --- SIGCHLD (Child exited) @ 0 (0) ---
[pid 22711] write(1, "If you still want to try: \n", 27If you still want to try:
 <unfinished ...>
[pid 22715] getuid32( <unfinished ...>
[pid 22712] exit_group(0)               = ?
Process 22712 detached
[pid 22711] <... write resumed> )       = 27
[pid 22715] <... getuid32 resumed> )    = 0
[pid 22711] --- SIGCHLD (Child exited) @ 0 (0) ---
[pid 22715] open("/dev/console", O_RDONLY|O_NOCTTY <unfinished ...>
[pid 22711] fstat64(0,  <unfinished ...>
[pid 22715] <... open resumed> )        = 3
[pid 22711] <... fstat64 resumed> {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 0), ...}) = 0
[pid 22715] rt_sigaction(SIGINT, {0x1400000008048258, [], SA_STACK|0x4a2b8},  <unfinished ...>
[pid 22711] mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0 <unfinished ...>
[pid 22715] <... rt_sigaction resumed> {SIG_DFL}, 8) = 0
[pid 22711] <... mmap2 resumed> )       = 0xfffffffff7f8d000
[pid 22715] rt_sigaction(SIGTERM, {0x1400000008048258, [], SA_STACK|0x4a2b8},  <unfinished ...>
[pid 22711] read(0,  <unfinished ...>
[pid 22715] <... rt_sigaction resumed> {SIG_DFL}, 8) = 0
[pid 22715] rt_sigaction(SIGQUIT, {0x1400000008048258, [], SA_STACK|0x4a2b8}, {SIG_DFL}, 8) = 0
[pid 22715] rt_sigaction(SIGTSTP, {0x1400000008048258, [], SA_STACK|0x4a2b8}, {SIG_DFL}, 8) = 0
[pid 22715] ioctl(3, KDSETLED, 0)       = 0
[pid 22715] nanosleep({0, 80000000}, NULL) = 0
[pid 22715] ioctl(3, KDSETLED, 0x2)     = 0
[pid 22715] nanosleep({0, 80000000}, NULL) = 0
[pid 22715] ioctl(3, KDSETLED, 0)       = 0
[pid 22715] nanosleep({0, 80000000}, NULL) = 0
[pid 22715] ioctl(3, KDSETLED, 0x2)     = 0
[pid 22715] nanosleep({0, 80000000}, NULL) = 0
[pid 22715] ioctl(3, KDSETLED, 0)       = 0
[pid 22715] nanosleep({0, 80000000}, NULL) = 0
[pid 22715] ioctl(3, KDSETLED, 0x2)     = 0
[pid 22715] nanosleep({0, 300000000}, NULL) = 0
[pid 22715] ioctl(3, KDSETLED, 0)       = 0
[pid 22715] nanosleep({0, 80000000}, NULL) = 0
[pid 22715] ioctl(3, KDSETLED, 0x2)     = 0
[pid 22715] nanosleep({0, 80000000}, NULL) = 0
[pid 22715] ioctl(3, KDSETLED, 0)       = 0
[pid 22715] nanosleep({1, 200000000}, C <unfinished ...>
Process 22715 detached

Exiting!

Apreciamos como crea un proceso que hace parpadear (los ioctl con los nanosleep) el numlock, con la ayuda del OpenOffice lo podemos dibujar los tiempos de espera de la llamada nanosleep:

tiempo-nanosleeps

tiempo-nanosleeps

Podemos apreciar que existe una serie que se repite tres veces, por lo que de las 267 muestras nos quedamos con 89:

serie cripto02

serie cripto02

Llegados a este punto pensé en alguna codificación bipolar por el hecho de tener tres valores posibles, descartando Morse por este mismo motivo (este es el problema de mi razonamiento). También consideré el cambio de estado del LED:

Con el cambio de estado del LED

Con el cambio de estado del LED

Pero no tenia sentido por tener 89 nuestras, un numero primo:

$ for i in $(seq 1 89); do echo "89/$i" | bc -l; done | less | grep "\.00"
89.00000000000000000000
1.00000000000000000000

Con 88 tenia más sentido por ser divisible por 8, con lo que serían 11 caracteres:

$ echo "88/8" | bc -l
11.00000000000000000000

Por lo que imaginé que tendría algun sentido con la diferéncia entre el valor con el anterior:

Restando el valor actual del anterior

Restando el valor actual del anterior

Pero tampoco apareció nada, por lo que consulté con un amigo y tampoco llegué a ningún lado: solo quedaba reconocer la derrota:

EPIC FAIL

EPIC FAIL

Ahora, han publicado la solución de este puzzle por lo que me he lanzado a verla: La solución que pasaba por el Morse, que había descartado erróneamente por los tres estados, conjuntamente con el estado del LED según estas reglas que he visto en la solución:

  • Si el LED esta apagado y la pausa es de 1,2 segunos es un separador
  • Si el LED esta encendido y la pausa es de 0,08 segundos es un punto
  • Si el LED esta encendido y la pausa es de 0,3 es una raya

Además me he dado cuenta que vi mal los ceros, por lo que los gráficos están mal:

5832  nanosleep({0, 300000000}, NULL)   = 0
5832  nanosleep({0, 80000000}, NULL)    = 0

Traduciendo del morse según estas reglas se obtiene FUNKEYBOARDMORSE, el cual pasamos al programa tres veces seguidas obtenemos lo siguiente:

$ ./cripto02 FUNKEYBOARDMORSEFUNKEYBOARDMORSEFUNKEYBOARDMORSE
\x27\x68\x7f\x7b\x73\x75\x21\x72\x70\x60\x7c\x7e\x63\x3f\x6e\x73\x76\x62\x7b\x67\x36\x64\x72\x63\x61\x26\x2c\x28\x6f\x21\x3c\x29\x33\x21\x27\x24\x2b\x79\x2b\x3c\x61\x2a\x68\x34\x63\x28\x7f\x32

El cual tiene un tamaño de 48 bytes:

$ perl -e 'print "\x27\x68\x7f\x7b\x73\x75\x21\x72\x70\x60\x7c\x7e\x63\x3f\x6e\x73\x76\x62\x7b\x67\x36\x64\x72\x63\x61\x26\x2c\x28\x6f\x21\x3c\x29\x33\x21\x27\x24\x2b\x79\x2b\x3c\x61\x2a\x68\x34\x63\x28\x7f\x32";' |wc -c
48

Igual que la cadena que le hemos pasado al programa:

$ echo -n "FUNKEYBOARDMORSEFUNKEYBOARDMORSEFUNKEYBOARDMORSE" | wc -c
48

Luego se debe descifrar con un XOR, como se muestra en la solución, se podría usar algo así:

chr(ord(c)^ord(code[i]))

Con lo que obtendríamos:

a=106,c=1283,m=6075,s=0, the solution is x,y,z,w

De aquí se supone que deberíamos saber (o encontrar mediante google) que estos valores de inicialización son de un generador pseudo-aleatorio llamado Linear Congruential Generator que se define según esta formula:

Linear Congruential Generator

Linear Congruential Generator

Nos indica que la semilla es cero (s=0), por lo que:

x = (106*0)+1283%6075 = 1283
y = (106*1283)+1283%6075 = 3631
z = (106*3631)+1283%6075 = 3444
w = (106*3444)+1283%6075 = 1847

La solución es este vector:

(x,y,z,w)=(1283,3631,3444,1847)

Otra vez será y felicidades a los que lo consiguieron!

Relacionados

Imprimir Imprimir

2 comments to “cripto02 de la Campus Party Valencia 2010”

  1. Una lástima que te quedaras en la parte de morse. La verdad es que las ideas que se te ocurrieron son muy buenas a pesar de que la suposición de partida no era buena, intenté que no fuera demasiado idea feliz así que lo de morse era mucho más directo que jugar con codificaciones más exóticas o con alguna secuencia cifrada.

    Muchas gracias por poner aquí tu experiencia y la traducción :)

  2. Perder forma también parte del juego, pero ha estado muy entretenido. A ver si una próxima vez sale mejor la cosa :)

Deja un comentario:

XHTML - Tags permitidos:<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>