Los Caballeros

"Sobre seguridad, programación, modding, frikismo, etc... "

HTML5 (Canvas) + JS + Esteganografía + Cross-Domain

Twitter icon Twitter icon
Se me ocurrió en uno de esos #MomentosEureka xD

Entre las cosas que HTML5 integra se encuentra el elemento canvas el cual nos permite trabajar a "bajo nivel" con imágenes, entonces se me ocurrió si podemos leer los valores de los pixeles de las imágenes usando canvas y JS, y ademas tenemos que img no esta limitado por dominio por lo que se puede usar para cross-domain...

Entonces se me ocurrió cambiar pixeles de una imagen (o crear una imagen nueva) colocando char por char dentro del mensaje, podría colocar hasta 3 chars por pixel pero si la cosa es que no se note el mensaje o insertar en una imagen sin que esta se altere mucho entonces que sea 1 char por pixel ;)...

entonces tenemos que un pixel esta formado por 3 valores (RGB = Rojo, Verde, Azul) por lo que se pueden meter 3 valores pero por lo que explique arriba no tiene mucho caso :P

Entonces una forma divertida de hacerlo sin alterar tanto la imagen sería:
RGB = (Ascii del char, 0, ascii del char) el por que pase el verde a 0 mas que nada es para no confundir pixeles de la imagen con pixeles del mensaje, obviamente pueden haber otros pixeles de la imagen con esa configuración por lo que no sería mas problema que cambiar el verde de 0 a 1 (lo cual no es un tono que se pueda reconocer a simple vista :P).

<?php
/*
By Xianur0
http://hackingtelevision.blogspot.com
xianur0.null [at] gmail.com
*/
$file = "imagen.jpg"; /*El por que estoy insertando el mensaje dentro de una imagen existente en lugar de crear una? por que es divertido y menos sospechoso para muchas cosas xD. Ah recomiendo usar imágenes mas o menos grandes para que los pixeles editados no se noten mucho o para poder enviar mensajes mas grandes ;) */
$mensaje = "xianur0 was here";
function dibujarpixel($im,$x,$y,$color) {
$color = imagecolorallocate($im, $color[0],$color[1],$color[2]);
imagefilledrectangle($im, $x, $y, $x, $y, $color);
}
list($ancho, $alto, $tipo, $atributos) = getimagesize($file);
$pixeles = $ancho*$alto;
if(strlen($mensaje) > $pixeles) die("Imagen demasiado pequeña (o mensaje demasiado grande)!");
$proporcion = intval(($pixeles/strlen($mensaje)));
$im = "";
preg_match("/(\w+)$/",$file,$match);
switch(strtolower($match[1])){
case 'jpg':
case 'jpeg':
$im = imagecreatefromjpeg($file);
break;
case 'png':
$im = imagecreatefrompng($file);
break;
}
for($x=0;$x<$ancho;$x++){
for($y=0;$y<$alto;$y++){
$pixel = imagecolorat($im, $x, $y);
$colors = imagecolorsforindex($im,$pixel);
$r = $colors["red"];
$g = $colors["green"];
$b = $colors["blue"];
if($r == $b && $g == 0) { // Si el pixel puede interferir lo cambiamos un poco :P
dibujarpixel($im,$x,$y,array($r,1,$b));
}
}
}
$p = 0;
for ($i = 0; $i < strlen($mensaje); $i++) {
$y = intval($p/$ancho);
$x = intval($p-($y*$ancho));
dibujarpixel($im,$x,$y,array(ord($mensaje{$i}), 0, ord($mensaje{$i}))); // cambiamos este pixel por (ascii del char, 0, ascii del char) = (r,g,b)
$p += $proporcion;
}
header('Content-type: image/png');
imagepng($im); // Imprimimos esta imagen editada como png :P
imagedestroy($im);
?>


y ahora para leer dichos datos desde JS + Canvas:

<html>
<head>
<title>PoC Cross-Domain by Xianur0</title>
<script language="javascript">
function cargaContextoCanvas(idCanvas){
var elemento = document.getElementById(idCanvas);
if(elemento && elemento.getContext){
var contexto = elemento.getContext('2d');
if(contexto){
return contexto;
}
}
return FALSE;
}
window.onload = function(){
var ctx = cargaContextoCanvas('micanvas');
var cadena = "";
if(ctx){
var img = new Image();
img.src = 'png.php';
img.onload = function(){
ctx.drawImage(img, 0, 0);
var ele = document.getElementById('micanvas');
var canvasData = ctx.getImageData(0, 0, ele.width, ele.height);
for (var idx = 0; idx < (ele.width*ele.height)*4; idx+=4) {
var r = canvasData.data[idx + 0];
var g = canvasData.data[idx + 1];
var b = canvasData.data[idx + 2];
var a = canvasData.data[idx + 3];
if(r == b && g == 0)
cadena += String.fromCharCode(b);
}
alert(cadena);
}
}
}

</script>
</head>
<body>
<canvas id="micanvas" width="900" height="900">
Tu navegador no soporta canvas.
</canvas>
<script>
</script>
</body>
</html>


Como verán es algo bastante simple, mientras el carácter que queramos enviar no pase de 255 (ascii) no debería de haber problema :P.

Como dije arriba esta forma se puede usar para estenografía o bien para pasar datos de un dominio a otro (el same origen policy da dolor de cabeza a muchos desarrolladores web por lo que esta sería una opción bastante viable en muchos casos :P).

Es una forma muy simplona pero solo es un PoC xD...

Saludos!