Hiding JS in a JPEG header.

Santosh Bhandari
5 min readDec 27, 2017

--

It’s been a long time since I opened an account on Medium. Today , I’m going to share an intuitive idea on how we can hide a JavaScript in a JPEG header whilst being a valid JPEG image. Below is an image of Aaron Swartz (An Internet Activist)

Aaron Swartz

Lets try to embed this image as a script.

While opening it in a browser , browser throws an error indicating that JS contains illegal characters.

It is for sure that the image we have used can not be treated as a valid JavaScript. Now , lets write a valid JS and change the extension to JPEG.

Now , an alert box pops up in the browser as this is a valid JS.

But it’s not a valid image.

Before diving into the main parts, lets dissect the JPEG file format of the above image of Aaron Swartz.
Below is a screenshot of the image when opened through a command line app called hexdump .

The first two bytes ,FF D8 , represent the start of an image (SOI) ,the next two bytes , FF E0 , represent that the coming two bytes , 00 10, represent the length of a JPEG header. 00 10 is a hex equivalent of decimal number 16, which means that the length of JPEG header is 16 bytes counting themselves.

FF DB in the screenshot represents the start of a quantization table , the explanation of which is beyond the scope of this write-up . We will hide our JS, alert(1), in between FF E0 and FF DB.
We are using hexeditor to inject our JS code in between FF E0 and FF DB. Note : There can be multiple FF DB ; multiple quantization tables . But we have to inject our code in between FF E0 and first FF DB.
When we blindly try to inject the code using (hexeditor -b aaron.jpg), it throws an error in browsers console as it is still not a valid JS.

So , let’s comment out the garbage using /* and */ . Everything written in between /* */ in js is happily ignored by the JS engine.

We have used 2F 2A which is a hex representation of /* to start the comment to ignore the garbage , but 2F 2A in decimal equals to 12074 bytes .However , we have 16 bytes in between 2F 2A and FF DB inclusive.
The first four bytes FF D8 FF E0 make a valid non-ASCII variable name and we have injected /* as a start of comment . Now , we have to close out the comment using */ ,use an equals to operator “ = “ and use our payload alert(1); and again start another comment /* .

Since we have 16 bytes in between 2F 2A and FF DB inclusive ,we have to pad out remaining with null bytes i.e. 12074- 16- 14 i.e 12044.

Let’s write a simple JS which injects 12044 number of null bytes , our payload and saves the final output as an image.

So , this script reads the JPEG file , converts the buffer into hex , inserts our payload in between 2F 2A and FF DB , changes the final hex into buffer , and writes the buffer into a file named newaaron.jpg .
Now , let’s open hexeditor to insert the end of comment before the end of image . The last bytes of JPEG file is FF D9. We will insert 2A 2F which is a hex equivalent of */ before FF D9 using hexeditor .

Now , let’s open it in a browser.

Congo , we have an alert box.

Lets view the source.

It’s a valid image as well.

We can see the script through a debugger tab.

As you can see, we have ÿØÿà/*JFIF ……. */ = alert(1);/* …….. /*
This made a valid JPEG image as well as a valid script .

Note : As of Firefox 51 , this is fixed but you can verify it in your browser.
Suggestions and criticisms are appreciated.

Any questions ? Contact me at facebook.com/MRcodedbrain

--

--