Contenido principal

Spy Orange

Febrero 18, 2013

Two files are provided in the challenge: oranges.pdf and oranges.wav.

First one contains the text:

February 15, 1973
SUBJECT: RE: Spies Among Us
As per action US182.97, we have continued to
monitor the suspected foreign spies via
telephone wiretap. At 8:12AM this morning, a
call was placed from ORCHID to LILAC containing
what is believed to be a coded message.
You will find enclosed a recording of this
event on audio cassette tape. We request the
immediate analysis of this recording for hidden
meaning or message. This tasking will expire
in 48 hours, at which time OPERATION PSIFERTEX
will commence as planned.
Lt Gen Samuel C. Phillips,
United States Air Force
Director of the NSA

Second one contains a transmission using frecuency-shift keying (FSK), we can follow these steps to decode the signal:

:arrow: Download and compile MultimonNG
:arrow: Use MultimonNG with the CLIPFSK demodulator (Phiber rules!):

# ./multimonNG -t wav -c -a CLIPFSK ../oranges.wav
multimonNG  (C) 1996/1997 by Tom Sailer HB9JNX/AE4WA
            (C) 2012 by Elias Oenal
available demodulators: POCSAG512 POCSAG1200 POCSAG2400 EAS UFSK1200 CLIPFSK AFSK1200 AFSK2400 AFSK2400_2 AFSK2400_3 HAPN4800 FSK9600 DTMF ZVEI SCOPE
Enabled demodulators: CLIPFSK
sox WARN dither: dither clipped 9 samples; decrease volume?
CLIPFSK: CS DATE=02102221 CID=6169405176 CNT=BIT.LY/U3MMRU

We got a phone number from United States (6169405176), and a that points to

The file contains the text:

Lzw ywfwjsd osflk log hgsuzwv wyyk vwdanwjwv xgj tjwscxskl. Qgmj afyjwvawflk sjw wfudgkwv.


Using ROT-8 algorithm, we can retrieve the original text that contains a password protected file encoded with base64:

The general wants two poached eggs delivered for breakfast. Your ingredients are enclosed. 


When you called the phone (6169405176), you could hear The Lincolnshire Poacher:

Using "The Lincolnshire Poacher" as the password for the zip, we were able to get the flag:
I see all the code and I watch it run

Archivado en: Retos informáticos | Comentarios (0)

Rutinas para la detección del uso de máquinas virtuales

Enero 11, 2013

A continuación puede encontrar tres rutinas para la detección de ambientes virtualizados que hacen uso del software VMware, Oracle VM VirtualBox, Windows Virtual PC o QEMU, y cuyo enfoque es para el sistema operativo Windows. La detección es realizada buscando valores por defecto en la configuración de las tarjetas de red (dirección MAC), valores localizados en el registro, y a través de Windows Management Instrumentation (Que finalmente es traducido también en valores encontrados en el registro).

Detección a través de la dirección MAC

Se obtiene la dirección MAC de todas las tarjetas de red y se compara con prefijos predeterminados por cada software:

Fabricante Prefijo
VMware 00:05:69:xx:xx:xx
VMware 00:0C:29:xx:xx:xx
VMware 00:1C:14:xx:xx:xx
Oracle VM VirtualBox 08:00:27:xx:xx:xx
Windows Virtual PC 00:03:FF:xx:xx:xx
QEMU 52:54:00:xx:xx:xx

La rutina puede ser observada a continuación:

BOOL mac_test()
 unsigned char MACData[8];

 DWORD dwEntriesRead;
 DWORD dwTotalEntries;
 BYTE *pbBuffer;

 NET_API_STATUS dwStatus = NetWkstaTransportEnum(NULL, 0, &pbBuffer, MAX_PREFERRED_LENGTH, &dwEntriesRead, &dwTotalEntries, NULL);
 pwkti = (WKSTA_TRANSPORT_INFO_0 *)pbBuffer;

 for (DWORD i = 1; i < dwEntriesRead; i++) {
  swscanf((wchar_t *)pwkti[i].wkti0_transport_address, L"%2hx%2hx%2hx%2hx%2hx%2hx", &MACData[0], &MACData[1], &MACData[2], &MACData[3], &MACData[4], &MACData[5]);

  if ((MACData[0] ==  0 && MACData[1] ==  5 && MACData[2] == 105) || // VMware
   (MACData[0] ==  0 && MACData[1] == 12 && MACData[2] ==  41) || // VMware
   (MACData[0] ==  0 && MACData[1] == 28 && MACData[2] ==  20) || // VMware
   (MACData[0] ==  0 && MACData[1] == 80 && MACData[2] ==  86) || // VMware
   (MACData[0] ==  8 && MACData[1] ==  0 && MACData[2] ==  39) || // Oracle VM VirtualBox
   (MACData[0] ==  0 && MACData[1] ==  3 && MACData[2] == 255) || // Windows Virtual PC
   (MACData[0] == 82 && MACData[1] == 84 && MACData[2] ==   0)) { // QEMU

   dwStatus = NetApiBufferFree(pbBuffer);
   return TRUE;

 dwStatus = NetApiBufferFree(pbBuffer);
 return FALSE;

Detección a través del registro de Windows

Se obtiene el valor de la cadena "0" en la clave "HKLM\SYSTEM\CurrentControlSet\Services\Disk\Enum", y se busca valores predeterminados por cada software:

Fabricante Subcadena
VMware VMware
Oracle VM VirtualBox VBOX
Windows Virtual PC DiskVirtual
Windows Virtual PC VIRTUAL

La rutina puede ser observada a continuación:

BOOL reg_test()
 HKEY hKey;
    CHAR szBuffer[1024];  
    ULONG hSize = sizeof(szBuffer);

    if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, "SYSTEM\\CurrentControlSet\\Services\\Disk\\Enum", 0, KEY_READ, &hKey) == ERROR_SUCCESS) {
        if (RegQueryValueEx(hKey, "0", NULL, NULL, (unsigned char *)szBuffer, &hSize) == ERROR_SUCCESS) {
            if (strstr(szBuffer, "VBOX") != NULL ||
    strstr(szBuffer, "VMware") != NULL ||
    strstr(szBuffer, "DiskVirtual") != NULL ||
    strstr(szBuffer, "VIRTUAL") != NULL ||
    strstr(szBuffer, "QEMU") != NULL) {

                return TRUE;

 return FALSE;

Detección a través de Windows Management Instrumentation

El procedimiento realizado es similar a los dos anteriores, se busca valores predeterminados a través del uso de WMI. Dichos valores son obtenidos a través de WQL, usando las siguientes consultas y valores predeterminados por cada software:


Fabricante Subcadena
Oracle VM VirtualBox VBOX
Windows Virtual PC A M I - 8000914

SELECT Model FROM Win32_ComputerSystem

Fabricante Subcadena
VMware VMware
Oracle VM VirtualBox VirtualBox
Windows Virtual PC Virtual Machine
QEMU Bochs


Fabricante Subcadena
Oracle VM VirtualBox VBOX

SELECT PNPDeviceID FROM Win32_DiskDrive

Fabricante Subcadena
VMware VMware
Oracle VM VirtualBox VBOX
Windows Virtual PC DISKVIRTUAL

SELECT Description FROM CIM_LogicalDevice

Fabricante Subcadena
VMware VMware
Oracle VM VirtualBox VirtualBox

La rutina aunque un poco más compleja pero no menos importante puede ser observada a continuación:

BOOL wmi_test()
 CHAR buffer[64];
 CHAR value[256];
 PSTR objects[5][6] =   {{"Win32_BIOS", "Version", "VBOX", "BOCHS", "A M I  - 8000914", NULL},
       {"Win32_ComputerSystem", "Model", "VirtualBox", "Bochs", "VMware", "Virtual Machine"},
       {"Win32_CDROMDrive", "DeviceID", "VBOX", "QEMU", NULL, NULL},
       {"Win32_DiskDrive", "PNPDeviceID", "VBOX", "QEMU", "VMware", "DISKVIRTUAL"},
       {"CIM_LogicalDevice", "Description", "VirtualBox", "VMware", NULL, NULL}};

 HRESULT hres;
    hres = CoInitializeEx(0, COINIT_MULTITHREADED);
    if (FAILED(hres))
        return FALSE;

    if (FAILED(hres)) {
        return FALSE;

    IWbemLocator *pLoc = NULL;
    hres = CoCreateInstance(CLSID_WbemLocator, 0, CLSCTX_INPROC_SERVER, IID_IWbemLocator, (LPVOID *) &pLoc);
    if (FAILED(hres)) {
        return FALSE;

    IWbemServices *pSvc = NULL;
    hres = pLoc->ConnectServer(_bstr_t(L"ROOT\\CIMV2"), NULL, NULL, 0, NULL, 0, 0, &pSvc);
    if (FAILED(hres)) {
        return FALSE;

    if (FAILED(hres)) {
        return FALSE;

 for (int i = 0; i < 5; i++) {
  IEnumWbemClassObject* pEnumerator = NULL;
  memset(buffer, 0, 64);
  strcpy(buffer, "SELECT * FROM ");
  strcat(buffer, objects[i][0]);

  hres = pSvc->ExecQuery(bstr_t("WQL"), bstr_t(buffer), WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY, NULL, &pEnumerator);
  if (FAILED(hres)) {
   return FALSE;
  IWbemClassObject *pclsObj;
  ULONG uReturn = 0;
  while (pEnumerator) {
   HRESULT hr = pEnumerator->Next(WBEM_INFINITE, 1, &pclsObj, &uReturn);

   if (0 == uReturn)

   VARIANT vtProp;

   wchar_t wcstring[256];
   mbstowcs(wcstring, objects[i][1], 256);

   hr = pclsObj->Get(wcstring, 0, &vtProp, 0, 0);
   memset(value, 0, 256);
   strcpy(value, _bstr_t(vtProp.bstrVal).operator char *());
   for (int j = 2; j < 6; j++) {
    if (objects[i][j] == NULL)

    if (strstr(value, objects[i][j]) != NULL) {

     return TRUE;



    return FALSE;

Pruebas finales

Haciendo uso del anterior código y ejecutando las tres rutinas de forma consecutiva:

int main(int argc, char* argv[])
 printf("MAC Address Test: %d\n", mac_test());
 printf("Registry Test: %d\n", reg_test());
 printf("Wmi Test: %d\n", wmi_test());

 return 0;

Se obtiene el siguiente resultado (True indica detección exitosa):

Fabricante MAC Test REG Test WMI Test
VMware True True True
Oracle VM VirtualBox True True True
Windows Virtual PC True True True
QEMU True True True
Windows 7 Sin virtualizar True False True

Código fuente

El archivo comprimido contiene cuatro archivos:
:arrow: vmtest.c: Recopilación de las rutinas.
:arrow: StdAfx.cpp, StdAfx.h: Código necesario para la compilación.
:arrow: vmtest.exe: Ejecutable de prueba (Para el arriesgado).

Archivado en: Seguridad, Sistemas operativos | Comentarios (2)

BarCamp Security Edition v3.0

Noviembre 16, 2012

Este sábado primero de diciembre de 8:00 a.m. a 8:00 p.m. se estará realizando en diferentes ciudades de Colombia el evento BarCamp Security Edition, en su versión número 3. Es una buena oportunidad para compartir y conocer nuevas personas, quizás estaré por esos lados, ya que estoy pensando en dar una pequeña desconferencia sobre las diferentes experiencias con los Wargames y Capture The Flags en los cuales me he desempeñado en nuestro equipo NULL Life, a ver si me animo!

"Como objetivo principal el evento busca reunir personas y compartir experiencias alrededor del tema de seguridad informática, es por esto que durante un día se preparan conferencias, talleres, demostraciones y retos informáticos que ayudan a fortalecer los lazos entre los asistentes y por otro lado ayudan a crear consciencia sobre un tema tan importante como es el de seguridad informática en el país."

Enlaces de interés

:arrow: BarCamp Security Edition Colombia
:arrow: BarCamp en la Wikipedia

Archivado en: Miscelaneo | Comentarios (0)

Linux Unified Key Setup (LUKS) template for 010 Editor

Agosto 10, 2012

This is my first template for my favorite multipurpose editor: 010 Editor. The idea behind the template comes from a challenge of DC3 2012 ("Create a program that will perform a dictionary and/or brute force attack against the encrypted volume")

The first version of this template is able to identify the header of a disk encrypted with LUKS specification, including:
:arrow: Ciphername used in the disk encryption
:arrow: Mode of the cipher
:arrow: Hash specification
:arrow: Variables related to the password

Disk data can not be decrypted using the template.

This is a preview of the template using the file provided in the challenge:

You can download the template from here: LUKS Template

Template is also included in the 010 Editor template repository.

Archivado en: Criptografía, Seguridad | Comentarios (1)

Uso de entropía para la obtención de candidatos a texto plano

Agosto 3, 2012

El día 12 de Julio, a través de Facebook, publiqué un pequeño desafío en el cual se debía obtener el texto plano de una cadena cifrada.

El desafío en cuestión era:
"Gana una licencia original de ESET Smart Security Home Edition resolviendo un reto! Estos son los datos:
Algoritmo: RC2
Modo: ECB
Texto cifrado: jf3NiZlBlpU3kRe3Kndb5RfEqBrukWk/KoEx1mNYKTc="

El desafío se extendió en twitter para tener más cobertura, pero luego de dos semanas sin recibir respuestas decidí rifar la licencia.


Durante el transcurso de las dos semanas se dieron las siguientes pistas:
:arrow: Entropía de informaciónía_(información)!
:arrow: if (entropy(variable) <= 4,7) then variable is a plaintext candidate
:arrow: password can be found in a common wordlist.

Juntando las pistas y sin entrar en mucho detalle, construimos un pequeño script en PHP que permite obtener los posibles candidatos a texto plano usando el concepto de entropía:


// Decodificamos el texto cifrado para llevarlo a su estado natural
$cipher = base64_decode('jf3NiZlBlpU3kRe3Kndb5RfEqBrukWk/KoEx1mNYKTc=');

// Leemos las contraseñas de una lista con palabras comunes
$words = file('worst.txt');

foreach ($words as $word) {
    $word = trim($word);
    // Desciframos el texto usando una de las palabras comunes
    // El algoritmo es RC2 y el modo ECB
    $text = mcrypt_decrypt(MCRYPT_RC2, $word, $cipher, MCRYPT_MODE_ECB);
    // En este punto se encuentra la magia
    // Si la entropía del texto descifrado es menor o igual a 4.7
    // entonces lo consideramos como un posible candidato a texto plano
    if (entropy($text) <= 4.7) {
        echo $word . "\t" . $text . "\t" . entropy($text) . PHP_EOL;

// Función de entropía que obtiene el grado de dispersión del texto plano
// a menor dispersión, mayor probabilidad de encontrar un texto coherente
// a mayor dispersión, mayor probabilidad de encontrar un texto binario
function entropy($text) {
    $entropy = 0;
    $len = strlen($text);
    if ($len == 0)
        return 0;
    $map = array();
    for ($i = 0; $i < 256; $i++)
        $map[$i] = 0;
    for ($i = 0; $i < $len; $i++)
    for ($i = 0; $i < 256; $i++) {
        $p = $map[$i] / $len;
        if ($p > 0) {
            $entropy -= $p * (log($p) / log(2));
    return $entropy;

Suponiendo que los únicos caracteres que conforman el texto plano son las veintiseis (26) letras del alfabeto latino (a - z), la entropía por carácter se calcula usando la fórmula "Logaritmo en base 2 de 26", que tiene como resultado 4,7.

Ejecutando el script observamos las posibles claves y valores para el texto plano:

Clave   Candidato texto plano                   Entropía
123456  Quiero una licencia de 3537!            3.890
fuck    G╩▄ı·Úæ$9 íÊù­æYl4─çÝ─ cı♠\╣▄¾Ï♠         4.625
love    ­►õÑq─#êC¾►▀\1‼↑±╠«ìD¿┤bàfbà▀¶‼←         4.687
sparky  Á┌®§*,¯ê:~ ¬"Z¬®ê╔ü!>»oDåñZOh»,Ù        4.625
andrea  ♠¯┌ºoòaR¨é└ù£┘ò\]aùÉp┘bh¾┌├µ|♫Kp        4.625
morgan   _zß┴ó°QôYıùø►É%Ö▲ïÚ5÷╚┐┴ã▓5 ßı♫        4.687
teens   ]╬╬┼"G♀P«↑♠¨áxóÀb╬r2♫uä?.2♥╬‼|Ýæ        4.687
lakers  k¸╗☼¶Ûµø]][#aª┬¤èª<┼?ªÑ║¸ƒ║¤Xîñ4        4.601
monster Ä╗↔·\7SÓ+_ 0ÿï♦┤Ï→ƒ7¨­¶§l\ÙCÏÄu♦         4.687
baby    UÀ☺╣ÔÃ5yî(█º┴Ô­D±¨vâyá|ÑÆ☼DÆU¨j|         4.562
squirt  íÜ‗ð½♫ÆQ©╠ü ñ­£©þ─↑³|ÜíJ(%Nº▲Qý³         4.687
hunting õC♠/z♠¢═à♥█◄JUÂ\Y▓\1┌▓ÒÒ↑U⌂ÚÁiã▬        4.687

El mismo concepto puede apreciarse en herramientas de criptografía como Cryptool:

Entropía de información

La respuesta final al desafío era: Quiero una licencia de 3537!

Resolución alternativa

La resolución alternativa por Perverths0 es utilizar expresiones regulares para descartar cualquier texto en plano que no contenga caracteres ASCII, una muy buena idea también:


$method = 'RC2-ECB';
$decode = 'jf3NiZlBlpU3kRe3Kndb5RfEqBrukWk/KoEx1mNYKTc=';

$fp = fopen('cain.txt', 'r');
while ($linea = fgets($fp, 1024)) {
    $pass = trim($linea);
    $plain = mcrypt_decrypt(MCRYPT_RC2, $pass, base64_decode($decode), MCRYPT_MODE_ECB);
    if (!preg_match('/[\x80-\xFF]/', $plain)) {
        echo "La respuesta es: $pass - $plain";

Archivado en: Criptografía, Retos informáticos | Comentarios (2)