Solución Reto #4: Multi-estegano
Marzo 29, 2009
A continuación, presento las soluciones enviadas por paipai y Saurom (En orden de resolución). Felicitaciones para ambos!
Paipai no realizó la primera parte del reto, pero si envió un script realizado en PHP donde nos explica como obtener el archivo oculto que se encuentra en la imagen de la cabina.
Script PHP realizado por paipai
$im1=imagecreatefrompng('estegano_alpha.png');
$ancho=imagesx($im1);//ancho de la imagen
$alto =imagesy($im1);//alto de la imagen
$buf="";
$temp="";
//buscar en la imagen los valores RGB y ALPHA de la imagen
for($x=0;$x<$ancho;$x++){
for($y=0;$y<$alto;$y++){
$color = imagecolorat($im1,$x,$y);
$r=($color >> 16) & 0xFF; //color rojo
$g=($color >> 8) & 0xFF; //color verde
$b=$color & 0xFF; //color azul
$a=($color & 0x7F000000) >> 24;//canal alpha
//guardamos en $buf el valor del canal alpha
$buf.=$a;
}
}
//convertimos los bits a decimal(de 8 en 8 ) y luego a su caracter ASCII
//y se guarda todo en la variable $temp
for($i=0;$i<strlen($buf);$i+=8)
{
$temp.=chr(bindec(substr($buf,$i,8)));
}
//Lo guardamos todo en un archivo
//como no sabemos que tipo de archivo es, lo guardo como hex,
//luego con un editor hex, miramos la cabecera y vemos que es GIF
//asi que cambiamos la extension a gif y ya lo podemos ver
$fp=fopen('data.hex',"w");
fwrite($fp,$temp);
fclose($fp);
imagedestroy($im1);
?>
Solución oficial por Saurom
Parte1
- gif65535.gif Imagen del reto
- 1 a 4.jpg Imagenes explicativas de como usar el GIFAnimator para solucionar el reto
- gif65535_animado.gif Solucion del reto
- GIFAnimator.exe No incluido. Bajar de http://microsoft-gif-animator.softonic.com/
Parte2
- estegano_alpha.png Imagen del reto
- reto4.php Codigo php usado para pasar el reto
- hide_estegano_alpha.gif Solucion del reto
Parte1
Y como resultado:
Parte2
/****************************************************************************************/
/* Solucion Reto #4: Multi-estegano Parte 2 */
/* */
/* Autor: Saurom (saurom@yashira.org) */
/* */
/* Requisitos: Tener cargada la libreria gd y estar la imagen estegano_alpha.png en el */
/* mismo directorio que el script */
/* */
/* Explicacion para entender el reto: */
/* Algunos formatos de imagen como puede ser PNG guardan cada pixel en RGBA, lo que */
/* significa que se guarda informacion para R (color rojo), G (color verde), B (color */
/* azul) y A (transparencia de la imagen) tambien conocido como canal alpha. */
/* */
/* El canal alpha contiene la informacion referente a la transparencia de la imagen. */
/* Se usa un byte en cada pixel para el canal alpha, de manera que, si este tiene valor */
/* 0, la imagen es completamente transparente, si tiene valor 255 la imagen no tiene */
/* transparencia y para valores entre 0 y 255 el nivel de transparecia cambia */
/* de mas a menos. */
/* */
/* Sabiendo esto, porque no usar este canal que no se muestra en la imagen para ocultar */
/* informacion? Pues eso es en lo que consiste el reto, en recorrer toda la imagen */
/* obteniendo sus valores alpha de cada pixel en donde se encuentra oculta la */
/* informacion (en este caso una imagen) */
/****************************************************************************************/
// Creditos de la funcion => http://snarkles.net/scripts/sneak/sneak.php
function bin2asc($str) {
$str = str_replace(" ", "", $str);
$text_array = explode("\r\n", chunk_split($str, 8));
for ($n = 0; $n < count($text_array) - 1; $n++) {
$newstring .= chr(base_convert($text_array[$n], 2, 10));
}
return $newstring;
}
// Creamos la imagen en la que buscamos "algo oculto"
$img = imagecreatefrompng('estegano_alpha.png');
$chr = '';
// Recorremos el ancho de la imagen
for($x=0;$x<imagesx($img);$x++){
// Y a medida que recorremos el ancho vamos recorriendo el alto
for($y=0;$y<imagesy($img);$y++){
// Obtenemos el color de cada pixel [posición (x,y) en la imagen]
$rgba = imagecolorat($img,$x,$y);
// Obtenemos el valor del canal alpha
$alpha = ($rgba & 0x7F000000) >> 24;
// Almacenamos en un string todos los valores alpha en binario
$chr .= base_convert($alpha,10,2);
}
}
// Convertimos el valor binario obtenido a su ascii correspondiente
$hide = bin2asc($chr);
// Si imprimimos el resultado [echo $hide;] vemos que la cabecera es GIF89a por lo que salvamos el resultado como .gif para
// tener la imagen oculta
file_put_contents('hide_estegano_alpha.gif',$hide);
// Y ya tenemos un monigote cun bonito movimiento pelvico y el correspondiente mensajito....
?>
Solución oficial alternativa
Primera parte
Como lo explico Saurom, la imagen de la primera parte se compone de otras dos imágenes, su extensión es .gif, por lo tanto es un GIF animado, como todo archivo imagen tiene su estructura, este también lo tiene, a continuación el formato de los gif animados (Solo está la parte de la extensión de control gráfico, la cabecera del GIF como tal no está):
En verde, se encuentra la extensión de la aplicación, la string NETSCAPE2.0 está definida por la longitud en el tamaño del bloque, inmediatamente después de ésta extensión, se encuentra la primera imagen, la cual se compone de una cabecera, un byte que maneja tres atributos: color transparente, entrada del usuario y método x; dos bytes que indican el tiempo que transcurre antes de pasar a la siguiente imagen, cada byte son x/100 segundos (Donde x es el valor que le otorguemos a este atributo), el index del color transparente, un byte que indica que ha terminado la cabecera de la imagen y el contenido de la imagen como tal.
Podemos identificar en la primera imagen, que el tiempo que transcurre antes de mostrar la siguiente imagen es "FF FF", que en decimal es 65535, adicional a esto, representa x/100 segundos, así que el tiempo real es de 655.35 minutos, que equivale a casi once horas. Así que deberíamos esperar casi once horas para poder ve la siguiente imagen.
Para la cabecera de la segunda imagen, tenemos que el tiempo que transcurre para mostrar la siguiente imagen es de "00 00", que prácticamente serían cero segundos, así que no valdría de nada esperar casi once horas, para poder ver la clave en tan solo 0 segundos (La imagen aunque tenga este atributo a 0 segundos sí se muestra, pero demora milésima de segundo en mostrarse)
Así que lo único que tenemos que hacer es cambiar la cabecera de los dos archivo para que se muestre en un tiempo razonable, si queremos mostrar cada imagen en un tiempo de un segundo, debemos modificar los dos bytes a "64 00", donde 64 representa el número cien en decimal, y si reemplazamos en la fórmula, entonces sería 100/100 segundos, que sería un segundo.
Segunda parte
La pista para la segunda parte era "canal alpha". El canal alpha se utiliza en las imágenes PNG para indicar transparencia, los valores que acepta es de 0 (Sin transparencia) hasta 127 (Total transparencia), como podemos ver en las siguientes imágenes con canales de transparencia (0, 1, 32, 64, 96, 127 respectivamente)
Vemos claramente la diferencia entre las imágenes, y como se va "desapareciendo" (Aunque en realidad los datos RGB siguen estando allí). Observamos con claridad la poca diferencia entre el archivo guardado con alpha0 y con alpha1, ya que el canal alpha fue aplicado a TODO el archivo, pero si hacemos que el canal alpha varie entre cero y uno através de toda la imagen (Aplicado a un pixel por separado y no a toda la imagen completa), entonces el ojo humano no es capaz de apreciar la diferencia entre pixeles.
El reto fue elaborado precisamente con la misma idea, como únicamente fueron guardados ceros y unos (Ya que no percibimos diferencia alguna en la imagen, esto es que no se ven mezclas de partes transparentes con partes no transparentes), es sólo cuestión de extraer dicho canal, convertilo a ASCII y guardarlo en un archivo.
Me tome la tarea de realizar un script que analizara el canal, y elaborara una imagen en base a las siguientes consideraciones:
Si el pixel que está leyendo posee un canal alpha diferente a cero, entonces ese pixel lo reemplaza por un pixel blanco
Si el pixel que está leyendo posee un canal alpha igual a cero, entonces ese pixel lo reemplaza por un pixel negro
El script es el siguiente:
// @titulo: Analizador de imágenes PNG con canales alpha
// @autor: http://www.sinfocol.org
// @descripción: Analiza una imagen para encontrar si está usando canales alpha
//--
//Abrimos la imagen
$img = imagecreatefrompng('estegano_nuevo.png');
//Obtenemos sus dimensiones
$x = imagesx($img);
$y = imagesy($img);
//Creamos una nueva imagen
$nueva_imagen = imagecreate($x,$y);
//Obtenemos el index de los colores blanco y negro respectivamente
$blanco = imagecolorallocate($nueva_imagen,255,255,255);
$negro = imagecolorallocate($nueva_imagen,0,0,0);
//Recorremos toda la imagen
for($i=0;$i<$x;$i++){
for($j=0;$j<$y;$j++){
//Extraemos su alpha
$alpha = (imagecolorat($img,$i,$j) & 0x7f000000) >> 24;
//Si el alpha es algún número diferente a cero pone un pixel blanco en la nueva imagen
if($alpha) imagesetpixel($nueva_imagen,$i,$j,$blanco);
//De lo contrario pone un pixel negro (Si alpha = 0)
else imagesetpixel($nueva_imagen,$i,$j,$negro);
}
}
//Mostramos la nueva imagen
header('Content-Type: image/png');
imagepng($nueva_imagen);
El output es el siguiente:
Apreciamos que efectivamente existe un canal alpha, y que el posible archivo está guardado en forma de columnas (Por la columna negra al final de la imagen, es poco probable que hayan bits nulos continuos "00000000000000" en una archivo con alta dispersión), así que el siguiente paso es elaborar un script que extraiga el canal alpha, lo convierta a ASCII y lo almacene en un archivo.
// @titulo: Script solucionador reto #4 - Parte2
// @autor: http://www.sinfocol.org
// @descripción: Extrae el canal alpha en un punto y lo guarda a un archivo, sabiendo que solo contiene unos y ceros
//--
//Abrimos la imagen
$img = imagecreatefrompng('estegano_alpha.png');
$x = imagesx($img);
$y = imagesy($img);
//Variable para guardar los canales alpha
$chr='';
//Recorremos la imagen en columnas (Por cada columna recorremos todas sus filas)
for($i=0;$i<$x;$i++){
for($j=0;$j<$y;$j++){
//Extraemos su alpha
$rgb = (imagecolorat($img,$i,$j) & 0x7F000000) >> 24;
//Como obtenemos un entero, hacemos type casting a string para guardar el canal alpha
//Sabemos de antemano que solo hay unos y ceros en el canal alpha
$chr.= (string)$rgb;
}
}
//Abrimos el archivo reto_sol.hex en modo de escritura
$fp = fopen('reto_sol.hex','w');
//Escribimos el ascii de la cadena de binarios obtenida
fwrite($fp, bin2asc($chr));
fclose($fp);
//Función proporcionada por http://snarkles.net/scripts/sneak/sneak.php
function bin2asc($str) {
$str = explode("\r\n", chunk_split($str, 8));
$ns = '';
for ($n = 0; $n < count($str) - 1; $n++){
$ns .= chr(base_convert($str[$n], 2, 10));
}
return $ns;
}
Observamos la cabecera con un editor hexadecimal
Identificamos la cabecera como un archivo GIF, renombramos y vemos el mismo archivo oculto que paipai y Saurom alcanzaron a ver:
Archivado en: Esteganografía, Retos informáticos |
Haaaa no puede ser, yo llegue a la parte del canal alfa, y ya no supe que hacer jajajaja, pero veo que no estaba tan perdido, muy buen reto XD.
Por cierto, el primer gif tambien se podia ver usando gimp, pero la falta de seguridad hace pensar a uno que puede ser una trampa :(.
Salu2...
Me equivoqué en el nivel del reto , el próximo si lo haces!
Espero que pronto publiques el nuevo reto, ahora si tratare de poner mas atencion XD.
Salu2...
Muy buena la explicacion de como activar el gif animado. Mas elite que como yo lo resolvi. Use un metodo sencillo xD xD
Solo una pequeña aclaracion sobre lo del canal alpha, para que no se lie la gente si lee las especificaciones del formato PNG (http://www.w3.org/TR/PNG/). En tu gran explicacion (como siempre) pones
"los valores que acepta es de 0 (Sin transparencia) hasta 127 (Total transparencia)"
Eso es asi al usar la libreria gd de php que trata con esos valores el canal, pero en las especificaciones del formato PNG es al reves
"Zero alpha represents a completely transparent pixel, maximum alpha represents a completely opaque pixel", es decir, valor 0 total transparencia y el maximo valor sin transparencia
Sísí, había olvidado aclarar que toda mi explicación es basada en la librería gd2 de PHP, porque obviamente los scripts son en PHP.
Otra cosa que se me olvidó explicar fue que al igual que la técnica para ocultar archivos en el bit menos significativo, se tiene que cumplir obligatoriamente la siguiente condición:
anchoImagen*altoImagen >= PesoDelArchivo*8
Gracias por la aclaración Saurom
Muy interesante esto de la esteganografia pero cuando quiero usar los script que nos brindan me genera errores en --> $alpha = (imagecolorat($img,$i,$j) & 0×7f000000) >> 24;
de ese tipo de errores de php -->
Parse error: syntax error, unexpected T_STRING in C:\AppServ\www\alfa.php on line 20
todo esto en el script parar Analizador de imágenes PNG con canales alpha.
Como puedo corregirlo ???? o es que me esta faltando algo hacer algo???
Lo acabo de probar creando un nuevo archivo y copiando el código y no saca ningún error. Si lo haz modificado no te doy ninguna garantía de que funciona. Fíjate que no hayas eliminado por error un punto y coma, casi todas las líneas finalizan con ";".
Si podes publica todo el código yo lo reviso.
muy bueno exelente con demostraciones exactas
Tengo el mismo problema que aldito, lo e probado en varios servers y nada =/
El error se encuentra en esta línea: $rgb = (imagecolorat($img,$i,$j) & 0×7F000000) >> 24;
Y es que al momento de copiar y pegar el texto se copia un carácter que no es el que debería ser, he intentado corregir este problema pero aún no lo logro.
Lo que debes hacer es cambiar el "×" en esa línea por "x", son muy similares pero no son iguales.
La línea quedaría así:
xD...ya Ve mejor Ponga en el post una NOTA: que diga lo de la "x" ya nos ah pasado a muchos Apenas hace unos dias un conocido me pregunto lo mismo y le tuve que explicar....