viernes, 29 de abril de 2011

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 :-/.

No hay comentarios:

Publicar un comentario