Sorry, this entry is only available in European Spanish. For the sake of viewer convenience, the content is shown below in the alternative language. You may click the link to switch the active language.

CyberCamp 2019

Con esta entrada comenzamos una serie de writeups resolviendo los retos de la fase clasificatoria online de la CyberCamp 2019. Fueron unos retos bastante entretenidos empezando por una dificultad básica hasta algunos de dificultad elevada.

Enunciado

En este caso nos piden conseguir a acceder a un panel login y nos dan para ello dos archivos:index.html y obfuscated_ashqiwvo.js.
Accediendo a la web del archivo index.html podemos ver un simple login


Solución

El sistema de login esta hecho en el lado del cliente por medio de Javascript por lo que analizando el código deberíamos ser capaces de obtener las credenciales.
Simplemente viendo el nombre del archivo Javascript que es ejecutado por la web podemos suponer que estará ofuscado para añadir cierta dificultad al reto.
Para mejorar la legibilidad de este código utilizamos http://jsnice.org/.



Como vemos esto facilitará en gran medida la comprensión del flujo del programa. Hay que tener cuidado porque el uso de estas herramientas podría provocar que el código no funcionara correctamente en caso de tener una protección de integridad.

Al comienzo del código encontramos una lista de cadenas de texto y en el siguiente fragmento de código que provoca el desorden de la lista.



En el siguiente fragmento encontramos una función que recibe un numero entero y extrae de la lista de cadenas el valor en la posición indicada.
Por mejorar la comprensión del código se modifica el nombre de la variable que contiene la lista por valores y el nombre de la función por get_value.



Revisando los usos encontrados de la función comprendemos que el funcionamiento de la ofuscación ha sido sustituir ciertos elementos del código y almacenarlos en la lista, la mayoría son llamadas a funciones o nombres de elementos del DOM.
Se obtiene la lista con el orden correcto a través de la inserción de un console.log(valores).
Una vez ejecutado obtenemos la lista:



Para automatizar la tarea y no tener que observar en cada uso de la función get_value el valor de la lista correspondiente se realiza un script que mediante expresiones regulares sustituya los usos por el valor correcto.

import re
valores= [
  "apply",
  "function *\\( *\\)",
  "\\+\\+ *(?:_0x(?:[a-f0-9]){4,6}|(?:\\b|\\d)[a-z0-9]{1,4}(?:\\b|\\d))",
  "test",
  "input",
  "click",
  "preventDefault",
  "#user",
  "val",
  "#pass",
  "administrador",
  "length",
  "#respuesta",
  "html",
  "<div class='alert alert-danger'>Error en la contraseña.</div>",
  "location",
  "href",
  "?access=",
  "<div class='alert alert-danger'>Genial! envia la flag como flag{password}.</div>",
  "string",
  "constructor",
  "counter",
  "debu",
  "gger",
  "call",
  "action",
  "stateObject"
]

with open('beauty.js') as infile:
    content = infile.read()
    result=re.sub(r'get_value\("(0x[a-zA-z0-9]+)"\)', lambda x : '"'+valores[int(x.group(1),base=16)]+'"',content)
    with open('output.js','w') as outfile:
        outfile.write(result)

Una vez tenemos el código corregido encontramos la función que se ejecuta al presionar el boton de Login pudiendo así comprender su funcionamiento.



Lo primero que vemos es que nuestro usuario debe ser administrador y posteriormente se realizan comprobaciones sobre cada carácter del usuario y la contraseña.

for (; i < data["length"]; i++) {
      if (data["charCodeAt"](i) + indexMap["charCodeAt"](i) + i * 10 != testvectors[i]) {

En este extracto vemos que comprueba que el codigo ascii de cada letra del username(data) sumado al código ascii de cada letra de la contraseña(indexMap) mas la posición de la letra por 10 sea igual al numero que encontramos en la posición i de una lista.
Con este funcionamiento sacamos una simple ecuación de primer grado para cada letra.
Para automatizar esta tarea realizaremos un script:

testvectors = [209, 165, 203, 245, 259, 208, 240, 271, 245, 304, 268, 304, 323]
data = 'administrador'
password=''
for i in range(len(data)):
     password += chr(testvectors[i]-i*10-ord(data[i]))
print('la flag es flag{%s}' % password)

la flag es flag{p7Jnm5AU3uDSY}

¿Me ayudas a compatirlo?