sábado, 13 de agosto de 2011

WriteUp - forensicK - ptrace.net

After taking part in Sibctf quals, I saw a related tweet from Ptrace Security group that took me to their website, where I found this nice challenge:


http://ptrace.net/files/challenges/4.txt

Here is my solution, probably not the best one:


We are given a pcap network capture, which we can know by verifying the magic number, but when we try to open it, Wireshark says it is corrupted. Only six packets are viewed, which we assume to be correct.



We open the file in 010 Editor and apply the pcap template by Didier Stevens to see what is happening. The file fomat seems to be quite easy... just a header for the file and a little header for each packet consisting of:


timestamp seconds
timestamp microseconds
number of octets of packet saved in file
actual length of packet

After playing for a while with 010 Editor I realized that some packets were overlapping the next ones. The sizes reflected for each packet in the cap metadata seemed to match the ones in the IP header and TCP sequences, so I guess some bytes were deleted from the original capture, although I was not able to determine which ones for sure.


For example, in the next image we see the 6th packet overlaps 10 bytes from the next packet. In fact it should finish in offset 43Ch, and then begin the header for 7th packet, but according to its header, it finishes in 446h:"

Since the metadata in the pcap file let us indicate the size of the packet that was captured, I decided to use that field to fix the file.


The problem is that the header for each packet has no fixed token, so we must guess where each packet begins. In this case it was not difficult because in the network capture there was only two MAC addresses involved, so we used them to identify each packet.


I admit I did it manually the first time, but since there are more than a handful, later I made this python script.



import re
import mmap
import binascii
from struct import *

def nextframe(map, last, pat1, pat2):
next1=map.find(pat1,last+1)
next2=map.find(pat2,last+1)
if next1 < 0:
if next2 <0:
next=-1
else:
next=next2
else:
if next2 < 0:
next=next1
else:
next=min(next1,next2)
return next

with open("4_forensicK.bin.cap", "r+") as f:
# memory-map the file, size 0 means whole file
map = mmap.mmap(f.fileno(), 0)
map2 = map
previous=0
next=0
#I only expect 1 unicast comunication on layer 2, with only IP packets
#so I use 2 different ethernet frame headers to locate frames
pat1=binascii.unhexlify('00c049d2e4640016d32987a10800')
pat2=binascii.unhexlify('0016d32987a100c049d2e4640800')
next=nextframe(map,next,pat1,pat2)

while True:
previous=next
captnum=unpack('<i',map[next-8:next-4])[0]
next=nextframe(map,next,pat1,pat2)
if next!=-1:
distanciapaq=next-previous-16 #Each packet has a 16 byte header
if distanciapaq != captnum:
if distanciapaq > captnum:
print "Next packet is further than expected. There may be more layer 2 comunications. Review manually."
else:
#Actual packet is overlapping next one
map2[previous-8:previous-4]=pack('<i',distanciapaq)
print 'Cap fixed at offset', previous-8,' Previous capture size', captnum, 'actual size', distanciapaq
else:
break

map.close()



Be careful, it overwrites the input file. You don't want to tamper your evidences ;-)


After that, I could open the file without problems, so I reviewed the connections, decoded connections to tcp port 8086 as HTTP and could see the server answers uncompressed to find the flag:



So, the flag is: 3406654e25675f56ce7922cf5ec12952


Of course, it could be resolved much faster just by following the hints in the http queries, visible with strings, and uncompressing the gzip streams in the answers that followed them, but we are not here for the money, you know!! ;-)

jueves, 21 de julio de 2011

WriteUp - fatherapple - wgsbd2

Nos proporcionan el ejecutable sig_32.


Al ejecutarlo muestra el siguiente mensaje y se queda esperando:



Al pulsar Ctrl-C para salir, nos da un mensaje extra:



Los números que muestra cambian en cada ejecución, pero no sabemos mucho más, así que pasamos a analizarlo con IDA. Por suerte, es un binario muy sencillo. Vemos aquí la función principal:



Podemos ver que se realiza un fork y las actividades básicas tanto del proceso padre (salida del fork distinta de cero) como del hijo. No vemos directamente que se imprima el mensaje "Hack ___ planet", pero recordamos que ha salido al pulsar Ctrl-C, por lo que debe imprimirse en el manejador de la señal, así que nos fijamos en ese punto.


Efectivamente el proceso padre está redefiniendo la señal 2 (SIGINT), por lo que asumimos que en esa función se muestra el comentado "Hack ___ planet". Por su parte, el proceso hijo redefine la señal 14 (SIGALRM) y después muestra el mensaje principal, donde vemos que los números de manzanas que nos indicaba son el pid del proceso padre y el del hijo.


Queremos ver qué hace este proceso al recibir la señal SIGALRM, así que vamos a la función legendary:



Curioso... fija fflussh como el manejador de la señal 12 (SIGUSR2), espera 1 segundo y vuelve a cambiarlo a la función boobs. Sólo por el nombre de la función, miramos primero esta última y vemos que no hace nada (¿WTF? juegan con mis sentimientos como si fuera una marioneta ;-)). fflussh en cambio parece contener algún mensaje oculto.


Como ya estoy vago, vamos a probar si el análisis que hemos hecho hasta ahora está bien... necesitamos enviar al proceso hijo, que amablemente nos indica su pid, la señal 14, y en menos de 1 segundo (pero dándole tiempo a fijar la nueva señal) enviar la señal 12:



It works!!! :-)

miércoles, 20 de julio de 2011

WriteUp - therabbit - wgsbd2

Nos proporcionan el ejecutable therabbit.exe.


Si lo ejecutamos, descarga del servidor del reto un fichero llamado metienescontento.arj, pero en seguida vemos mediante un editor hexadecimal que es un RAR, y al intentar descomprimirlo, que tiene contraseña.


Atacar por fuerza bruta la contraseña del RAR es lentísimo, así que decidimos analizar el ejecutable porque parece lógico que contenga la contraseña para descomprimir el archivo que ha bajado.


Analizando las cadenas contenidas en el binario vemos que está comprimido con UPX:


>strings -q therabbit.exe|head
!This program cannot be run in DOS mode.
\>%
\>J
|)E
\>!
Rich
UPX0
UPX1
.rsrc
3.00

Descargamos el packer de su web http://upx.sourceforge.net/ y lo utilizamos para extraer el binario original:



El ejecutable desempaquetado resulta ser un script de AutoIt transformado en ejecutable, como podemos observar en las cadenas del ejecutable o en las propiedades del mismo:



Investigando un poco, llegamos a esta entrada en el fabuloso blog de Didier Stevens:


http://blog.didierstevens.com/2007/10/02/autoit-malware-revisited/

Siguiendo sus instrucciones, nos bajamos la versión de AutoIt apropiada y mediante Exe2Aut recuperamos el script original, por suerte sin contraseña.


Es muy fácil localizar la parte interesante, y a primera vista llama la atención una variable con el valor "car411o" que apesta a contraseña. Además vemos otras 2 variables que tampoco se utilizan, con los valores "unsoldo" y "fai":



Me puse en la piel de B4RRe1R0, me impregné de acento gallego y la solución salió sola: "faiunsoldocar411o".


Con esta contraseña podemos descomprimir el fichero descargado y obtenemos el token:


>cat cozashula.txt
eze_ezpanyolitoSexydem0da

WriteUp - 90sdancing - wgsbd2

Lo primero que tienes que hacer es leer la impresionante solución de Sherab Giovannini a este reto:


http://www.reversingcode.com/f1l3s/90sdancing.by.Sherab.Giovannini.zip

Una vez hecho eso, si su capacidad te desborda (como a mí), igual te interesa esta solución de andar por casa.


Nos proporcionan el binario crackme.exe. Analizando las cadenas de texto incluidas en el mismo enseguida vemos que es un ejecutable creado a partir de un script de python con py2exe.



>strings crackme.exe|grep -i py2exe
PY2EXE_VERBOSE
PY2EXE_VERBOSE
py2exe
C:\Python24\lib\site-packages\py2exe\boot_common.pyR
This file and also _memimporter.pyd is part of the py2exe package.

Buscando en Google un rato, localizamos un script que nos permite deshacer la conversión:


http://osdir.com/ml/python.py2exe/2007-11/msg00030.html

Como indican en la misma página, hay que ejecutarlo con la versión de python que se utilizó para generar el ejecutable, así que instalo en mi máquina python 2.4 y lo probamos:



>\Python24\python.exe exe2py.py crackme.exe
HEADER: 0x78563412 0 0 3039
ZipArchive:
Found code object: C:\Python24\lib\site-packages\py2exe\boot_common.py
Extracting to: boot_common.pyc
Found code object:
Disassembly:
1 0 LOAD_CONST 0 (None)
3 IMPORT_NAME 0 (zipextimporter)
6 STORE_NAME 0 (zipextimporter)
9 LOAD_NAME 0 (zipextimporter)
12 LOAD_ATTR 1 (install)
15 CALL_FUNCTION 0
18 POP_TOP
19 LOAD_CONST 0 (None)
22 RETURN_VALUE
Found code object: crackme.py
Extracting to: crackme.pyc

Perfecto, ya tenemos un script de python compilado, así que vamos a la web depython.net para obtener el código fuente:



Ya podemos ver claramente que pasando como parámetro "Captain hollywood" obtendremos el token que buscábamos: "find another way".

miércoles, 6 de julio de 2011

Quals SiBCTF 2011


Este fin de semana se han celebrado las quals del SiBCTF 2011. Hemos participado y hemos conseguido clasificarnos para la final del SiBCTF 2011. En la final estarán 8 equipos rusos y 8 equipos no rusos. Se celebrará el 11 de Septiembre del 2011. Los equipos que estarán en la final serán:

Lista de equipos rusos invitados a las finales del “SiBCTF 2011”

  • Leet More
  • HD || ! HD
  • PeterPen
  • MiT
  • HackerMayCry
  • Koibasta
  • [censored]
  • Honeypot

Lista de equipos no rusos invitados a las finales del “SiBCTF 2011”

  • Plaid Parliament of Pwning
  • disekt
  • SGM48
  • tiwfrags
  • shell-storm
  • Keysec
  • pwnfu
  • pentsec

Intentaremos hacerlo lo mejor posible. Hasta ese día, a practicar ;-)

domingo, 1 de mayo de 2011

WriteUp - Desafío 13 - H4ckc0nt3st GSIC 2011

Examinado el código html de la página vemos que se lanza un script en javascript bastante llamativo:

Después de hacer un par de pruebas vemos que el código está ofuscado y pasándolo por url decode no conseguimos nada.

La solución rápida y sencilla es apoyarnos en un plugin que existe para firefox llamado javascript deobfuscator que nos muestra el código directamente desde el motor del navegador.

Lo ejecutamos y nos muestra el siguiente código:

function anonymous() {
c = document.getElementById("clave").value;
if (c != "J4v4scr1p7fr0mh311") {
alert("clave incorrecta!");
} else {
document.acceso.submit();
}
}

Solución: J4v4scr1p7fr0mh311

WriteUp - Desafío 16 - H4ckc0nt3st GSIC 2011

Nos presentan un binario llamado ‘resiste’, a ver qué podemos hacer con él.


Si llamamos al binario sin parámetros o con más de uno, nos responde “¿Así, sin más?”, mientras que si ponemos solo un parámetro con cualquier valor tampoco le gusta demasiado y nos lo indica con un “:(”.


Abrimos el binario con el IDA y vemos que no tiene definida la función main, por lo que en su lugar nos lleva a start, que llama a __libc_start_main.



Vamos a sub_8048470 para observar el código llamado y vemos el siguiente esquema:



El programa descifra una sección del código en memoria y salta a él, por lo que sólo con análisis estático no vamos a poder hacer mucho y decidimos usar gdb.
Arrancamos con un parámetro para que el flujo del programa nos lleve hasta donde nos interesa:


# gdb -q --args ./resiste AAAAAAAAAA
(no debugging symbols found)

Con mis limitados conocimientos de gdb, suelo poner ‘start’ para cargar el programa y poder desensamblar el programa, pero haciéndolo en este caso lanza el programa completo, así que para evitarlo tengo que poner algún punto de ruptura. Como hemos visto antes una llamada a mmap() y está situada cerca del punto que nos interesa, la utilizo para fijar el breakpoint:


(gdb) b mmap
Function "mmap" not defined.
Make breakpoint pending on future shared library load? (y or [n]) y

Breakpoint 1 (mmap) pending.
(gdb) run
Starting program: /root/resiste AAAAAAAAAA
(no debugging symbols found)
(no debugging symbols found)
(no debugging symbols found)

Breakpoint 1, 0xb7ef3f80 in mmap () from /lib/tls/i686/cmov/libc.so.6

Comprobamos dónde nos encontramos para situarnos y poder fijar el siguiente punto de ruptura donde nos interese, en la zona de memoria desde la que se ha llamado a mmap():


(gdb) where
#0 0xb7ef3f80 in mmap () from /lib/tls/i686/cmov/libc.so.6
#1 0x080484ec in ?? ()
#2 0xb7e2c685 in __libc_start_main () from /lib/tls/i686/cmov/libc.so.6
#3 0x080483c1 in ?? ()

Intento obtener el código desensamblado con el comando que utilizo habitualmente para ello ‘disas’, pero no lo acepta por encontrase esa zona de memoria fuera de las funciones definidas, por lo que toca googlear un poco hasta localizar que podemos mostrar la memoria decodificándola como instrucciones, y este método sí nos funciona en este caso:


(gdb) disas 0x080484ec
No function contains specified address.
(gdb) x/20i 0x080484ec
0x80484ec: xor %edx,%edx
0x80484ee: cmp $0xffffffff,%eax
0x80484f1: je 0x8048576
0x80484f7: nop
0x80484f8: movzbl 0x80486c0(,%edx,4),%ecx
0x8048500: xor %edx,%ecx
0x8048502: mov %cl,(%eax,%edx,1)
0x8048505: add $0x1,%edx
0x8048508: cmp $0xbe,%edx
0x804850e: jne 0x80484f8
0x8048510: mov 0x4(%ebx),%edx
0x8048513: mov %eax,0x8049b84
0x8048518: mov %edx,(%esp)
0x804851b: call *%eax
0x804851d: test %eax,%eax
0x804851f: jne 0x8048547
0x8048521: mov 0x4(%ebx),%eax
0x8048524: movl $0x8048664,0x4(%esp)
0x804852c: mov %eax,0x8(%esp)
0x8048530: mov 0x8049b80,%eax

De esta forma ya podemos localizar la llamada al código desempaquetado (call *%eax) y fijamos un punto de ruptura en esa instrucción para poder entrar posteriormente en el método llamado.


(gdb) b *0x804851b
Breakpoint 2 at 0x804851b
(gdb) continue
Continuing.

Breakpoint 2, 0x0804851b in ?? ()
(gdb) stepi
0xb7f82000 in ?? ()

Ya estamos dentro del código desempaquetado y podemos ver el código desensamblado del método:


(gdb) x/70i 0xb7f82000
0xb7f82000: push %ebp
0xb7f82001: mov %esp,%ebp
0xb7f82003: sub $0x20,%esp
0xb7f82006: movb $0xf4,-0x17(%ebp)
0xb7f8200a: movb $0xee,-0x1f(%ebp)
0xb7f8200e: movb $0xcb,-0x20(%ebp)
0xb7f82012: movb $0xd3,-0x14(%ebp)
0xb7f82016: movb $0xce,-0xd(%ebp)
0xb7f8201a: movb $0xe2,-0x1d(%ebp)
0xb7f8201e: movb $0xc4,-0x1c(%ebp)
0xb7f82022: movb $0x0,-0x5(%ebp)
0xb7f82026: movb $0xec,-0x1e(%ebp)
0xb7f8202a: movb $0xe9,-0xc(%ebp)
0xb7f8202e: movb $0xe8,-0x15(%ebp)
0xb7f82032: movb $0xcf,-0xb(%ebp)
0xb7f82036: movb $0xe6,-0x1b(%ebp)
0xb7f8203a: movb $0xe8,-0x13(%ebp)
0xb7f8203e: movb $0xf4,-0x17(%ebp)
0xb7f82042: movb $0xee,-0x18(%ebp)
0xb7f82046: movb $0xc6,-0x12(%ebp)
0xb7f8204a: movb $0xe2,-0xa(%ebp)
0xb7f8204e: movb $0xeb,-0x1a(%ebp)
0xb7f82052: movb $0xeb,-0x19(%ebp)
0xb7f82056: movb $0xf3,-0x16(%ebp)
0xb7f8205a: movb $0xe6,-0x9(%ebp)
0xb7f8205e: movb $0xf1,-0x8(%ebp)
0xb7f82062: movb $0xf3,-0x10(%ebp)
0xb7f82066: movb $0xe6,-0xf(%ebp)
0xb7f8206a: movb $0xe2,-0x7(%ebp)
0xb7f8206e: movb $0xf5,-0xe(%ebp)
0xb7f82072: movb $0xd4,-0x11(%ebp)
0xb7f82076: movb $0xe9,-0x6(%ebp)
0xb7f8207a: push %ecx
0xb7f8207b: push %edi
0xb7f8207c: push %esi
0xb7f8207d: xor %eax,%eax
0xb7f8207f: mov $0xffffffff,%ecx
0xb7f82084: mov 0x8(%ebp),%edi
0xb7f82087: xor %eax,%eax
0xb7f82089: cld
0xb7f8208a: repnz scas %es:(%edi),%al
0xb7f8208c: not %ecx
0xb7f8208e: dec %ecx
0xb7f8208f: cmp $0x1b,%ecx
0xb7f82092: je 0xb7f8209b
0xb7f82094: mov $0xffffffff,%ecx
0xb7f82099: jmp 0xb7f820b7
0xb7f8209b: mov $0x1b,%ecx
0xb7f820a0: xor %eax,%eax
0xb7f820a2: lea -0x20(%ebp),%eax
0xb7f820a5: mov %eax,%edi
0xb7f820a7: mov 0x8(%ebp),%esi
0xb7f820aa: mov (%edi),%al
0xb7f820ac: xor (%esi),%al
0xb7f820ae: xor $0x87,%al
0xb7f820b0: jne 0xb7f820b7
0xb7f820b2: inc %edi
0xb7f820b3: inc %esi
0xb7f820b4: dec %ecx
0xb7f820b5: jne 0xb7f820aa
0xb7f820b7: mov %ecx,%eax
0xb7f820b9: pop %esi
0xb7f820ba: pop %edi
0xb7f820bb: pop %ecx
0xb7f820bc: leave
0xb7f820bd: ret
0xb7f820be: add %al,(%eax)
0xb7f820c0: add %al,(%eax)
0xb7f820c2: add %al,(%eax)
0xb7f820c4: add %al,(%eax)

Vemos cómo se colocan una serie de bytes en un array, en lo que probablemente sea la solución que necesitamos cifrada, pero en su momento no veíamos claro dónde o si se descifraba esta cadena, aunque sí parece que coge la cadena byte a byte y hace algún xor con cada byte, así que fijamos un breakpoint y recogemos la cadena cifrada de la memoria:


(gdb) b *0xb7f820b7
Breakpoint 3 at 0xb7f820b7
(gdb) continue
Continuing.

Breakpoint 3, 0xb7f820b7 in ?? ()
(gdb) x/32b $ebp-0x20
0xbfa37bd8: 0xcb 0xee 0xec 0xe2 0xc4 0xe6 0xeb 0xeb
0xbfa37be0: 0xee 0xf4 0xf3 0xe8 0xd3 0xe8 0xc6 0xd4
0xbfa37be8: 0xf3 0xe6 0xf5 0xce 0xe9 0xcf 0xe2 0xe6
0xbfa37bf0: 0xf1 0xe2 0xe9 0x00 0xc4 0x7c 0xa3 0xbf
(gdb)

Cogemos la cadena hasta el byte nulo y le aplicamos fuerza bruta buscando la respuesta que necesitamos mediante el script:


import hashlib
import binascii
from itertools import cycle, izip

def mixor (ss, key):
 key = cycle(key)
 return ''.join(chr(ord(x) ^ ord(y)) for (x,y) in izip(ss, key))

cadena='cbeeece2c4e6ebebeef4f3e8d3e8c6d4f3e6f5cee9cfe2e6f1e2e9'

for a in range(0,256):
 print str(a)+' '+mixor(binascii.unhexlify(cadena),chr(a))

Revisando la salida encontramos la respuesta, correspondiente al xor con 135.



Podemos comprobar que funciona introduciéndola como parámetro:


# ./resiste LikeCallistoToAStarInHeaven
Efectivamente, la clave es LikeCallistoToAStarInHeaven. Ahí te he visto fino yo ;)

Analizando posteriormente el código, comprobamos que la cadena no se descifra en memoria en ningún momento, sino que se hace un xor byte a byte con el parámetro introducido por el usuario, y posteriormente con 0x87(135). Dadas las propiedades de la operación xor, si la clave es correcta, el resultado será 0 para cada byte.


PD: Este es el último de los retos que resolví en este concurso, así que aprovecho para agradecérselo a los "culpables" del reto y unas excelentes jornadas... ¡¡Muchas gracias a Miguel Gesteiro y a la gente de GSI Coruña!!. Gracias también a la gente de HackPlayers y BatchDrake, que sé que aportaron pruebas. Si me he dejado a alguien no dudéis en decírmelo, por favor :-).

WriteUp - Desafío 7 - H4ckc0nt3st GSIC 2011

Nos encontramos ante un archivo de tipo PDF (Descargar). Al abrirlo observamos lo siguiente:


Decidimos usar PDFStreamDumper para analizar detenidamente todo el PDF. Una
vez abierto y revisándolo nos encontramos con un Javascript:


Usamos la opción y pulsamos . Al momento obtenemos

la clave del desafío:


sábado, 30 de abril de 2011

WriteUp - Desafío 15 - H4ckc0nt3st GSIC 2011

En este desafío nos presentan un servicio de correo web sencillo y nos piden que obtengamos la contraseña del administrador.


Nos registramos en el servicio y curioseamos un poco para ver a qué nos enfrentamos… Tenemos un sencillo formulario para enviar mensajes a otros usuarios de la plataforma, una opción para revisar nuestros datos en la que podemos observar nuestra contraseña sin cifrar e incluso un amable mensaje de nuestro administrador indicándonos que si tenemos cualquier problema le escribamos a su cuenta ‘administrador’.


Parece que nos está pidiendo a gritos un XSS (si es que se le puede llamar así en este caso, ya que inyectamos código pero apuntando al mismo site), así que es lo primero que probamos, y de paso vemos el formato que necesitamos para enviar mensajes, que es autoexplicativo:


https://10.20.63.1:6666/desafios/MensajeriaWEB/enviar.hc?para=ram&asunto=prueba&cuerpo=<script>alert('xss')</script>

Hemos tenido suerte, porque al abrir el mensaje se nos abre el MessageBox esperado :-). Ahora tenemos que preparar el definitivo.


Antes de enviar el mensaje definitivo, nos creamos una segunda cuenta e hicimos pruebas entre ellas, para que no cantaran demasiado todas nuestras pruebas erróneas en el buzón del administrador, pero aquí pondremos los finales.


En primer lugar preparamos la url que queremos que visite nuestro administrador. Queremos robarle la cookie de sesión para poder ir al panel y obtener su contraseña, y nos valemos de la propia plataforma para que nos la envíe. Es decir, queremos que nos envíe un mensaje con su cookie, por lo que siguiendo el formato que hemos visto, debería visitar una url similar a la siguiente:


https://10.20.63.1:6666/desafios/MensajeriaWEB/enviar.hc?para=ram&asunto=tuclave&cuerpo=AQUI_LA_COOKIE

Para poder añadir el valor de la cookie a esa url debemos obtenerlo mediante javascript, y además no estamos seguros de que el administrador vaya a pinchar en nuestros enlaces, así que preferimos redirigirle automáticamente, con lo que el cuerpo de nuestro mensaje debe ser algo como esto:


<script>document.location="https://10.20.63.1:6666/desafios/MensajeriaWEB/enviar.hc?para=ram&asunto=tuclave&cuerpo="+document.cookie</script>

Ahora sólo tenemos que enviarle el script, para lo que utilizamos la siguiente url, que envía nuestro mensaje con el payload deseado. Notar que en dicho payload ha habido que codificar los caracteres especiales en una url (?, =, &) para que no fueran interpretados en el primer envío por enviar.hc:


https://10.20.63.1:6666/desafios/MensajeriaWEB/enviar.hc?para=administrador&asunto=prueba&cuerpo=<script>document.location="https://10.20.63.1:6666/desafios/MensajeriaWEB/enviar.hc%3fpara%3dram%26asunto%3dtuclave%26cuerpo%3d"+document.cookie</script>

Ya hemos enviado el mensaje, ahora sólo nos queda esperar que el administrador lea sus mensajes y no tenga filtros especiales que impidan que funcione nuestro ataque. Esto es lo que más cuesta del desafío, porque el administrador se estaba echando una merecida siesta después del trabajo bien hecho ;-).


Cuando por fin accede, recibimos el correo que esperábamos con la cookie del administrador, la sustituimos por la nuestra en el navegador y accedemos al panel. Vamos a la opción de revisar la contraseña y conseguimos nuestro objetivo:



Introducimos ‘m41l_XSS’ en el formulario de la prueba y superamos el desafío.

WriteUp - Desafío 12 - H4ckc0nt3st GSIC 2011

En este desafío nos encontramos ante la siguiente página:

Introduciendo cualquier cosa en el primer campo y dando a “continuar” nos da el mensaje “No es lo que busco…”, mientras que rellenar el segundo campo y pulsar “desvelar” no tiene efecto aparente.

Recargando la página va cambiando la imagen del conejo, que vemos que tiene un nombre con un patrón numérico, por lo que decidimos descargar todas las imágenes y ver a dónde nos llevan.
import urllib2

base='https://10.20.63.1:6666/desafios/12/conejos/rabbit' #01.png

for a in range(1,9):
 fich=open('rabbit0'+str(a)+'.png','wb')
 page=urllib2.urlopen(base+'0'+str(a)+'.png')
 cont=page.read()
 fich.write(cont)
 fich.close()


for a in range(10,100):
 fich=open('rabbit'+str(a)+'.png','wb')
 page=urllib2.urlopen(base+str(a)+'.png')
 cont=page.read()
 fich.write(cont)
 fich.close()

De esta forma llegamos hasta el conejo 40. Mi script cutre no trata los errores, así que no ha seguido más allá, por lo que decido probar algún valor más manualmente y enseguida localizo una imagen algo rara en el número 100. Además, mirando alrededor de esta última encontramos también al conejo 99.

Viendo la pinta que tiene rabbit100.png, decidimos mirar su contenido mediante un editor hexadecimal, aunque aquí sólo mostramos la salida ASCII:

Parece que hay algún tipo de script dentro de la paleta de colores de la imagen, ahora sólo falta saber qué hacer con él.

Para ello miramos el código fuente de la página y vemos que los dos botones presentes en la página hacen ejecutan funciones javascript:
  window.onload = function(){
   document.getElementById("gtk").onclick = getKey;
   document.getElementById("dec").onclick = decrypt;
  }
 </script>
 <br/>
 <br/>
 <br/>
 <div class="tCentrado">
  <img width="150px" height="150px" src="./desafios/12/conejos/rabbit03.png"><br/>
  1. sigue al conejo para encontrar la llave de la primera puerta<br/>
  <br/>
  llave: <input type="text" value="" id="source">
  <input id="gtk" type="button" value=" continuar ">
  <input id="key" type="hidden" name="key" value=""><br/>
  <br/>
  2. tras la primera puerta encontrarás una segunda, que se abre con una nueva llave<br/>
  <br/>
  llave: <input type="text" id="dkey" name="dec">
  <input id="dec" type="button" value=" desvelar ">
 </div>

De momento necesitamos superar el primer paso, así que nos fijamos en la función getkey, que coge el dato introducido en el campo de texto y se lo pasa a la función loadData:

function getKey(){
  var source = document.getElementById("source").value;
  loadData(source,function(x){eval(x);estego();});
}

Viendo la definición de esta función parece que el primer parámetro que espera es un nombre de fichero:

function loadData(strFilename, fncCallback)
Como no tenemos ningún fichero más que los propios conejos, introducimos en el cuadro de texto la url del conejo número 100 y al darle a continuar recibimos el mensaje “funciona!”.
Esto tiene buena pinta, y como habíamos visto que el contenido de la imagen recordaba a un script, revisamos los que contiene la página web en el navegador y vemos que se ha añadido uno correctamente:

El script añadido completo es:

var xFakeOne = function (){alert("Lets make the image bigger!");}; estego = function(){document.getElementById("key").value="jjss11";};var z = function whatElseYouExpect(){alert("Let");}
En la función getKey ya habíamos visto que como callback de loadData se ejecutaba estego, y podemos comprobar que ha añadido el valor de la variable correctamente:

Sin más dilación, introducimos “jjss11” en el segundo cuadro de texto y pulsando desvelar superamos el desafío.


Para más información, el botón ‘desvelar’ llamaba a la función decrypt:

function decrypt(){
 var message = "KwLMGF6yZU0iCkCrWAJgmM5hKFvimZao6TWQR15jCbvy86ctBAjnlZ8u5h8idzjcXvpEHmpXz8gwxMMq5QqYWUvAZBN3pq5k1xk9G0KiydDN/v4poUXNRSu2rkLaChAS1MOfiuIx/GrZTEwMp4VoLgLmL5K8sTtiy3U+FQ==";
 var key = document.getElementById("dkey").value;
 var dec = Aes.Ctr.decrypt(message,key,256);
 eval(dec);
 exec();
}


Como se ve, descifra mediante AES-CTR el contenido de ‘message’, lo ejecuta y llama a la función exec. Aunque durante el concurso lo dejamos tras superar el desafío, si alguien tiene curiosidad, el contenido de ‘message’ es el siguiente:

var exec = function(){document.getElementById("respuesta").value="torrijin!";document.forms["formulario"].submit();}
Es decir, completa un formulario que no es visible en la página con el valor “torrijin!” y lo envía al servidor.

Por curiosidad, posteriormente he revisado qué pasaba con rabbit100.png con más detalle: la imagen se carga en un objeto canvas, que nos permite interactuar desde javascript con ella; para generar el script se coge un pixel de cada cuatro de la imagen; cada pixel referencia a un color de la paleta, y al ser una imagen en tonos de grises, cada color se codifica en un byte, en nuestro caso en un carácter. Como las secciones IDAT de un PNG van comprimidas, para verificar que esto cuadraba aproximadamente con lo obtenido, he descomprimido la sección con el siguiente script:

import zlib
import binascii
import re

seccion='08 99 01 D2 00 2D FF 00 01 03 05 07 09 0B 0C 0E 10 12 14 15 16 18 00 19 1B 1D 1E 20 22 24 26 27 28 2A 2C 2E 2F 00 31 32 33 34 35 37 39 3A 3B 3D 3E 40 41 42 00 43 44 45 47 48 49 4A 4B 4C 4E 4F 50 52 53 00 54 55 56 57 59 5A 5B 5D 5F 60 61 62 63 64 00 65 66 67 68 69 6A 6B 6C 6D 6E 6F 70 71 72 00 73 74 75 77 78 79 7A 7B 7C 7D 7E 80 81 82 00 83 85 86 87 88 89 8A 8B 8D 8F 91 92 93 94 00 95 96 97 98 99 9A 9C 9D 9E 9F A0 A1 A2 A4 00 A5 A6 A7 A9 AA AB AC AD AE AF B0 B1 B2 B4 00 B5 B6 B7 B8 B9 BA BB BC BD BE BF C0 C2 C3 00 C4 C5 C6 C7 C8 C9 CB CC CD CE CF D1 D2 D3 00 D4 D5 D6 D7 D8 D9 DA DB DC DD DE DF E0 E1 00 E2 E3 E4 E5 E7 E8 E9 EA EB EC ED EE EF F0 93 19 63 39 03 61 4D 0C'
p=re.compile(' ')
seccionsinespacios=p.sub('',seccion)
seccionbinaria=binascii.unhexlify(seccionsinespacios)
salida=open('salida1.raw','wb')
salida.write(zlib.decompress( seccionbinaria[2:] , -15))
salida.close()

viernes, 29 de abril de 2011

WriteUp - Desafío 5 - H4ckc0nt3st GSIC 2011

Tenemos como referencia la palabra “Laconada” que aparece al colocar el ratón encima de los bloques.

Buscamos en www.cryptool-online.org y encontramos un tipo de codificación llamada “bacon” que consiste en:

Haciendo la conversión obtenemos la clave del desafio:

00101 – aabab – F

10000 – baaaa – R

00100 – aabaa - E

00110 – aabba - G

01101 – abbab - O

01100 – abbaa - N

00000 – aaaaa - A

00010 – aaaba - C

01101 – abbab - O

10001 – baaab - S

01011 – ababb - M

01000 – abaaa - I

00010 – aaaba - C

00000 – aaaaa - A


Solución: fregonacosmica

WriteUp - Desafío 11 - H4ckc0nt3st GSIC 2011

En este desafío debemos encontrar el código de desbloqueo de un móvil Android simulado en un objeto flash.


Haciendo alguna prueba al azar sólo conseguimos el mensaje “Lo sentimos, inténtelo de nuevo”

En primer lugar descargamos el .swf correspondiente y lo desensamblamos con la aplicación Flash Decompiler Trillix, obteniendo la siguiente estructura:


Observamos que la mayor parte del código ha sido ofuscado sustituyendo los nombres de clases, funciones y variables por cadenas semialeatorias que dificultan la interpretación de los entresijos del programa. Las pocas cosas que no están ofuscadas son pistas claras hacia la solución que no supe interpretar hasta haber hecho un análisis algo más completo de la aplicación.

Mirando por encima el código vemos un array de arrays dentro de la clase eMoUtd7A8h666 que apesta a cadenas de caracteres, pese a que algunos de los elementos está fuera del ASCII estándar:

//eMoUtd7A8h666
package
{
  public class eMoUtd7A8h666 extends Object
  {
    public function eMoUtd7A8h666()
    {
      super();
      return;
    }

    public static function byqBTJOaGW666(arg1:int, arg2:int):String
    {
      var loc1:*="";
      var loc2:*=ar[arg1];
      var loc3:*=0;
      while (loc3 < loc2.length)       {         loc1 = loc1 + String.fromCharCode(loc2[loc3] - arg2);         ++loc3;       }       return loc1;     }          {       ar = [[74, 111, 104, 123, 112, 107, 38, 107, 114, 38, 118, 103, 122, 120, 249, 116, 38, 118, 103, 120, 103, 38, 106, 107, 121, 104, 114, 117, 119, 123, 107, 103, 120], [68, 60, 61, 61, 62, 67, 63, 60, 63, 67, 68, 60, 61, 65, 109, 63, 68, 62, 65, 62, 114, 65, 67, 112, 63, 62, 60, 61, 113, 64, 60, 110], [58], [91, 126, 47, 130, 116, 125, 131, 120, 124, 126, 130, 59, 47, 120, 125, 131, 248, 125, 131, 116, 123, 126, 47, 115, 116, 47, 125, 132, 116, 133, 126], [85, 122, 115, 134, 123, 118, 49, 118, 125, 49, 129, 114, 133, 131, 260, 127, 49, 129, 114, 131, 114, 49, 117, 118, 132, 115, 125, 128, 130, 134, 118, 114, 131], [142, 132, 135, 144, 141, 138, 142], [], [], [], [63], [79], [54], [53], [73], [63], [84], [71], [74], [67], [114], [118], [91, 130, 90, 100, 64, 114, 81, 81, 83, 73, 70, 70, 70], [104], [116, 65, 62, 79, 127, 62, 126, 118, 110, 94, 67, 67, 67], [121], [65, 137], []];     }     internal static var ar:Array;   } }

También podemos ver una función bastante simple que parece decodificar cada cadena restando de cada carácter el número indicado como segundo parámetro. Para ver si las cadenas nos dan alguna pista, buscamos las llamadas a dicha función para recopilar el desplazamiento correspondiente a cada cadena.

>strings.exe -q -s *.as|grep byqBTJOaGW666
Lanzo el strings de Sysinternals previo al grep porque el grep de UnxUtils no encuentra las cadenas Unicode generadas por la exportación del Flash Decompiler Trillix.

Con estos datos preparamos un script en python:

import binascii

ar = [[74, 111, 104, 123, 112, 107, 38, 107, 114, 38, 118, 103, 122, 120, 249, 116, 38, 118, 103, 120, 103, 38, 106, 107, 121, 104, 114, 117, 119, 123, 107, 103, 120], [68, 60, 61, 61, 62, 67, 63, 60, 63, 67, 68, 60, 61, 65, 109, 63, 68, 62, 65, 62, 114, 65, 67, 112, 63, 62, 60, 61, 113, 64, 60, 110], [58], [91, 126, 47, 130, 116, 125, 131, 120, 124, 126, 130, 59, 47, 120, 125, 131, 248, 125, 131, 116, 123, 126, 47, 115, 116, 47, 125, 132, 116, 133, 126], [85, 122, 115, 134, 123, 118, 49, 118, 125, 49, 129, 114, 133, 131, 260, 127, 49, 129, 114, 131, 114, 49, 117, 118, 132, 115, 125, 128, 130, 134, 118, 114, 131], [142, 132, 135, 144, 141, 138, 142], [], [], [], [63], [79], [54], [53], [73], [63], [84], [71], [74], [67], [114], [118], [91, 130, 90, 100, 64, 114, 81, 81, 83, 73, 70, 70, 70], [104], [116, 65, 62, 79, 127, 62, 126, 118, 110, 94, 67, 67, 67], [121], [65, 137], []]

off = [6,12,14,15,17,27,17,21,11,15,30,4,2,21,10,30,16,18,10,17,20,16,4,13,19,17,22]

for a in range(0,len(ar)):
  cadena=''
  print "Cadena "+str(a)
  for b in range(0,len(ar[a])):
    cadena=cadena+chr(ar[a][b]-off[a])
  print cadena+'\n'

De esta forma obtenemos las cadenas disponibles.



Cadena 0
Dibuje el patr¾n para desbloquear

Cadena 1
80112730378015a38252f57d3201e40b

Cadena 2
,

Cadena 3
Lo sentimos, intÚntelo de nuevo

Cadena 4
Dibuje el patr¾n para desbloquear

Cadena 5
siluros

Cadena 6


Cadena 7


Cadena 8


Cadena 9
0

Cadena 10
1

Cadena 11
2

Cadena 12
3


Cadena 13
4

Cadena 14
5

Cadena 15
6

Cadena 16
7

Cadena 17
8

Cadena 18
9

Cadena 19
a

Cadena 20
b

Cadena 21
KrJT0bAAC9666

Cadena 22
d

Cadena 23
g41Br1qiaQ666

Cadena 24
f

Cadena 25
0x

Cadena 26


La cadena 1 no es llamada desde el código, así que hemos obtenido el desplazamiento por fuerza bruta, sólo para observar que coincide con una de las cadenas no ofuscadas que mostraba el código. Probablemente la habían ofuscado en primera instancia y después decidieron ponerla en claro para facilitar la solución basada en las pistas.

eIqyPOye1A666.as (1 hits)
Line 76: th4t1s = MLOSVylCb1666.hpys.utils.r3oAjJpsMZ666.k1TNULc6Sk666("80112730378015a38252f57d3201e40b");
También sorprende que donde podíamos esperar los caracteres ‘c’ y ‘e’ encontramos identificadores similares a los que ha generado la ofuscación. Los identificadores no se corresponden con nada que tengamos en el código decompilado y el array donde se incluyen está dentro de una función que no parece ser llamada desde ningún sitio, así que dejamos esto de lado.

Resumiendo, de momento no hemos encontrado gran cosa, y lo único que no parece obvio es la cadena ‘siluros’, que aún no sabemos para qué se utiliza, pero que también aparece sin ofuscar en el código.

MLOSVylCb1666\meychi\ascrypt3\_YOeZFRVpU666.as
Line 57: var loc4:*="teatime_siluros";

Aunque no consigo localizar el final exitoso para ir hacia atrás hasta la validación (me temo que aún no sé relacionar la información gráfica con el ActionScript que proporciona el decompilador), sí vemos el mensaje de fallo, y buscando dónde se utiliza observamos que la validación que necesitamos está en la función eIqyPOye1A666.JA2IBhhdu5666. Pese a esto, para entender mejor el programa decido rastrear la entrada de datos.

En el constructor de la clase eIqyPOye1A666 vemos el siguiente array con nueve elementos, que tiene toda la pinta de ser el array con los “botones”, así que vamos siguiendo qué ocurre con él:

this.qaqnUft5Aa666 = [this.one, this.two, this.three, this.four, this.five, this.six, this.seven, this.eight, this.nine];
Justo después se llama a la función eIqyPOye1A666. lj2WnmDGg6666, donde se añaden a cada “botón” manejadores para los eventos de MOUSE_DOWN o MOUSE_UP, concretamente eIqyPOye1A666.f9W2RUEehO666 y eIqyPOye1A666.HENyNE3kUZ666 respectivamente.

Para no extendernos demasiado en los detalles, cuando se pulsa sobre un botón, se mete una referencia a dicho botón en el array QQguJkqffe666 y se añade un manejador de MOUSE_OVER en todos los botones. Este manejador sigue añadiendo al array comentado todos los botones sobre los que vamos pasando, y al soltar el botón del ratón se quitan los manejadores para MOUSE_OVER y se llama a la función en la que tenemos la comprobación final que ya habíamos comentado antes eIqyPOye1A666.JA2IBhhdu5666.

En esta función observamos que se compara la secuencia introducida por nosotros (en el array QQguJkqffe666) con otra que ha obtenido de separar por comas (cadena 2) el resultado recibido al llamar a r3oAjJpsMZ666.k1TNULc6Sk666 con la cadena alfanumérica que ya nos ha llamado la atención previamente “80112730378015a38252f57d3201e40b”.


private function JA2IBhhdu5666():void{
  var Khwxjj96Ad666:* = 0;
  var mGQBBPZvp5666:* = null;
  var DtRwsK7NRk666:* = null;
  var _WqTfZQq9g666:* = 0;
  var tdQ5VNvK4q666:* = null;
  var oNWCfo_rcc666:* = null;
  var th4t1s:* = r3oAjJpsMZ666.k1TNULc6Sk666("80112730378015a38252f57d3201e40b");
  var bK5n5Gcq8Y666:* = th4t1s.split(eMoUtd7A8h666.byqBTJOaGW666(2, 14));

  var rIkvHB8wSa666:* = bK5n5Gcq8Y666.length;
  var DKlQa0mHrt666:* = 0;
  Khwxjj96Ad666 = 0;
  while (Khwxjj96Ad666 < rIkvHB8wSa666) {     if (bK5n5Gcq8Y666[Khwxjj96Ad666] == this.QQguJkqffe666[Khwxjj96Ad666]){       DKlQa0mHrt666 = (DKlQa0mHrt666 + 1);     };
    Khwxjj96Ad666 = (Khwxjj96Ad666 + 1);
  };
  if (DKlQa0mHrt666 == rIkvHB8wSa666){
    mGQBBPZvp5666 = new SecondView();
    addChild(mGQBBPZvp5666);
  } else {
    oNWCfo_rcc666 = function (_arg1:TimerEvent):void{
      texto.text = eMoUtd7A8h666.byqBTJOaGW666(4, 17);
      tdQ5VNvK4q666.removeEventListener(TimerEvent.TIMER, oNWCfo_rcc666);
      DtRwsK7NRk666.parent.removeChild(DtRwsK7NRk666);
    };
    this.texto.text = eMoUtd7A8h666.byqBTJOaGW666(3, 15);
    DtRwsK7NRk666 = new Shape();
    DtRwsK7NRk666.graphics.lineStyle(10, 0xFFD700, 1, false, LineScaleMode.VERTICAL, CapsStyle.NONE, JointStyle.MITER, 10);
    _WqTfZQq9g666 = this.WasP1Q_YeG666.length;
    DtRwsK7NRk666.graphics.moveTo(this.WasP1Q_YeG666[0], this.UsmsWUWxbi666[0]);
    Khwxjj96Ad666 = 0;
    while (Khwxjj96Ad666 < _WqTfZQq9g666) {       DtRwsK7NRk666.graphics.lineTo(this.WasP1Q_YeG666[Khwxjj96Ad666], this.UsmsWUWxbi666[Khwxjj96Ad666]);       Khwxjj96Ad666 = (Khwxjj96Ad666 + 1);     };     this.addChild(DtRwsK7NRk666);     tdQ5VNvK4q666 = new Timer(1500);     tdQ5VNvK4q666.start();     tdQ5VNvK4q666.addEventListener(TimerEvent.TIMER, oNWCfo_rcc666);   };   this.WasP1Q_YeG666 = [];   this.UsmsWUWxbi666 = [];   this.QQguJkqffe666 = []; }

Visto esto parece claro que si sabemos descifrar dicha cadena alfanumérica obtendremos la secuencia que necesitamos para desbloquear nuestro móvil. En principio esto podría ser inmediato modificando el ActionScript y compilándolo para que nos muestre dicha secuencia, pero mi ignorancia y limitaciones en este entorno no me lo permite, así que toca investigar qué hacen exactamente para descifrar la cadena.

Parece que tenemos una clase auxiliar con sólo tiene dos funciones, que enseguida suponemos que son cifrar y descifrar, que llaman a su vez a otra añadiendo en las llamadas un segundo parámetro que suponemos que es la clave y que es la cadena ‘siluros’ que habíamos visto antes.


//r3oAjJpsMZ666
package MLOSVylCb1666.hpys.utils
{
  import MLOSVylCb1666.meychi.ascrypt3.*;
 
  public class r3oAjJpsMZ666 extends Object
  {
    public function r3oAjJpsMZ666()
    {
      super();
      return;
    }

    public static function k1TNULc6Sk666(arg1:String):String
    {
      var loc1:*=new MLOSVylCb1666.meychi.ascrypt3._YOeZFRVpU666();
      var loc2:*=loc1.N9SO84jcFw666(arg1, y4HgL82VZJ666);
      return loc2;
    }

    public static function HfM4eDkkN0666(arg1:String):String
    {
      var loc1:*=new MLOSVylCb1666.meychi.ascrypt3._YOeZFRVpU666();
      var loc2:*=loc1.VretvlRcF9666(arg1, y4HgL82VZJ666);
      return loc2;
    }

    static const y4HgL82VZJ666:String=eMoUtd7A8h666.byqBTJOaGW666(5, 27);
  }
}

Parece que ya sólo nos queda ver qué es exactamente MLOSVylCb1666.meychi.ascrypt3._YOeZFRVpU666, y por suerte nos han dejado como pista parte de la ruta sin cifrar, así que investigando un poco por ascrypt rápidamente encontramos la web http://osflash.org/ascrypt, donde nos indica que uno de los cifrados soportado es TEA, y entonces nos acordamos de otra pista que nos habían dejado a la vista, la cadena ‘teatime_siluros’ sin cifrar, así que directamente intentamos descifrar mediante este algoritmo.

Para hacerlo, con el Cryptool codificamos los datos hexadecimales en base64.


Y gracias a la siguiente web obtenemos la secuencia deseada:


Introduciendo la secuencia asumiendo que los botones estaban numerados de arriba a abajo y de izquierda a derecha comprobamos que efectivamente es la solución deseada.


Solución más rápida


Al crear la prueba nos han dejado varias pistas, y siendo un poco receptivo hacia ellas y con un poco de cultura sobre algoritmos de cifrado que yo no tengo (no conocía el cifrado TEA), podíamos haber resuelto el reto mucho más rápido.

Nos han dejado a la vista la cadena a descifrar asignada a una variable que nos indicaba que era lo que debíamos buscar:

ActionScript 3.0\eIqyPOye1A666
Line 76: th4t1s = MLOSVylCb1666.hpys.utils.r3oAjJpsMZ666.k1TNULc6Sk666("80112730378015a38252f57d3201e40b");

También nos han dejado en claro la ruta al paquete de cifrado utilizado que ya hemos visto, y la pista definitiva, una referencia al algoritmo utilizado con la clave en claro en una variable no utilizada:

ActionScript 3.0\MLOSVylCb1666\meychi\ascrypt3\_YOeZFRVpU666.as (1 hits)
Line 57: var loc4:*="teatime_siluros";

Esto unido a la cadena ‘siluros’ que ya habíamos visto, el lugar donde se usa, y un análisis muy por encima de la aplicación debería permitir solucionar el reto bastante rápido a gente más despierta que yo :-/.