Warning: This challenge is still active and therefore should not be resolved using this information.
Aviso: Este reto sigue en activo y por lo tanto no se debería resolver utilizando esta información.

Intro

This crackme is for the crack challenge 6 of canyouhack.it.

In this crackme the goal is to turn on all the lights. Note that a light off to the next, so if we interrupt this, we win.

Tools

Exeinfo (For crackme info)

Delphi Decompiler (For decompile)

 OllyDbg (For debug)

Decompiling

With Delphi Decompiler we can found easy the buttons and his offsets.
Go to the offset 401A64 in OllyDbg and analyze the code.
We view two jumps, one turn ON the light and the other Turn OFF the next light. Patching the call from offset 401A8B we get the serial.

Links


Primeras impresiones Analizamos el programa con PEiD y nos muestra que está hecho en ensamblador. Unas pruebas introduciendo datos nos
Introducción Continuamos con la segunda entrega de Cruehead. En este caso nos encontramos con un único campo de contraseña para
st2 arcade
He de iniciar esta entrada diciendo que la segunda temporada de Stranger Things es sencillamente genial. Son 9 horas intensas
Intro Es un crackme realizado en ensamblador y en el que el objetivo es remover la NAG de la forma

Introducción

Hace tiempo que me aficioné a los retos de Hacking y Cracking, y si bien la mayoría de ellos consisten en desencriptar una clave o realizar ingeniería inversa sobre un ejecutable, también los hay sobre programación pura y dura.

En esta ocasión se nos proporciona un código «muestra» parecido a PHP o C++ y tenemos que ingeniarnoslas para mejorarlo y ganar a la máquina.

Objetivo del juego y normas

El objetivo de esta misión es ganar a Tr0n en su propio juego: las carreras de motos. Se te proporcionará un programa (código) funcional para que veas como se controla el vehiculo. Usando tu inteligencia, tendrás que entender su uso y mejorarlo, ya que no es lo suficientemente bueno como para ganar a Tr0n. Tr0n lleva ya bastante tiempo en la parrilla de juegos y es bastante habilidoso 🙂

Cuando venzas a Tr0n un mínimo de 5 veces consecutivas, se te dará por superada esta prueba.

Buena suerte!!!

[ Available functions / Funciones disponibles ]
direction() returns current direction, change to a new one with direction([newdir])
getX(), getY() returns X and Y coordinates
collisionDistance() | collisionDistance([anydir]) returns the distance until collision
Note: parameters [*dir] can be empty or one of this values: UP DOWN LEFT or RIGHT

[ Constants / Constantes ]
UP DOWN LEFT RIGHT MAX_X MAX_Y

[ Rules / Reglas ]
Try to survive driving your bike and … / Intenta sobrevivir conduciendo tu moto y…
Don’t cross any line / No cruces ninguna línea
or crash with the corners! / o choques con las esquinas!

[ Mission / Mision ]
Use well this controller and beat Tr0n 5 consecutive times to score in this game
Usa bien este controlador y vence a Tr0n 5 veces consecutivas para puntuar en este juego

Código inicial

Nada más comenzar vemos que hemos perdido nuestra primera partida con el siguiente código:

	function controller(playerController $c){
		if($c->direction()==UP && $c->getY()<10){
			if(rand(0,1)==0) $c->direction(LEFT);
				else $c->direction(RIGHT);
			goto done;
		}
		if($c->direction()==DOWN && MAX_Y-$c->getY()<10){
			if(rand(0,1)==0) $c->direction(LEFT);
				else $c->direction(RIGHT);
			goto done;
		}
		if($c->direction()==LEFT && $c->getX()<10){
			if(rand(0,1)==0) $c->direction(UP);
				else $c->direction(DOWN);
			goto done;
		}
		if($c->direction()==RIGHT && MAX_X-$c->getX()<10){
			if(rand(0,1)==0) $c->direction(UP);
				else $c->direction(DOWN);
		}
		done:
	}

Nosotros somos el AZUL y la máquina es el VERDE.

loose_inicial

Primeras modificaciones

Lo primero que tenemos que modificar son las distancias de las coordenadas que estan puestas en «<10» al mínimo, que sería «<2«. También sustituir la aleatoriedad «rand(0,1)==0» por algo más útil y comenzar a usar la función «collisionDistance()«.

Como podéis observar en el código inferior, usamos la función «collisionDistance()» para detectar cuando estamos a punto de chocar «collisionDistance() ==1» y para detectar a que lado nos conviene más girar en función de donde podamos recorrer más distancia «if($c->collisionDistance([LEFT]) >2) $c->direction(LEFT); else $c->direction(RIGHT);«.

if($c->direction()==UP && $c->getY()==1 && $c->collisionDistance() ==1){
			if($c->collisionDistance([LEFT]) >2) $c->direction(LEFT);
				else $c->direction(RIGHT);
		}
if($c->direction()==DOWN && MAX_Y-$c->getY()<2 || $c->collisionDistance() ==1){
			if($c->collisionDistance([LEFT]) >2) $c->direction(LEFT);
				else $c->direction(RIGHT);
		}
if($c->direction()==LEFT && $c->getX()==1 && $c->collisionDistance() ==1){
			if($c->collisionDistance([UP]) >2) 
                                $c->direction(UP);
				else 
                                $c->direction(DOWN);
		}
if($c->direction()==RIGHT && MAX_X-$c->getX()<2 || $c->collisionDistance() ==1){
			if($c->collisionDistance([UP]) >2) $c->direction(UP);
				else $c->direction(DOWN);
				
		}

Terminando la faena

El código anterior de por sí no nos resuelve mucho si no afinamos un poco más, comprobando todos las posibles colisiones y tomando la dirección correcta en función de la mayor distancia a recorrer.

    if($c->collisionDistance([UP])==1 || $c->collisionDistance() ==1){
             if($c->collisionDistance([LEFT]) > $c->collisionDistance([RIGHT]))
               $c->direction(LEFT);
             else 
               $c->direction(RIGHT);
     }
     if($c->collisionDistance([DOWN])==1 || $c->collisionDistance() ==1){
            if($c->collisionDistance([LEFT]) > $c->collisionDistance([RIGHT]))
               $c->direction(LEFT);
             else 
               $c->direction(RIGHT);
     }
     if($c->collisionDistance([RIGHT])==1 || $c->collisionDistance() ==1){
             if($c->collisionDistance([UP]) > $c->collisionDistance([DOWN]))
               $c->direction(UP);
             else 
               $c->direction(DOWN);
     }
     if($c->collisionDistance([LEFT])==1 || $c->collisionDistance() ==1){
          if($c->collisionDistance([UP]) > $c->collisionDistance([DOWN]))
               $c->direction(UP);
             else 
               $c->direction(DOWN);
     }

Código ganador

El código que utilicé yo para ganar a Tron es el siguiente:

function controller(playerController $c){
uno:
if($c->direction()==UP && $c->getY()==1 && $c->collisionDistance() ==1){
			if($c->collisionDistance([LEFT]) >2) $c->direction(LEFT);
				else $c->direction(RIGHT);
				
		}
if($c->direction()==DOWN && MAX_Y-$c->getY()<2 || $c->collisionDistance() ==1){
			if($c->collisionDistance([LEFT]) >2) $c->direction(LEFT);
				else $c->direction(RIGHT);
				
		}
if($c->direction()==LEFT && $c->getX()==1 && $c->collisionDistance() ==1){
			if($c->collisionDistance([UP]) >2) 
                                $c->direction(UP);
				else 
                                $c->direction(DOWN);
				
		}
if($c->direction()==RIGHT && MAX_X-$c->getX()<2 || $c->collisionDistance() ==1){
			if($c->collisionDistance([UP]) >2) $c->direction(UP);
				else $c->direction(DOWN);
				
		}
dos:
    if($c->collisionDistance([UP])==1 || $c->collisionDistance() ==1){
             if($c->collisionDistance([LEFT]) > $c->collisionDistance([RIGHT]))
               $c->direction(LEFT);
             else 
               $c->direction(RIGHT);
     }
     if($c->collisionDistance([DOWN])==1 || $c->collisionDistance() ==1){
            if($c->collisionDistance([LEFT]) > $c->collisionDistance([RIGHT]))
               $c->direction(LEFT);
             else 
               $c->direction(RIGHT);
     }
     if($c->collisionDistance([RIGHT])==1 || $c->collisionDistance() ==1){
             if($c->collisionDistance([UP]) > $c->collisionDistance([DOWN]))
               $c->direction(UP);
             else 
               $c->direction(DOWN);
     }
     if($c->collisionDistance([LEFT])==1 || $c->collisionDistance() ==1){
          if($c->collisionDistance([UP]) > $c->collisionDistance([DOWN]))
               $c->direction(UP);
             else 
               $c->direction(DOWN);
     }
		done:
	}

Mis jugadas ganadoras:

01

02

03

04

05

El código no es infalible ya que como comprabaréis vosotros mismos, no se puede ganar siempre por el mero hecho de la aleatoriedad y de la suerte. Cuando dispongais de un código decente, ejecutarlo varias veces para estar seguros antes de desecharlo.

Curiosidades

Como se suele decir, la banca siempre gana, y en este caso no iba a ser menos y es que en caso de empate ¡la banca gana!

empate

 

Por último deciros que podéis utilizar el código ya que la web detecta los códigos ganadores para que no se repitan.

Enlaces

Se nos entrega un ELF que decompilado presenta este aspecto:

/* This file was generated by the Hex-Rays decompiler version 8.4.0.240320.
   Copyright (c) 2007-2021 Hex-Rays <info@hex-rays.com>

   Detected compiler: GNU C++
*/

#include <defs.h>


//-------------------------------------------------------------------------
// Function declarations

__int64 (**init_proc())(void);
__int64 sub_401020();
__int64 sub_401030(); // weak
__int64 sub_401040(); // weak
__int64 sub_401050(); // weak
__int64 sub_401060(); // weak
__int64 sub_401070(); // weak
// int puts(const char *s);
// int printf(const char *format, ...);
// __int64 __isoc99_scanf(const char *, ...); weak
// void __noreturn exit(int status);
void __fastcall __noreturn start(__int64 a1, __int64 a2, void (*a3)(void));
void dl_relocate_static_pie();
char *deregister_tm_clones();
__int64 register_tm_clones();
char *_do_global_dtors_aux();
__int64 frame_dummy();
int __fastcall main(int argc, const char **argv, const char **envp);
_BYTE *__fastcall encode(__int64 a1);
__int64 __fastcall validar(const char *a1);
int banner();
int comprar();
void _libc_csu_fini(void); // idb
void term_proc();
// int __fastcall _libc_start_main(int (__fastcall *main)(int, char **, char **), int argc, char **ubp_av, void (*init)(void), void (*fini)(void), void (*rtld_fini)(void), void *stack_end);
// __int64 _gmon_start__(void); weak

//-------------------------------------------------------------------------
// Data declarations

_UNKNOWN _libc_csu_init;
const char a31mparaSeguirU[43] = "\x1B[31mPara seguir usando este producto deber"; // idb
const char a32myaPuedesSeg[61] = "\x1B[32mYa puedes seguir afinando tus instrumentos (y tus flags "; // idb
const char aDirigaseANuest[21] = "\nDirigase a nuestra p"; // idb
__int64 (__fastcall *_frame_dummy_init_array_entry)() = &frame_dummy; // weak
__int64 (__fastcall *_do_global_dtors_aux_fini_array_entry)() = &_do_global_dtors_aux; // weak
__int64 (*qword_404010)(void) = NULL; // weak
char _bss_start; // weak


//----- (0000000000401000) ----------------------------------------------------
__int64 (**init_proc())(void)
{
  __int64 (**result)(void); // rax

  result = &_gmon_start__;
  if ( &_gmon_start__ )
    return (__int64 (**)(void))_gmon_start__();
  return result;
}
// 404090: using guessed type __int64 _gmon_start__(void);

//----- (0000000000401020) ----------------------------------------------------
__int64 sub_401020()
{
  return qword_404010();
}
// 404010: using guessed type __int64 (*qword_404010)(void);

//----- (0000000000401030) ----------------------------------------------------
__int64 sub_401030()
{
  return sub_401020();
}
// 401030: using guessed type __int64 sub_401030();

//----- (0000000000401040) ----------------------------------------------------
__int64 sub_401040()
{
  return sub_401020();
}
// 401040: using guessed type __int64 sub_401040();

//----- (0000000000401050) ----------------------------------------------------
__int64 sub_401050()
{
  return sub_401020();
}
// 401050: using guessed type __int64 sub_401050();

//----- (0000000000401060) ----------------------------------------------------
__int64 sub_401060()
{
  return sub_401020();
}
// 401060: using guessed type __int64 sub_401060();

//----- (0000000000401070) ----------------------------------------------------
__int64 sub_401070()
{
  return sub_401020();
}
// 401070: using guessed type __int64 sub_401070();

//----- (00000000004010D0) ----------------------------------------------------
// positive sp value has been detected, the output may be wrong!
void __fastcall __noreturn start(__int64 a1, __int64 a2, void (*a3)(void))
{
  __int64 v3; // rax
  int v4; // esi
  __int64 v5; // [rsp-8h] [rbp-8h] BYREF
  char *retaddr; // [rsp+0h] [rbp+0h] BYREF

  v4 = v5;
  v5 = v3;
  _libc_start_main(
    (int (__fastcall *)(int, char **, char **))main,
    v4,
    &retaddr,
    (void (*)(void))_libc_csu_init,
    _libc_csu_fini,
    a3,
    &v5);
  __halt();
}
// 4010DA: positive sp value 8 has been found
// 4010E1: variable 'v3' is possibly undefined

//----- (0000000000401100) ----------------------------------------------------
void dl_relocate_static_pie()
{
  ;
}

//----- (0000000000401110) ----------------------------------------------------
char *deregister_tm_clones()
{
  return &_bss_start;
}
// 404050: using guessed type char _bss_start;

//----- (0000000000401140) ----------------------------------------------------
__int64 register_tm_clones()
{
  return 0LL;
}

//----- (0000000000401180) ----------------------------------------------------
char *_do_global_dtors_aux()
{
  char *result; // rax

  if ( !_bss_start )
  {
    result = deregister_tm_clones();
    _bss_start = 1;
  }
  return result;
}
// 404050: using guessed type char _bss_start;

//----- (00000000004011B0) ----------------------------------------------------
__int64 frame_dummy()
{
  return register_tm_clones();
}

//----- (00000000004011B6) ----------------------------------------------------
int __fastcall main(int argc, const char **argv, const char **envp)
{
  int v4; // [rsp+10h] [rbp-10h] BYREF
  int v5; // [rsp+14h] [rbp-Ch]
  unsigned __int64 v6; // [rsp+18h] [rbp-8h]

  v6 = __readfsqword(0x28u);
  v5 = 0;
  puts("\n\x1B[31m    -----------Se le ha acabado el periodo de prueba gratuito-----------\n");
  puts(a31mparaSeguirU);
  do
  {
    banner();
    __isoc99_scanf("%d", &v4);
    if ( v4 == 3 )
      exit(0);
    if ( v4 > 3 )
      goto LABEL_10;
    if ( v4 == 1 )
    {
      comprar();
      continue;
    }
    if ( v4 == 2 )
      v5 = validar("%d");
    else
LABEL_10:
      puts("Opcion invalida, pruebe otra vez");
  }
  while ( !v5 );
  puts(a32myaPuedesSeg);
  return 0;
}
// 4010B0: using guessed type __int64 __isoc99_scanf(const char *, ...);

//----- (0000000000401291) ----------------------------------------------------
_BYTE *__fastcall encode(__int64 a1)
{
  _BYTE *result; // rax
  int i; // [rsp+14h] [rbp-4h]

  for ( i = 0; i <= 33; ++i )
  {
    if ( *(char *)(i + a1) <= 96 || *(char *)(i + a1) > 122 )
    {
      if ( *(char *)(i + a1) <= 64 || *(char *)(i + a1) > 90 )
      {
        result = (_BYTE *)*(unsigned __int8 *)(i + a1);
        *(_BYTE *)(i + a1) = (_BYTE)result;
      }
      else
      {
        result = (_BYTE *)(i + a1);
        *result = (5 * ((char)*result - 65) + 8) % 26 + 65;
      }
    }
    else
    {
      result = (_BYTE *)(i + a1);
      *result = (5 * ((char)*result - 97) + 8) % 26 + 97;
    }
  }
  return result;
}

//----- (00000000004013DB) ----------------------------------------------------
__int64 __fastcall validar(const char *a1)
{
  int i; // [rsp+Ch] [rbp-64h]
  char v3[48]; // [rsp+10h] [rbp-60h] BYREF
  __int64 v4[6]; // [rsp+40h] [rbp-30h] BYREF

  v4[5] = __readfsqword(0x28u);
  qmemcpy(v4, "RisgAv{rIU_ihHwvIxA_sAppCsziq3vzC}", 34);
  printf("\nIntroduce tu licencia: ");
  __isoc99_scanf("%s", v3);
  encode((__int64)v3);
  for ( i = 0; i <= 33; ++i )
  {
    if ( v3[i] != *((_BYTE *)v4 + i) )
    {
      puts("\n\x1B[31mTu licencia es incorrecta\x1B[37m\n");
      return 0LL;
    }
  }
  puts("\n\x1B[32mEres un crack, lo conseguiste\x1B[37m");
  return 1LL;
}
// 4010B0: using guessed type __int64 __isoc99_scanf(const char *, ...);
// 4013DB: using guessed type char var_60[48];

//----- (00000000004014CE) ----------------------------------------------------
int banner()
{
  puts("                     ___________OPCIONES___________");
  puts("                    | 1: Comprar licencia premium  |");
  puts("                    | 2: Validar clave de licencia |");
  puts("                    | 3: Salir                     |");
  puts("                     ------------------------------");
  return printf("> ");
}

//----- (0000000000401526) ----------------------------------------------------
int comprar()
{
  return puts(aDirigaseANuest);
}

//----- (0000000000401540) ----------------------------------------------------
void __fastcall _libc_csu_init(unsigned int a1, __int64 a2, __int64 a3)
{
  signed __int64 v3; // rbp
  __int64 i; // rbx

  init_proc();
  v3 = &_do_global_dtors_aux_fini_array_entry - &_frame_dummy_init_array_entry;
  if ( v3 )
  {
    for ( i = 0LL; i != v3; ++i )
      (*(&_frame_dummy_init_array_entry + i))();
  }
}
// 403E10: using guessed type __int64 (__fastcall *_frame_dummy_init_array_entry)();
// 403E18: using guessed type __int64 (__fastcall *_do_global_dtors_aux_fini_array_entry)();

//----- (00000000004015B0) ----------------------------------------------------
void _libc_csu_fini(void)
{
  ;
}

//----- (00000000004015B8) ----------------------------------------------------
void term_proc()
{
  ;
}

// nfuncs=33 queued=21 decompiled=21 lumina nreq=0 worse=0 better=0
// ALL OK, 21 function(s) have been successfully decompiled

Para resolver el juego y obtener una licencia válida, nos fijamos en el proceso de validación que se encuentra en la función validar (líneas 237 a 258). Esta función compara una entrada de licencia codificada con una licencia codificada almacenada en el programa.

La licencia almacenada es "RisgAv{rIU_ihHwvIxA_sAppCsziq3vzC}", y se utiliza la función encode (líneas 207 a 234) para codificar la entrada del usuario antes de compararla. La función encode aplica un cifrado simple a la entrada, alterando los caracteres alfabéticos según una fórmula específica.

La función de cifrado encode realiza lo siguiente:

  • Si el carácter es una letra minúscula (a-z), se convierte según la fórmula (5 * (char - 97) + 8) % 26 + 97.
  • Si el carácter es una letra mayúscula (A-Z), se convierte según la fórmula (5 * (char - 65) + 8) % 26 + 65.

Nos construimos una función en Python para decodificar la Flag y reto superado.

def decode(encoded_char):
    if 'a' <= encoded_char <= 'z':
        original_char = chr(((ord(encoded_char) - 97 - 8) * 21) % 26 + 97)
    elif 'A' <= encoded_char <= 'Z':
        original_char = chr(((ord(encoded_char) - 65 - 8) * 21) % 26 + 65)
    else:
        original_char = encoded_char
    return original_char

encoded_license = "RisgAv{rIU_ihHwvIxA_sAppCsziq3vzC}"
decoded_license = "".join(decode(char) for char in encoded_license)

print("Licencia descifrada:", decoded_license)

Introducción

Hoy tenemos aquí un crackme de los que te hacen temblar las conexiones neuronales. Estamos acostumbrados al típico serial asociado a un nombre y a veces nos sorprenden.

El crackme data del año 2000, está realizado por aLoNg3x y lo tenéis colgado en crackmes.de. En crackmes.de también disponéis de una solución muy elegante realizada por cronos, pero que no acaba de saciar nuestro afán de descubrir todas las soluciones posibles.

El algoritmo

Abrimos el crackme con Olly y enseguida encontramos la rutina de comprobación junto con los mensajes de éxito y error. Os dejo la rutina comentada como siempre.

004012D7   |.  83C4 08             ADD ESP,8                                 ;  
004012DA   |.  09C0                OR EAX,EAX                                ;  
004012DC   |. /74 16               JE SHORT Zebrone.004012F4                 ;  Salta a Bad boy
004012DE   |. |6A 00               PUSH 0                                    ; /Style = MB_OK|MB_APPLMODAL
004012E0   |. |68 26324000         PUSH Zebrone.00403226                     ; |Title = "Great !!!"
004012E5   |. |68 30324000         PUSH Zebrone.00403230                     ; |Text = "Congratulations, you have cracked the Zebra Crackme ver 1.1"
004012EA   |. |FF75 08             PUSH [ARG.1]                              ; |hOwner = 0011067C ('Zebra - aLoNg3x - 1.1 Version',class='#32770')
004012ED   |. |E8 C6010000         CALL <JMP.&USER32.MessageBoxA>            ; \MessageBoxA
004012F2   |. |EB 14               JMP SHORT Zebrone.00401308
004012F4   |> \6A 00               PUSH 0                                    ; /Style = MB_OK|MB_APPLMODAL
004012F6   |.  68 F8314000         PUSH Zebrone.004031F8                     ; |Title = "Hmmmm :P"
004012FB   |.  68 01324000         PUSH Zebrone.00403201                     ; |Text = "Sorry... The Serial isn't correct :Þ"
00401300   |.  FF75 08             PUSH [ARG.1]                              ; |hOwner = 0011067C ('Zebra - aLoNg3x - 1.1 Version',class='#32770')
00401303   |.  E8 B0010000         CALL <JMP.&USER32.MessageBoxA>            ; \MessageBoxA
00401308   |>  31C0                XOR EAX,EAX
0040130A   |.  40                  INC EAX
0040130B   |.  EB 39               JMP SHORT Zebrone.00401346
0040130D   |>  6A 00               PUSH 0                                    ; /Result = 0
0040130F   |.  FF75 08             PUSH [ARG.1]                              ; |hWnd = 0011067C ('Zebra - aLoNg3x - 1.1 Version',class='#32770')
00401312   |.  E8 89010000         CALL <JMP.&USER32.EndDialog>              ; \EndDialog
00401317   |.  31C0                XOR EAX,EAX
00401319   |.  40                  INC EAX
0040131A   |.  EB 2A               JMP SHORT Zebrone.00401346
0040131C   |>  6A 00               PUSH 0                                    ; /Style = MB_OK|MB_APPLMODAL
0040131E   |.  68 40304000         PUSH Zebrone.00403040                     ; |Title = "Zebra ver. 1.1"
00401323   |.  68 4F304000         PUSH Zebrone.0040304F                     ; |Text = "This is the 1.1 Zebra Crackme, Thanks to Quequero and Koma, to have said me a bug of the previous version. (It was due to an orrible cpu appoximation). As usually you cannot patch this .EXE, you've to find one of the many correct solut"...
00401328   |.  FF75 08             PUSH [ARG.1]                              ; |hOwner = 0011067C ('Zebra - aLoNg3x - 1.1 Version',class='#32770')
0040132B   |.  E8 88010000         CALL <JMP.&USER32.MessageBoxA>            ; \MessageBoxA
00401330   |.  31C0                XOR EAX,EAX
00401332   |.  40                  INC EAX
00401333   |.  EB 11               JMP SHORT Zebrone.00401346
00401335   |>  6A 00               PUSH 0                                    ; /Result = 0
00401337   |.  FF75 08             PUSH [ARG.1]                              ; |hWnd = 0011067C ('Zebra - aLoNg3x - 1.1 Version',class='#32770')
0040133A   |.  E8 61010000         CALL <JMP.&USER32.EndDialog>              ; \EndDialog
0040133F   |.  31C0                XOR EAX,EAX
00401341   |.  40                  INC EAX
00401342   |.  EB 02               JMP SHORT Zebrone.00401346
00401344   |>  31C0                XOR EAX,EAX
00401346   |>  C9                  LEAVE
00401347   \.  C2 1000             RETN 10
================================================================
0040134A   /$  55                  PUSH EBP
0040134B   |.  89E5                MOV EBP,ESP
0040134D   |.  83EC 68             SUB ESP,68
00401350   |.  FF75 08             PUSH [ARG.1]                              ; /x1
00401353   |.  E8 78010000         CALL <JMP.&CRTDLL.atof>                   ; \atof
00401358   |.  DD55 E8             FST QWORD PTR SS:[EBP-18]
0040135B   |.  83EC 08             SUB ESP,8
0040135E   |.  DD1C24              FSTP QWORD PTR SS:[ESP]
00401361   |.  E8 82010000         CALL <JMP.&CRTDLL.floor>
00401366   |.  DD5D F8             FSTP QWORD PTR SS:[EBP-8]
00401369   |.  FF75 0C             PUSH [ARG.2]                              ; /x2
0040136C   |.  E8 5F010000         CALL <JMP.&CRTDLL.atof>                   ; \atof
00401371   |.  DD55 D8             FST QWORD PTR SS:[EBP-28]
00401374   |.  83EC 08             SUB ESP,8
00401377   |.  DD1C24              FSTP QWORD PTR SS:[ESP]
0040137A   |.  E8 69010000         CALL <JMP.&CRTDLL.floor>
0040137F   |.  83C4 18             ADD ESP,18
00401382   |.  DD55 F0             FST QWORD PTR SS:[EBP-10]
00401385   |.  DC4D F8             FMUL QWORD PTR SS:[EBP-8]
00401388   |.  D9EE                FLDZ
0040138A   |.  DED9                FCOMPP                                    ;  floor(x1)*floor(x2)=0 ???
0040138C   |.  DFE0                FSTSW AX                                  ;  <<Store status word
0040138E   |.  9E                  SAHF                                      ;  <<Store AH into FLAGS
0040138F   |.  75 07               JNZ SHORT Zebrone.00401398                ;  Si salta todo OK
00401391   |.  31C0                XOR EAX,EAX
00401393   |.  E9 96000000         JMP Zebrone.0040142E                      ;  Bad boy
00401398   |>  DD45 F8             FLD QWORD PTR SS:[EBP-8]                  ;  <<Floating point load
0040139B   |.  DC5D F0             FCOMP QWORD PTR SS:[EBP-10]               ;  x1 = x2 ???
0040139E   |.  DFE0                FSTSW AX                                  ;  <<Store status word
004013A0   |.  9E                  SAHF                                      ;  <<Store AH into FLAGS
004013A1   |.  75 07               JNZ SHORT Zebrone.004013AA                ;  Si salta todo OK
004013A3   |.  31C0                XOR EAX,EAX
004013A5   |.  E9 84000000         JMP Zebrone.0040142E                      ;  Bad boy
004013AA   |>  DD45 F8             FLD QWORD PTR SS:[EBP-8]                  ;  <<Floating point load
004013AD   |.  DD5D C8             FSTP QWORD PTR SS:[EBP-38]
004013B0   |.  D9E8                FLD1                                      ;  Carga 1 en el stack
004013B2   |.  DD55 C0             FST QWORD PTR SS:[EBP-40]                 ;  <<Floating point store
004013B5   |.  DC5D C8             FCOMP QWORD PTR SS:[EBP-38]               ;  x1 > 1 ???
004013B8   |.  DFE0                FSTSW AX                                  ;  <<Store status word
004013BA   |.  9E                  SAHF                                      ;  <<Store AH into FLAGS
004013BB   |.  77 2D               JA SHORT Zebrone.004013EA                 ;  Si salta bad boy
004013BD   |.  DF2D 38304000       FILD QWORD PTR DS:[403038]                ;  <<Load integer>> 2540BE400 = 10^10
004013C3   |.  DD55 B8             FST QWORD PTR SS:[EBP-48]                 ;  <<Floating point store
004013C6   |.  DC5D C8             FCOMP QWORD PTR SS:[EBP-38]               ;  x1 < 10^10 ???
004013C9   |.  DFE0                FSTSW AX                                  ;  <<Store status word
004013CB   |.  9E                  SAHF                                      ;  <<Store AH into FLAGS
004013CC   |.  72 1C               JB SHORT Zebrone.004013EA                 ;  Si salta bad boy
004013CE   |.  DD45 F0             FLD QWORD PTR SS:[EBP-10]                 ;  <<Floating point load
004013D1   |.  DD5D B0             FSTP QWORD PTR SS:[EBP-50]                ;  <<Store and pop
004013D4   |.  DD45 C0             FLD QWORD PTR SS:[EBP-40]                 ;  <<Floating point load
004013D7   |.  DC5D B0             FCOMP QWORD PTR SS:[EBP-50]               ;  x2 > 1 ???
004013DA   |.  DFE0                FSTSW AX                                  ;  <<Store status word
004013DC   |.  9E                  SAHF                                      ;  <<Store AH into FLAGS
004013DD   |.  77 0B               JA SHORT Zebrone.004013EA                 ;  Si salta bad boy
004013DF   |.  DD45 B8             FLD QWORD PTR SS:[EBP-48]                 ;  <<Floating point load>> carga 10^10
004013E2   |.  DC5D B0             FCOMP QWORD PTR SS:[EBP-50]               ;  x2 < 10^10 ???
004013E5   |.  DFE0                FSTSW AX                                  ;  <<Store status word
004013E7   |.  9E                  SAHF                                      ;  <<Store AH into FLAGS
004013E8   |.  73 04               JNB SHORT Zebrone.004013EE                ;  Salta si menor
004013EA   |>  31C0                XOR EAX,EAX
004013EC   |.  EB 40               JMP SHORT Zebrone.0040142E                ;  Bad boy
004013EE   |>  DD45 F8             FLD QWORD PTR SS:[EBP-8]                  ;  <<Floating point load>> carga x1
004013F1   |.  D9FE                FSIN                                      ;  Sin(x1)
004013F3   |.  DD5D A8             FSTP QWORD PTR SS:[EBP-58]                ;  <<Store and pop
004013F6   |.  DD45 F0             FLD QWORD PTR SS:[EBP-10]                 ;  <<Floating point load>> carga x2
004013F9   |.  D9FE                FSIN                                      ;  Sin(x2)
004013FB   |.  DD5D A0             FSTP QWORD PTR SS:[EBP-60]                ;  <<Store and pop
004013FE   |.  DD45 A8             FLD QWORD PTR SS:[EBP-58]                 ;  <<Floating point load
00401401   |.  DC4D A0             FMUL QWORD PTR SS:[EBP-60]                ;  Sin(x1) * Sin(x2)
00401404   |.  DF2D 30304000       FILD QWORD PTR DS:[403030]                ;  <<Load integer>> 2386F26FC10000 = 10^16
0040140A   |.  DEC9                FMULP ST(1),ST                            ;  10^16 * (Sin(x1) * Sin(x2))
0040140C   |.  83EC 08             SUB ESP,8
0040140F   |.  DD1C24              FSTP QWORD PTR SS:[ESP]                   ;  <<Store and pop
00401412   |.  E8 D1000000         CALL <JMP.&CRTDLL.floor>
00401417   |.  83C4 08             ADD ESP,8
0040141A   |.  DD5D 98             FSTP QWORD PTR SS:[EBP-68]
0040141D   |.  D9EE                FLDZ                                      ;  <<Load 0.0 onto stack
0040141F   |.  DC5D 98             FCOMP QWORD PTR SS:[EBP-68]               ;  10^16 * (Sin(x1) * Sin(x2)) = 0 ???
00401422   |.  DFE0                FSTSW AX
00401424   |.  9E                  SAHF                                      ;  <<Store AH into FLAGS
00401425   |.  75 05               JNZ SHORT Zebrone.0040142C                ;  Si NO salta todo OK
00401427   |.  31C0                XOR EAX,EAX
00401429   |.  40                  INC EAX
0040142A   |.  EB 02               JMP SHORT Zebrone.0040142E
0040142C   |>  31C0                XOR EAX,EAX
0040142E   |>  C9                  LEAVE
0040142F   \.  C3                  RETN

La primera dificultad que podemos encontrar es que utiliza instrucciones FPU y coma flotante, ya que si no tenemos la vista entrenada nos puede resultar un engorro. Superado esto, la rutina de comprobación se puede resumir así:

  • x1 * x2 != 0
  • x1 != x2
  • x1 > 1 y < 10^10
  • x2 > 1 y < 10^10
  • Floor[10^16 * sin(x1) * sin(x2)] = 0

A priori no parece que tenga mucha dificultad, pero vamos a analizarlo más concienzudamente. Necesitamos que la parte entera del resultado de la multiplicación sea 0, algo que parece sencillo, pero fíjate que la constante 10^16 nos obliga a su vez, a que el resultado del seno sea muy pequeño, cosa que como comprobaréis limita mucho los resultados satisfactorios.

Repasando trigonometría

Cuando nos enseñó nuestro profesor la función del seno nos hizo el siguiente dibujo:

circunferencia_e

Partiendo de la circunferencia unitaria, podemos concluir que el seno de alpha es igual a la altura x. Como lo que nos interesa a nosotros es que el seno sea muy pequeño, en realidad estamos buscando que la x sea lo más pequeña posible. Llegamos entonces a la conclusión de que las soluciones para enteros entre 1 y 10^10 van a ser muy reducidas. Además nos percatamos que el ángulo alpha va a tener que estar muy proximo a 0º – 360 (0 – 2π) y a 180º (π). En el siguiente gráfico queda claro el estrecho margen en el que nos movemos.

circunferencia_angulos_e

Si habéis leído la solución de cronos ahora le encontraréis algo más de sentido a por que él utilizó fracciones continuas de π y cogió como resultado los numeradores más cercanos a 10^10, en su caso 245850922 y 411557987.

Análisis operacional

Vamos a analizar un ejemplo operacional.

sin( x rad)
sin(245850922) = 6,1180653830011163142712109862972e-9
sin(411557987) = 2,536716051963676479648989773448e-9

sin(245850922)*sin(411557987) = 1,5519794664022230015882605365808e-17

10^16 * 1,5519794664022230015882605365808e-17 = 0,15519794664022230015882605365808

Floor(0,15519794664022230015882605365808) = 0

Como veis, el exponente negativo (^-17) debe ser mayor que el positivo (^16) para tener éxito.

Fuerza bruta

Lo que vamos a hacer a continuación es buscar todos los senos con exponente negativo ^-8 ó ^-9 de enteros entre 1 y 10^10, y vamos a cruzar los resultados para determinar todos los resultados válidos.

Preparamos el programa y le dejamos trabajar. En principio vamos a filtrar todos los resultados que tengan exponente negativo y luego ya aislaremos los que nos interesan. Esto lo hago por curiosidad.

aprox

La fuerza bruta nos arroja 63663 resultados con exponente negativo entre ^-5 y ^-9, de los cuales solamente nos quedamos con 65, que son los comprendidos a exponentes de entre ^-8 y ^-9. Los números mágicos son los siguientes:

magicnumbers

Los rojos son exponentes ^-9, el resto ^-8.

La mayoría de estos números solo valen con ciertas combinaciones, de hecho, ninguno vale para todos. Esto se debe, a parte del propio exponente, a que hay senos positivos y negativos y para hacer válido a un seno negativo hay que combinarlo con otro negativo. Esto último se debe únicamente a la interpretación que hace el crackme.

 Finalmente cruzamos los resultados y obtenemos 44 combinaciones de seriales válidos que si obviamos repeticiones se reducen a la mitad.

 checker

Combinaciones válidas:

seriales

Conclusiones

Podemos concluir que para cada 10^10 enteros hay 22 soluciones posibles. Finalmente comentar que si aLoNg3x no hubiera puesto el límite en 10^10, habría soluciones infinitas.

Links


Intro Hoy nos enfrentamos a un crackme realizado en Delphi con un algoritmo bastante sencillo. Está empacado con UPX pero
Sinopsis Enemigo público (Enemy of the State) es una película de acción y suspense dirigida por Tony Scott, estrenada en
AVISO: Debido a que este reto está en activo no publicaré a donde pertenece. En este reto stego nos proporcionan
File carving is the process of reassembling computer files from fragments in the absence of filesystem metadata. Wikipedia. "File carving", literalmente tallado

Aviso: Este crackme forma parte de una serie de pruebas de Yoire.com que todavía está en activo. Lo ético si continuas leyendo este manual es que no utilices la respuesta para completar la prueba sin esfuerzo. 😉

Saltando el Anti-Debug

Abrimos el crackme con Ollydbg y nos salta una protección Anti-Debug.

Si nos fijamos en las «Text Strings» vemos que es la clásica isDebuggerPresent. Pinchamos en ella y vemos claramente el salto que debemos forzar, se encuentra en el offset 401015. Podemos invertir el salto o cambiarlo a JMP para que salte siempre.

Rutina de comprobación del serial

A simple vista vemos instrucciones como FILD y FIDIVR que trabajan con los registros FPU, por lo que tendremos que fijarnos en dichos registros.

Retomemos analizando la rutina de comprobación.

FLD DWORD PTR DS:[403080]    - Carga el entero "720300" en ST7
FSTP [LOCAL.1]               - Guarda "720300" en memoria (Local 1)
MOVSX EDX,BYTE PTR DS:[EAX]  - Coje nuestro primer dígito en ascii y lo carga en EDX
SUB EDX,30                   - Le resta 30 a EDX
PUSH EDX                     - Carga EDX en la pila
FILD DWORD PTR SS:[ESP]      - Carga el valor de EDX en ST0
POP EDX                      - Recupera el valor de la pila
FDIVR [LOCAL.1]              - Divide Local 1 entre nuestro dígito hex y lo guarda en ST0
FSTP [LOCAL.1]               - Guarda el resultado de ST0 en Local 1
INC EAX                      - Siguiente dígito
CMP BYTE PTR DS:[EAX],0      - Comprueba si quedan dígitos en nuestro serial
JNZ SHORT 05_crack.004010F4  - Bucle

Después de la rutina de comprobación simplemente comprueba el valor del resultado de la división con 1 y si es verdad serial válido.

Buscando un serial válido

Podríamos hacer fuerza bruta, pero en esta ocasión no es necesario ya que con la calculadora, boli y papel lo sacamos rápido.
720300 / 2 = 360150
360150 / 2 = 180075
180075 / 5 = 36015
36015  / 5 = 7203
7203   / 3 = 2401
2401   / 7 = 343
343    / 7 = 49
49     / 7 = 7
7      / 7 = 1

Por lo que un serial válido sería: 225537777

La rutina de comprobación del serial podría resumirse también así:

720300 MOD serial = 720300

Links


Aquí tenemos un crackme fuera de lo común, más que nada por que está programado en Brainfuck, un lenguaje de
AVISO: Debido a que este reto está en activo no publicaré a donde pertenece. Ya sabéis que los retos stego
Hoy en día, la descarga de contenido multimedia de ciertas webs es imposible o muy difícil. En ciertos casos lo
Intro Se suele decir que para cada problema hay una solución. Si esto lo llevamos al terreno stego podemos decir

Aquí tenemos un crackme fuera de lo común, más que nada por que está programado en Brainfuck, un lenguaje de programación esotérico bastante complejo.

[-]>[-]<>++++++++[<++++++++++>-]<.+++++++++++++++++.>+++[<++++++>-]<..++++.-
-------.+++.--------------.>++++++[<------>-]<-.>+++++[<------>-]<-.,>,>,>,>
>>>+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+++++++++++++++++++++++++++++++>>>>+++++++++++++++++++++++++++++++++++++++++
+++++++++++>>>>+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
++++++++++++++++++++++++++++++++++++++>>>>++++++++++++++++++++++++++++++++++
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++<<<
<<<<<<<<<<<<<<<<[>>>>>>>-<<<<<<<-]>>>>>>><<+>>[[-]++++++++++++++++++++++++++
++++++++++++++++++++++++++++++++++++++++++++.<]<[>]<<<<<[>>>>>>>>>>-<<<<<<<<
<<-]>>>>>>>>>><<+>>[[-]+++++++++++++++++++++++++++++++++++++++++++++++++++++
++++++++++++.<]<[>]<<<<<<<<[>>>>>>>>>>>>>-<<<<<<<<<<<<<-]>>>>>>>>>>>>><<+>>[
[-]+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
.<]<[>]<<<<<<<<<<<[>>>>>>>>>>>>>>>>-<<<<<<<<<<<<<<<<-]>>>>>>>>>>>>>>>><<+>>[
[-]+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+++.<]<[>]>>>[-]>[-]<+++++++++++++.---.>+++++++[<++++++++++++>-]<.>+++++++[<
--------->-]<+.>+++++[<++++++++>-]<+.>++++[<+++++++>-]<+.>+++++++[<---------
->-]<.>++++++++[<+++++++++>-]<+.+++++++++++.>+++++++[<----------->-]<.>+++++
++[<+++++++++++>-]<-.>+++++++[<------------>-]<+.>++++++++[<+++++++++++>-]<-
.-----.---------.+++++++++++..---------------.+++++++++.>++++++[<-----------
-->-]<.+++++++.>+++++[<++++++>-]<+.-----.++++++++.+++.>++++++[<------>-]<-.-
------.>++++++++[<++++++++++>-]<-.+++.>+++++++++[<--------->-]<-.+++++++.>++
+++[<++++++>-]<+.-----.+++++++++++.>++++++[<------>-]<-.-------.>++++++++[<+
+++++++++>-]<-.+++.>+++++++++[<--------->-]<-.+++++++.>+++++[<+++++>-]<+.+++
+++++.+++.>++++++[<------>-]<-.+++++++...--------------.>++++++++[<+++++++++
++>-]<+.----------.++++++.>+++++++[<------------>-]<-.>++++++++[<+++++++++>-
]<.-------.>+++[<+++++++>-]<.-----------------.>+++++++[<---------->-]<+.>++
+++++[<++++++++++>-]<.-----.++++++++.+++.-------.-.>++++++[<--------->-]<.--
------------.>+++++[<++++++++>-]<+.>++++[<+++++++>-]<+.>+++++++[<---------->
-]<.>+++++++[<++++++++++++>-]<.------------.---.+++++++++++++.-------------.
>+++++++[<---------->-]<+.>++++++++[<+++++++++>-]<+.++++++++++.>+++++++[<---
--------->-]<+.>++++++[<+++++++++++++>-]<.+.+++++.------------.+.+++++.-----
--.>++++++[<---------->-]<+.------------.>++++++++[<+++++++++++>-]<+.-------
---.++++++.>+++++++[<------------>-]<-.>++++++++[<+++++++++>-]<.-------.>+++
[<+++++++>-]<.-----------------.>+++++++[<---------->-]<+.>++++++++[<+++++++
++++>-]<-.--------------.+++++.>++++++[<------------->-]<.+.

La solución que he encontrado yo, es convertir el código brainfuck a algo más amigable y depurarlo hasta encontrar la solución. La conversión la he realizado con VBBrainFNET y luego la depuración con Visual Studio. El crackme te pide una clave de cuatro cifras para darte la solución, pero si no quieres volverte loco puedes amañar los bucles para encontrar la solución.

¡SUERTE!

Enlaces

 

AVISO: Debido a que este reto está en activo no publicaré a donde pertenece.

Ya sabéis que los retos stego son muy variopintos. El otro día me encontré con uno que parecía que iba a ser complejo pero en realidad era bastante sencillo.

Tras varias pruebas complejas infructuosas, se me ocurrió descomponer por canales y efectivamente ese era el camino. Para ello yo utilicé la herramienta StegSolve de mi querido Caesum, pero podéis resolverlo incluso online con Pinetools.

Descomposición RGB

Hoy en día, la descarga de contenido multimedia de ciertas webs es imposible o muy difícil. En ciertos casos lo entiendo, exponer el contenido supone una pérdida de ingresos y eso es inaceptable. Las cadenas de TV son tema aparte, emiten contenido por varios medios y les gusta que lo veas y que lo compartas, eso sí, que lo compartas desde su plataforma, ya que lo que estás compartiendo es un enlace, no el vídeo.

Este caso es un caso intermedio entre una plataforma de pago que codifica sus contenidos y una web que nos permita descargar su contenido directamente.

Imaginemos que vemos un vídeo y queremos mandarlo por Whatsapp a nuestros amigos. Lo primero es echar un vistazo al código fuente de la web y localizar el código del reproductor web (player). Para esta tarea podemos ayudarnos de una extensión muy conocida para navegadores como es Firebug. Una vez instalada, la activamos con F12 y mediante el botón Inspect localizamos el player.

...    
<p itemprop="keywords" itemscope="itemscope" itemtype="http://schema.org/Text" class="antetitulo" lang="es">EL INTERMEDIO LE PILLA EN "EL TRONO"</p>
    <h1 class="title-new" itemprop="headline">Joaquín Reyes se mete en la piel de Juan Carlos I: "Soy tan campechano que podéis llamarme Juan Carlos Palote"</h1>
    <sumary class="entradilla" itemprop="description">
<p><p class="MsoNormal">Los reyes eméritos han celebrado sus bodas de esmeralda y
con motivo de tan señalada fecha, Juan Carlos I ha hecho un hueco en su
apretada agenda para concederle unos minutos a <a title="<b>El Intermedio</b>" href="http://www.lasexta.com/temas/el_intermedio-1" target="_blank"><b>El Intermedio</b></a>. Eso sí, en su
versión de <a title="<b>Joaquín Reyes</b>" href="http://www.lasexta.com/temas/joaquin_reyes-1" target="_blank"><b>Joaquín Reyes</b></a>.  <o:p></o:p></p>	</sumary>

	<div class="great-element-multimedia">
	    <section class="modVideo a3mod_player" data-mod="a3mod_player" data-model="/json/video/7/2017/05/15/591a08c1986b2810b31577c1.json">
	        <a itemprop="url" href="#" class="icon link-content" title="" data-mod-elem="icon">
	            <div class="wrap-img" role="banner">
	                <div itemprop="video" itemscope itemtype="http://schema.org/VideoObject">
	                    <picture>
	                        <!--[if IE 9]><video style="display: none;"><![endif]-->
	                        <source media="(max-width:520px)" srcset="http://fotografias.lasexta.com/clipping/cmsimages02/2017/05/15/14069ECA-B0E4-4F09-A5B7-04B600C016AD/64.jpg" />
	                        <source media="(max-width:1023px)" srcset="http://fotografias.lasexta.com/clipping/cmsimages02/2017/05/15/14069ECA-B0E4-4F09-A5B7-04B600C016AD/60.jpg" />
	                        <source media="(min-width:1024px)" srcset="http://fotografias.lasexta.com/clipping/cmsimages02/2017/05/15/14069ECA-B0E4-4F09-A5B7-04B600C016AD/58.jpg" />
	                        <!--[if IE 9]></video><![endif]-->
								<img src="http://fotografias.lasexta.com/clipping/cmsimages02/2017/05/15/14069ECA-B0E4-4F09-A5B7-04B600C016AD/58.jpg" alt="Joaquín Reyes, como el rey Juan Carlos I" title="Joaquín Reyes, como el rey Juan Carlos I" />
	                    </picture>
	                    <meta itemprop="description" content=""/>
	                    <meta itemprop="name" content=""/>
	                    <meta itemprop="thumbnailUrl" content="" />
	                    <meta itemprop="uploadDate" content=""/>
	                    <meta itemprop="url" content=""/>
	                    <meta itemprop="width" content=""/>
	                    <meta itemprop="height" content=""/>
	                </div>
	            </div>
	        </a>
	    </section>
	</div>
...

Si os fijáis bien, el reproductor hace referencia a un archivo json (591a08c1986b2810b31577c1.json), reconstruimos la url y miramos su contenido

{"id":"591a08c1986b2810b31577c1","type":"video","tipo":"video","subtipo":"video","imgThumb":"http://fotografias.lasexta.com/clipping/cmsimages02/2017/05/15/14069ECA-B0E4-4F09-A5B7-04B600C016AD/29.jpg","imgPoster":"http://fotografias.lasexta.com/clipping/cmsimages02/2017/05/15/14069ECA-B0E4-4F09-A5B7-04B600C016AD/31.jpg","live":false,"autoplay":true,"sources":[{"src":"http://vclip.atresmedia.com/vclip/_definst_/smil:assets10/2017/05/15/01229E28-A57E-4AC9-AFE7-EF1C27B5AA2A/es.smil/manifest_mvlist.mpd","type":"application/dash+xml"},{"src":"http://vclip.atresmedia.com/vclip/_definst_/smil:assets10/2017/05/15/01229E28-A57E-4AC9-AFE7-EF1C27B5AA2A/es.smil/playlist.m3u8","type":"application/vnd.apple.mpegurl"}],"omniture":{"section":"Joaquín Reyes","category":"El Intermedio","channel":"lasexta","type":"short","name":"Joaquín Reyes se mete en la piel de Juan Carlos I: \"Soy tan campechano que podéis llamarme Juan Carlos Palote\"","embeddedMode":false},"comscore":{"comscoreTag":"LASEXTA.COM","channel":"lasexta","kantar":{"programID":"1019","firstBroadcastDate":"*null","firstBroadcastTime":"*null","typeTvStream":"0002","kantarGenre":"0","channelId":"240"},"content_form":"short_form"},"urlHits":"http://hits.lasexta.com/l6//591a08c1986b2810b31577c1/3/348128,351435,351827,351865/","duration":"211.797333","embeddedUrl":"<iframe src=\"http://www.lasexta.com/embed/el-intermedio-le-pilla-en-el-trono/video/7/2017/05/15/591a08c1986b2810b31577c1\" width=\"560\" height=\"315\" frameborder=\"0\" allowfullscreen></iframe>","playondraw":true,"nextRelated":{"service_url":"http://www.lasexta.com/json/video/7/2017/05/15/591a08c1986b2810b31577c1_related.json"},"subtitle":[],"titulo":"Joaquín Reyes se mete en la piel de Juan Carlos I: \"Soy tan campechano que podéis llamarme Juan Carlos Palote\"","descripcion":"","sociales":{"hasTwitter":true,"hasFacebook":true,"hasGooglePlus":true,"hasWhatsapp":true,"twitter":"EL INTERMEDIO LE PILLA EN “EL TRONO”","facebook":"EL INTERMEDIO LE PILLA EN “EL TRONO”","googlePlus":"EL INTERMEDIO LE PILLA EN “EL TRONO”","whatsapp":"","hashtag":"","via":"sextaNoticias","urlPage":"https://goo.gl/cu98f0"},"vp_data":{"vp_category":"Atresmedia/Lasexta/programas/el-intermedio*","vp_tags":"","vp_content_form":"short_form"}}

Se puede ver a simple vista una lista de reproducción playlist.m3u8, cuyo contenido contiene más listas de reproducción con diferentes calidades.

#EXTM3U
#EXT-X-VERSION:3
#EXT-X-STREAM-INF:BANDWIDTH=796400,CODECS="avc1.77.30,mp4a.40.5",RESOLUTION=640x360
chunklist_b724000.m3u8
#EXT-X-STREAM-INF:BANDWIDTH=1159400,CODECS="avc1.77.30,mp4a.40.5",RESOLUTION=640x360
chunklist_b1054000.m3u8
#EXT-X-STREAM-INF:BANDWIDTH=1643400,CODECS="avc1.77.30,mp4a.40.5",RESOLUTION=720x404
chunklist_b1494000.m3u8
#EXT-X-STREAM-INF:BANDWIDTH=2248400,CODECS="avc1.77.31,mp4a.40.5",RESOLUTION=1280x720
chunklist_b2044000.m3u8

Reconstruimos la URL para la lista de reproducción de mayor calidad e inspeccionamos su contenido.

#EXTM3U
#EXT-X-VERSION:3
#EXT-X-TARGETDURATION:10
#EXT-X-MEDIA-SEQUENCE:0
#EXTINF:10.0,
media_b2044000_0.ts
#EXTINF:10.0,
media_b2044000_1.ts
#EXTINF:10.0,
media_b2044000_2.ts
#EXTINF:10.0,
media_b2044000_3.ts
#EXTINF:10.0,
media_b2044000_4.ts
#EXTINF:10.0,
media_b2044000_5.ts
#EXTINF:10.0,
media_b2044000_6.ts
#EXTINF:10.0,
media_b2044000_7.ts
#EXTINF:10.0,
media_b2044000_8.ts
#EXTINF:10.0,
media_b2044000_9.ts
#EXTINF:10.0,
media_b2044000_10.ts
#EXTINF:10.0,
media_b2044000_11.ts
#EXTINF:10.0,
media_b2044000_12.ts
#EXTINF:10.0,
media_b2044000_13.ts
#EXTINF:10.0,
media_b2044000_14.ts
#EXTINF:10.0,
media_b2044000_15.ts
#EXTINF:10.0,
media_b2044000_16.ts
#EXTINF:10.0,
media_b2044000_17.ts
#EXTINF:10.0,
media_b2044000_18.ts
#EXTINF:10.0,
media_b2044000_19.ts
#EXTINF:10.0,
media_b2044000_20.ts
#EXTINF:1.92,
media_b2044000_21.ts
#EXT-X-ENDLIST

Se pueden ver 21 archivos con extensión TS de 10 segundos cada uno a excepción del último que dura 1.92 segundos. Los archivos TS no son más que archivos MP4 por lo que una vez descargados, los podemos unir con MP4Tools por ejemplo.

La tarea es costosa, pero si os apetece enviar un vídeo en vez de un enlace, ya sabéis que en determinados casos se puede hacer.

Intro

Se suele decir que para cada problema hay una solución. Si esto lo llevamos al terreno stego podemos decir que para cada reto hay una herramienta que nos da la solución. En la entrada anterior os comenté que mi fondo de armario son steganabara y stegsolve aunque cuando la imagen es PNG, una herramienta de uso obligatorio es TweakPNG.

La víctima

imagen original del reto

Nos enfrentamos a una imagen PNG de 112KB (115477 bytes) con una resolución de 300×225 píxeles. A priori llama la atención el elevado tamaño VS la baja resolución, lo que aviva nuestras sospechas de que esos KB extras se deban a que haya insertado otro archivo en su interior.

Chunk

Los archivos PNG tienen la peculiaridad de que están divididos en secciones (chunks) en la que algunas son críticas como IHDR (cabecera), IDAT (la imagen) e IEND (final) y otras muchas secundarias como por ejemplo tEXt (para insertar texto). Al explorar el archivo con TweakPNG vemos la cabecera, varios chunks de texto, muchos IDAT que he combinado en uno para mejorar el análisis y la sección final. Si os fijáis, al combinar los IDAT ha cambiado el tamaño del PNG de 115447 a 110893 bytes aunque en este caso sigue siendo un tamaño elevado.

aspecto original de los chunks
aspecto de los chunks tras combinar todos los IDAT en uno

Llama la atención el chunk cHRm de 12595 bytes del que TweakPNG ya nos avisa que no reconoce su contenido. Cargamos la imagen en un editor hexadecimal y buscamos la palabra «Great» que es el texto que hay justo antes del chunk cHRm que nos interesa.

detalle del chunk cHRm en editor hexadecimal

La búsqueda da sus frutos ya que el chunk parece que está formado por un archivo mp4. A partir de aquí tenemos varias opciones, para mí la más limpia es con un editor hexadecimal apuntar los offsets de inicio y fin del chunk y crear un archivo nuevo con el contenido. Otra opción es exportar el chunk desde TweakPNG con extensión mp4 y borrar los bytes del nombre del chunk con un editor hexadecimal, de lo contrario no podréis reproducir el mp4.

nombre del chunk a borrar para que funcione el mp4

Hecho esto, al escuchar el mp4 obtenemos la solución del reto.

Enlaces

Nota: si algo os pide clave es deurus.info