We’ll show how to inject PHP code into the metadata of JPG files. Next, we’ll see how this code could be executed on a web server. The goal is to demonstrate one of the possible vulnerabilities when a website allows users to upload images.
JPG files may contain additional metadata. One such way of storing metadata is through the Exif standard.
EXIF is short for Exchangeable Image File, a format that is a standard for storing interchange information in digital photography image files using JPEG compression.Exifdata.com
While Exif is meant to store relevant information about the image file, it can be used to store malicious code.
Injecting PHP code to JPG
We’ll use the jhead tool to modify the Exif data of
$ jhead -ce file.jpg
-ce option of
jhead will launch a text editor to edit the comment section of the metadata.
We can then insert the following PHP code:
<?php echo 'Hello, world!'; __halt_compiler(); ?>
__halt_compiler() instruction is used to stop parsing the rest of the JPG file.
If we explore the hexadecimal contents of the file, we’ll be able to see the information added to
Website exploitation with injected code
Even after injecting the PHP code, the file remains a JPG file. It can be rendered inside a website using
<img> tags with no issue. The injected code will not be executed.
The injected code will be executed if
file.jpg is read as a PHP file. The most obvious way this can take place is by storing the file in the server using a php extension. See what happens when we access file.jpg.php.
One way this can happen is by letting users upload files and not verifying the file extension. Then, a valid jpg file with injected code, can be stored in the server as
Faulty server configuration can also lead to files stored as .jpg on the server to be read as .php files.
On Nginx servers, an old exploit allowed rendering https://mysite.com/file.jpg as a .php file by trying to access a non-existent php path: https://mysite.com/file.jpg/file.php. The solution involved configuring Nginx to return a 404 message for non-existent files.
try_files $uri =404;
A more detailed explanation to fix this issue on Nginx servers can be found here.