domingo, 25 de octubre de 2015

Ekoparty CTF 2015 writeup - Crazy JSON (web 300)



Escribo esto de memoria y apoyándome en los scripts de prueba que usé para resolver el reto. Disculpen si hay alguna inexactitud en la descripción del reto, nombres de variables u otros detalles.

El reto nos presenta un formulario que pide únicamente un password. Luego de probar el login enviando cualquier cosa como "password" el servidor nos responde con un código esotérico en formato JSON:

{"success":"0","O0O00O":{"=if":{"cond":{"=<":{"in1":"=OOO00O","in2":"=00000O"}},"then":{"O0000O":{"=+":{"in1":"=OOO00O","in2":1}},"0OO00O":{"=arr.append":{"arr":[],"e":{"=+":{"in1":{"=^":{"in1":{"=arr.at":{"arr":"=O0oo00","offset":"=OOO00O"}},"in2":{"=arr.at":{"arr":"=O00o00","offset":"=OOO00O"}}}},"in2":{"=+":{"in1":1,"in2":"=OOO00O"}}}}}},"=arr.append":{"arr":{"=O0O00O":{"OOO00O":"=O0000O"}},"e":"=0OO00O"}},"else":[]}},"oooo0O":{"=if":{"cond":{"=<":{"in1":"=o0oo0O","in2":"=o00O0O"}},"then":{"00ooOO":{"=+":{"in1":"=o0oo0O","in2":1}},"=+":{"in1":{"=if":{"cond":{"===":{"in1":{"=arr.at":{"arr":{"=arr.at":{"arr":"=O0oo0O","offset":"=o0oo0O"}},"offset":0}},"in2":{"=arr.at":{"arr":"=OO000O","offset":"=o0oo0O"}}}},"then":0,"else":1}},"in2":{"=oooo0O":{"o0oo0O":"=00ooOO"}}}},"else":0}},"0Ooo0O":{"=if":{"cond":{"===":{"in1":{"=arr.len":{"arr":"=O0oo0O"}},"in2":{"=arr.len":{"arr":"=OO000O"}}}},"then":{"=if":{"cond":{"=>":{"in1":{"=oooo0O":{"o0oo0O":0,"o00O0O":{"=arr.len":{"arr":"=O0oo0O"}}}},"in2":0}},"then":"0","else":"1"}},"else":"0"}},"O0oo00":[100, 115, 102, 97, 100, 115, 97, 102, 100, 115, 97, 102, 100, 115],"O00o00":[185, 14, 143, 119, 97, 20, 95, 92, 216, 142, 30, 26, 70, 45, 11, 27, 167, 63, 226, 19, 220, 251, 192, 63, 174, 56, 23, 30, 245, 67, 152, 150],"OO000O":[192, 283, 153, 227, 153, 66, 27, 175, 34, 186, 179, 205, 137, 237, 32, 161, 56, 66, 45, 132, 57, 49, 196, 199, 115, 117, 50, 87, 73, 240, 64, 139],"O0oo0O":{"=O0O00O":{"":"","OOO00O":0,"00000O":{"=arr.len":{"arr":"=O0oo00"}}}},"o0o00O":"=0Ooo0O"}

Observamos que el password de prueba (en mi caso "dsfadsafdsafds") forma parte del JSON pero esta representado como un vector con valores ASCII.

[100, 115, 102, 97, 100, 115, 97, 102, 100, 115, 97, 102, 100, 115]

Eso nos hace sospechar que ese extraño programa en JSON se encarga de validar nuestro password. El reto es entender el programa y conseguir el password correcto.

Revisando el código fuente, vemos un código JS similar a este:

var result = new Ajsone().eval( code );

Luego de googlear por "Ajsone" damos con la web: http://quaxio.com/ajsone/ Donde nos explican la sintaxis del código y además hay un interprete para hacer pruebas que nos viene muy bien :D El resto es ponerse a trabajar y convertir el código en algo más fácil de leer (Javascript).

El resultado más o menos es el siguiente:

function func8(var8, var10) {
    if( var8 < var10 ) {
        var11 = var8 + 1; 
        in1 = ( var9[var8][0] == var12[var8] ) ? 0 : 1;
        in2 = func8( var11 );
        return in1 + in2;
    }
    else {
        return 0;
    }
}

function func13() {
    if( var9.length == var12.length) ) {
        if( func8(0, var9.length) > 0) {
            return "0";
        }
        else {
            return "1";
        }
    }
    else {
        return "0";
    }
}

function func1(var2, var3) {
    if (var2 < var3) {
        var4 = var2 + 1;
        var5 = [];
        in1 = passwd[var2] ^ var7[var2];
        in2 = 1 + var2;
        e = in1 + in2;
        var5.push(e);
        return func1(var4).push( var5 );
    }
    else {
        return [];
    }
}

passwd = [100, 115, 102, 97, 100, 115, 97, 102, 100, 115, 97, 102, 100, 115];
  
var7 = [185, 14, 143, 119, 97, 20, 95, 92, 216, 142, 30, 26, 70, 45, 11, 27, 167, 63, 226, 19, 220, 251, 192, 63, 174, 56, 23, 30, 245, 67, 152, 150];

var12 = [192, 283, 153, 227, 153, 66, 27, 175, 34, 186, 179, 205, 137, 237, 32, 161, 56, 66, 45, 132, 57, 49, 196, 199, 115, 117, 50, 87, 73, 240, 64, 139];

var9 = func1( 0, passwd.length  )

var1 = func13();

return var1;


Resumiendo: Se hace XOR de los elementos de "passwd" con los de "var7" y al resultado se le suma la posición. Con los resultados se crea otro array pero en orden invertido. Finalmente se valida que la longitud de var12 y el array obtenido sean iguales y que sus elementos también sean iguales.

O si prefieren:

Para i de 1 a 32:
    Si passwd[i] ^ var7[i] + i != var12[33 - i]:
        retornar FALSO
retornar VERDADERO

Recordemos que XOR (^) tiene la siguiente propiedad:

Si A ^ B = C, entonces A ^ C = B y B ^ C = A

Luego, usando la propiedad y despejando passwd[i]:

Para i de 1 a 32:
    passwd[i] = (var12[33 - i] - i) ^ var7[i]

Y solo nos resta hacer un script en python para obtener la clave :D

var7 = [185, 14, 143, 119, 97, 20, 95, 92, 216, 142, 30, 26, 70, 45, 11, 27, 167, 63, 226, 19, 220, 251, 192, 63, 174, 56, 23, 30, 245, 67, 152, 150]

var12 = [192, 283, 153, 227, 153, 66, 27, 175, 34, 186, 179, 205, 137, 237, 32, 161, 56, 66, 45, 132, 57, 49, 196, 199, 115, 117, 50, 87, 73, 240, 64, 139]

passwd = []

for i in xrange(0, 32):
    passwd.append( (var12[31 - i] - (i + 1)) ^ var7[i] )

print ''.join( [chr(x) for x in passwd] )



El resultado es "30b23817f4871283718fdfc5890c38d6".

Enviando eso en la URL se obtenía el flag:

http://ctfchallenges.ctf.site:10000/jzoned/?token=30b23817f4871283718fdfc5890c38d6

FLAG: EKO{This_is_awejzoned!}


Un saludo.

3 comentarios:

  1. se me viene a la mente muchas cosas eso es en los routers como TP-LINK

    ResponderEliminar
  2. {"success":"0","O0O00O":{"=if":{"cond":{"=<":{"in1":"=OOO00O","in2":"=00000O"}},"then":{"O0000O":{"=+":{"in1":"=OOO00O","in2":1}},"0OO00O":{"=arr.append":{"arr":[],"e":{"=+":{"in1":{"=^":{"in1":{"=arr.at":{"arr":"=O0oo00","offset":"=OOO00O"}},"in2":{"=arr.at":{"arr":"=O00o00","offset":"=OOO00O"}}}},"in2":{"=+":{"in1":1,"in2":"=OOO00O"}}}}}},"=arr.append":{"arr":{"=O0O00O":{"OOO00O":"=O0000O"}},"e":"=0OO00O"}},"else":[]}},"oooo0O":{"=if":{"cond":{"=<":{"in1":"=o0oo0O","in2":"=o00O0O"}},"then":{"00ooOO":{"=+":{"in1":"=o0oo0O","in2":1}},"=+":{"in1":{"=if":{"cond":{"===":{"in1":{"=arr.at":{"arr":{"=arr.at":{"arr":"=O0oo0O","offset":"=o0oo0O"}},"offset":0}},"in2":{"=arr.at":{"arr":"=OO000O","offset":"=o0oo0O"}}}},"then":0,"else":1}},"in2":{"=oooo0O":{"o0oo0O":"=00ooOO"}}}},"else":0}},"0Ooo0O":{"=if":{"cond":{"===":{"in1":{"=arr.len":{"arr":"=O0oo0O"}},"in2":{"=arr.len":{"arr":"=OO000O"}}}},"then":{"=if":{"cond":{"=>":{"in1":{"=oooo0O":{"o0oo0O":0,"o00O0O":{"=arr.len":{"arr":"=O0oo0O"}}}},"in2":0}},"then":"0","else":"1"}},"else":"0"}},"O0oo00":[100, 115, 102, 97, 100, 115, 97, 102, 100, 115, 97, 102, 100, 115],"O00o00":[185, 14, 143, 119, 97, 20, 95, 92, 216, 142, 30, 26, 70, 45, 11, 27, 167, 63, 226, 19, 220, 251, 192, 63, 174, 56, 23, 30, 245, 67, 152, 150],"OO000O":[192, 283, 153, 227, 153, 66, 27, 175, 34, 186, 179, 205, 137, 237, 32, 161, 56, 66, 45, 132, 57, 49, 196, 199, 115, 117, 50, 87, 73, 240, 64, 139],"O0oo0O":{"=O0O00O":{"":"","OOO00O":0,"00000O":{"=arr.len":{"arr":"=O0oo00"}}}},"o0o00O":"=0Ooo0O"}
    {79.238.246.241,255.255.255.255,217.0.119.31,217.0.43.65,217.0.119.81}{192.168.2.1,255.255.255.0}{80,8080.80,8080.21,22.21,22.40-50,40-50.70,70.70,70.9090,9090}

    ResponderEliminar