Perils of Parsing — Pixel Flood Attack on Java ImageIO

Abhisek Datta
Engineering @ Chargebee
4 min readFeb 8, 2021

On a regular day interviewing a candidate for the SSE (Security Engineering) role, he started talking about vulnerabilities in our application. Intrigued by his analysis, we egged on the conversation further and found out that our application is exposed to the vulnerability of triggering an Out Of Memory (OOM) exception. This is possible by making the parser parse an image with fake high resolutions — the infamous Pixel Flood Attack.

As described by the candidate —

I tried a 40K image, it took ~22 Seconds for a request. Then I tried a 50K image, which took ~1.2 minutes. Since it is the production environment, I didn’t risk trying larger sizes than that. But it is always best to throw an error by checking pixel size prior to allocating heap space and processing.

This post is an outcome of how we reproduced and mitigated the vulnerability across modules that use Java’s ImageIO library for parsing images — all the while without causing any performance or functional impact.

Pixel Flood Attack on ImageIO

The whole idea of this attack is to force an image parser to allocate a large volume of memory based on the false parameters on image headers.

The Vulnerable Wedge

To understand this vulnerability, let’s take the JPEG File Interchange Format (JFIF) image format into perspective. It has a well-defined structure split into several segments.

Structure of the JFIF file format is shown below:

Image courtesy Ange Albertini

The Header segment contains meta-information about the image that needs to be conveyed to the image parsing library. This is needed to decompress the image data for visualization.

Many image parsers allocate memory to store the decompressed JPEG data based on dimension values available from the image header.

Here’s a code sample for parsing an image using the Java ImageIO library.

On parsing a malicious JPEG, the above code throws an exception. The stack trace for it is shown below:

Java stacktrace for ImageIO OutOfMemory exception

An Out Of Memory (OOM) exception is triggered when the ImageIO library tries to allocate a 2D array computed from the faulty Height * Width information obtained from the image header. The exception happens while preparing the image for rasterization. Rasterization is computing a model based on the image’s height and width, which in turn is used to allocate a DataBuffer.

https://github.com/AdoptOpenJDK/openjdk-jdk8u/blob/9a91972c76ddda5c1ce28b50ca38cbd8a30b7a72/jdk/src/share/classes/javax/imageio/ImageTypeSpecifier.java#L1070

An attacker can exploit this by uploading a JPEG file having fake high dimension in the header.

Let’s take an example to understand this better. A 10kb image can be used by the attacker and set to have a bogus 10000 x 10000 dimension. When this is uploaded, the parser is exploited to allocate the memory needed for a 10000 x 10000 image. This results in unnecessary high memory allocation on the heap space. Higher the dimension, higher the memory allocation, eventually leading to an OOM exception.

Is OOM Exception Really a Problem?

While the impact of this attack is limited only to memory allocation causing an OOM exception, the risk due to such a vulnerability is high in a multi-tenant environment with SLAs and quality guarantees.

Allocating high memory chunks from heap space for such exploits also leaves less for genuine users, which is indeed a security concern.

Mitigating Pixel Flood Attack on Java ImageIO

As a product, it is critical to ensure that a malicious user of the platform cannot in any way affect or degrade the service offered to other users of the platform.

In general, verification and validation is the first line of defence that needs to be in place before performing any sensitive operation. But how do you validate an input format when the library meant to recognize it is itself affected?

Luckily, while looking for solutions we found an ImageReader abstract class in Java Image IO that seemed to help. It loosely defines an interface for various image format implementations in ImageIO including JPEG, PNG, etc.

From ImageIO documentation —

ImageReader: A class containing static convenience methods for locating ImageReaders and ImageWriters, and performing simple encoding and decoding.

The cherry on top is that it also declares two useful methods that can be used to verify the dimensions before allocating heap memory.

public abstract int getWidth(int imageIndex) throws IOException;
public abstract int getHeight(int imageIndex) throws IOException;

Each of the above image formats implements the two methods, which in turn allows parsing the image header to retrieve its dimension.

Here’s an example of JPEG Image Reader:

https://github.com/AdoptOpenJDK/openjdk-jdk8u/blob/9a91972c76ddda5c1ce28b50ca38cbd8a30b7a72/jdk/src/share/classes/com/sun/imageio/plugins/jpeg/JPEGImageReader.java#L730

Now, Patching the Vulnerability

We leveraged this feature of the ImageIO library to validate image dimensions ahead of parsing. This way there is a check before allocating memory for rendering, typically providing a shield against Pixel Flood Attacks.

An example implementation of this validation is given below:

Our implementation of image dimension validation wrapper over Java ImageIO

So far this approach has been working well for us without any noticeable performance impact or security bypass.

Defense in Depth Approach

Parsing, especially binary parsing is complex and error-prone to the extent that Safe Parsing may be considered a misnomer. Compartmentalisation or sandboxing can significantly reduce the risk of binary format abuse.

Running these procedures as isolated, unprivileged modules using serverless or restricted containers is a long-term scalable solution to shield against such attacks.

We are hiring! Let us know if you are interested in our work and love to solve complex problems in SaaS products, platform & cloud infrastructure engineering.

Review and Edits

  • Vignesh P, Technical Writer
  • Girish Mehta, Documentation Manager

--

--

Abhisek Datta
Engineering @ Chargebee

Security Researcher | Security Engineering | Personal tweets @abh1sek | Workshop https://github.com/abhisek