Skip to content

Injecting executable PHP code to a JPG image file

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 meatadata

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.

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 file.jpg.

$ jhead -ce file.jpg
Sample jpg file (source)

The -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(); ?>

The __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 file.jpg.

JPG metadata
Header information shown using the hexdump tool

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.

Sample jpg with injected PHP code

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 file.jpg.php.

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 as a .php file by trying to access a non-existent php path: 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.

Published inArticles
Notify of
Inline Feedbacks
View all comments