Your Web News in One Place

Help Webnuz

Referal links:

Sign up for GreenGeeks web hosting
September 6, 2020 08:39 am GMT

This Image Is Also a Valid Javascript File

Images are usually stored as binary files, while a Javascript file is basically just text. Both have to follow their own rules: Images have a concrete file format, encoding the data in a certain way. Javascript files have to follow a specific syntax in order to be executable. I wondered: Can I create an image file that has valid Javascript syntax, so it is also executable?

Before you read on, I highly recommend checking out this code sandbox with the result of my experimentation:

https://codesandbox.io/s/executable-gif-8yq0j?file=/index.html

If you want to check out the image and inspect it yourself, you can download it here:

https://executable-gif.glitch.me/image.gif

Choosing the Right Image Type

Unfortunately, images contain a lot of binary data which will throw an error if interpreted as Javascript. So my first idea was: What if we just put all the image data in a large comment, like this:

/*ALL OF THE BINARY IMAGE DATA*/

That would be a valid Javascript file. However, image files need to start with a specific sequence of bytes; a file header that is specific to the image format. PNG files for example always have to start with the byte sequence 89 50 4E 47 0D 0A 1A 0A. If an image would start with /*, it would not be a valid image file anymore.

This file header lead to the next idea: What if we could use this byte sequence as variable name and have a huge string assignment like this:

PNG=`ALL OF THE BINARY IMAGE DATA`;

We are using template strings instead of the normal " or ' strings because the binary data could contain linebreaks and template strings are better at dealing with those.

Unfortunately, most byte sequences for image file headers contain unprintable characters that are not allowed in variable names. But there is one image format we can use: GIF. The GIF header block is 47 49 46 38 39 61, which conveniently spells GIF89a in ASCII, a perfectly legal variable name!

Choosing the Right Image Dimensions

Now that we found an image format that starts with a valid variable name, we need to add the equals and backtick characters. The next four bytes of the file are therefore: 3D 09 60 04

First bytes of the image

In the gif format, the four bytes following the header specify the dimensions of the image. We have to fit 3D (the equals sign) and 60 (the backtick that opens the string) in there. GIF uses little endian so the second and fourth character have a huge influence on image dimensions. We want to keep them as small as possible to not end up with an image that is tens of thousands pixels wide. Therefore we store the big bytes 3D and 60 in the least significant bytes.

The second byte of the image width needs to be a valid whitespace character, because this would be the space between the equals sign and the beginning of the string GIF89a= `.... Keep in mind that the hexcode of the characters should be as small as possible, or the image would be huge.

The smallest whitespace character is 09, the horizontal tab. This gives us an image width of 3D 09, which in little endian translates to 2365; a bit wider than I would have liked, but still reasonable.

For the second height byte, we can just choose something that produces a nice aspect ratio. I chose 04, which produces a height of 60 04, or 1120.

Getting our own script in there

Right now, our executable gif does not really do anything. It just assigns a large string to the global variable GIF89a. We want something interesting to happen! Most of the data inside the GIF is for encoding the image, so if we try to add Javascript in there, we would probably end up with a very corrupted image. But for some reason, the GIF format contains something called a Comment Extension. This is a place to store some metadata that will not be interpreted by the GIF decoder - a perfect place for our Javascript logic.

This comment extension comes right after the GIF color table. Since we can put any content in there, we can easily close the GIF89a string, add all our Javascript and then start a multiline comment block, so the rest of the image does not interfere with the Javascript parser.

All in all, our file could then look like this:

GIF89a= ` BINARY COLOR TABLE DATA ... COMMENT BLOCK:`;alert("Javascript!");/*REST OF THE IMAGE */

There is a small restriction: While the comment block itself can be of any size, it is composed of multiple sub-blocks, each of which has a maximum size of 255. Between the subblocks is a single byte that indicates the length of the next subblock. So in order to fit a larger script in there, it has to be divided into smaller chunks, like this:

alert('Javascript');/*0x4A*/console.log('another subblock');/*0x1F*/...

The hexcodes in the comments are the bytes that indicate the size of the next subblock. They are irrelevant for the Javascript, but required for the GIF file format. In order to prevent them from interfering with the rest of the code, they have to be in comments. I wrote a small script that processes the script chunks and adds them to the image file: https://gist.github.com/SebastianStamm/c2433819cb9e2e5af84df0904aa43cb8

Cleaning up the Binary

Now that we have the basic structure down, we need to make sure that the binary image data does not ruin our syntax. As explained in the previous section, the file has three sections: The first one is an assignment to the variable GIF89a, the second one is the Javascript code and the third one is a multiline comment.

Let's have a look at the first part, the variable assignment:

GIF89a= ` BINARY DATA `;

If the binary data would contain the character ` or the character combination ${ we are in trouble because this would either end the template string or produce an invalid expression. The fix here is quite easy: Just change the binary data! E.g. instead of using the ` character (hexcode 60), we could use the character a (hexcode 61). Since this part of the file contains the color table, it would result in some of the colors being slightly off, e.g. using the color #286148 instead of #286048. It is unlikely that someone will notice the difference.

Fighting the corruption

At the end of the Javascript code, we opened a multiline comment in order to make sure the binary image data does not interfere with the Javascript parsing:

alert("Script done");/*BINARY IMAGE DATA ...

If the image data would contain the character sequence */, the comment would end prematurely, which would result in an invalid Javascript file. Here again we can manually change one of the two characters so they no longer end the comment. However, since we are now in the encoded image section, this will result in a corrupted image, like this:

Corrupted Image

In extreme cases the image could not be displayed at all. By carefully choosing which bit to flip I was able to minimize the corruption. Fortunately there were only a handful of instances of the harmful */ combination to deal with. There is still a bit of corruption visible in the final image, e.g. at the bottom of the "Valid Javascript File" string, but overall I am pretty happy with the result.

Ending the File

The last operation we have to perform is at the end of the file. The file has to end with the bytes 00 3B. So we have to end our comment earlier. Since this is the end of the file and any potential corruption would not be very visible, I just ended the multi block comment and added a single line comment so that the end of the file would not cause any problems when parsing:

/* BINARY DATA*/// 00 3B

Convincing the Browser to Execute an Image

Now, after all this, we finally have a file that is both an image as well as a valid Javascript file. But there is one last challenge we have to overcome: If we upload the image to a server and try to use it in a script tag, we would most likely see an error like this:

Refused to execute script from 'http://localhost:8080/image.gif' because its MIME type ('image/gif') is not executable.

So the browser rightfully says "That's an image! I am not going to execute that!". Which in most cases is a good mindset to have. But we want to execute it anyway. Our solution here is to just not tell the browser that it's an image. For that I wrote a small server that serves the image without any header information.

Without the MIME type information from the header, the browser does not know that it is an image and just does what fits the context best: Display it as image in an <img> tag, or execute it as Javascript in a <script> tag.

But ... why?

That is something I have yet to figure out. It was a nice mental challenge to make this stuff work, but if you can think of any scenarios where this might actually be useful, please let me know!


Original Link: https://dev.to/sebastianstamm/this-image-is-also-a-valid-javascript-file-5fol

Share this article:    Share on Facebook
View Full Article

Dev To

An online community for sharing and discovering great ideas, having debates, and making friends

More About this Source Visit Dev To