Critical Next.js & libheif RCE Vulnerabilities: Inside the August 2026 AVIF Zero-Day Exploit Chain

The CyberSec Guru

Next.js RCE vulnerability

If you like this post, then please share it:

Buy me A Coffee!

Support The CyberSec Guru’s Mission

🔐 Fuel the cybersecurity crusade by buying me a coffee! Why your support matters: Zero paywalls: Keep the main content 100% free for learners worldwide.

“Your coffee keeps the servers running and the knowledge flowing in our fight against cybercrime.”☕ Support My Work

Buy Me a Coffee Button

In the modern web ecosystem, image optimization is not just a performance enhancement; it is a fundamental requirement for delivering fast, responsive user experiences. Frameworks like Next.js have integrated automated image optimization pipelines to seamlessly convert, resize, and compress modern formats like AVIF, WebP, and HEIC on the fly. However, this convenience relies on a deep, complex dependency tree of low-level C and C++ media parsing libraries. In August 2026, the intersection of web framework architecture and low-level memory management resulted in a catastrophic security wake-up call.

Security researchers and the Vercel engineering team disclosed a series of critical vulnerabilities, most notably a severe heap buffer overflow in the libheif library that directly leads to Unauthenticated Remote Code Execution (RCE) within the Next.js Image Optimization API. This vulnerability chain, tracked as GHSA-g89c-p67h-r497 in libheif and GHSA-2xp9-vwfh-vxw4 in Next.js, highlights the extreme danger of memory-unsafe media parsers operating in server-side rendering environments. This article provides an exhaustive, expert-level breakdown of the vulnerability mechanics, the manipulation of the ISOBMFF container graph, and the precise remediation steps required to secure your infrastructure.

The August 2026 Security Wake-Up Call

The August 2026 security release for Next.js and its upstream dependencies addresses multiple critical severity flaws that allow unauthenticated attackers to execute arbitrary code on production servers. The primary vector of attack involves the Next.js Image Optimization API (/_next/image), which processes user-supplied image URLs. By crafting a malicious AVIF or HEIC file with a specifically manipulated ISOBMFF (ISO Base Media File Format) container graph, an attacker can trigger a heap buffer overflow in the underlying libheif library used by the sharp image processing engine.

Because libheif is written in C++, this memory corruption flaw bypasses the safety guarantees of the Node.js JavaScript runtime, granting the attacker native code execution capabilities with the privileges of the web server process. In response to the severity of the upstream libheif flaw, the Next.js security team made the unprecedented decision to temporarily disable AVIF optimization in patched releases until a fix can fully propagate through the open-source supply chain. Additionally, a separate critical flaw (CVE-2026-75604) was patched, which affects Next.js deployments hosted on Windows filesystems.

The Next.js Attack Surface: Unpacking the August 2026 Security Release

To understand the blast radius of these vulnerabilities, one must first understand how Next.js handles media. When a client requests an optimized image via the next/image component, the Next.js server intercepts the request, fetches the original asset, and passes it through the sharp library (which utilizes libvips and libheif under the hood) to generate a resized, modern-format derivative. This entire process occurs server-side, making the image optimization endpoint a highly attractive target for threat actors.

Unauthenticated RCE in Image Optimization API (GHSA-2xp9-vwfh-vxw4)

The most severe issue addressed in the August 2026 release is an Unauthenticated Remote Code Execution vulnerability triggered specifically when processing AVIF files. AVIF (AV1 Image File Format) is based on the HEIF container standard, meaning it relies on libheif for decoding the underlying HEVC (H.265) or AV1 bitstreams and managing the complex item graph that defines the image’s color and alpha channels.

The vulnerability exists in how libheif handles nested identity-derivation (iden) and auxiliary (auxl) item references. When a maliciously crafted AVIF file is submitted to the Next.js image optimizer, the libheif parser enters a state where it allocates insufficient memory for an image plane but subsequently writes high-bit-depth pixel data into that undersized buffer. This results in a massive heap buffer overflow. Because this occurs during the automated optimization of an attacker-controlled URL, no authentication or prior access to the server is required. An attacker simply needs to host the malicious AVIF file on an external server and trick the Next.js application into optimizing it, instantly compromising the host.

Windows Filesystem RCE (CVE-2026-75604 / GHSA-p293-qw3h-jr36)

While the AVIF vulnerability dominates the headlines, the August 2026 release also patched a critical flaw specific to Windows-hosted Next.js servers. Tracked as CVE-2026-75604, this vulnerability affects applications utilizing both the Pages Router and the App Router without Cache Components.

In specific configurations on Windows filesystems, improper sanitization of pathing and caching mechanisms can lead to unauthenticated remote code execution. Unlike the libheif flaw, which is a memory corruption issue in a C++ library, this vulnerability is deeply tied to how Node.js interacts with the Windows NTFS filesystem and handles file resolution in the Next.js routing layer. Linux and macOS environments are inherently immune to this specific pathing flaw due to differences in POSIX filesystem semantics. There is no known workaround for affected Windows-hosted applications other than immediately applying the patched Next.js versions.

Deep Dive: The libheif Heap Buffer Overflow (GHSA-g89c-p67h-r497)

To truly grasp the severity of this exploit, we must descend from the JavaScript framework layer into the unforgiving world of C++ memory management. The vulnerability in libheif (affecting versions <= v1.23.1 and patched in v1.23.2) is not the result of a simple off-by-one error or a naive buffer copy. Instead, it is a sophisticated “chain of four” individually benign behaviors that, when combined via a malformed ISOBMFF container, produce a controlled, attacker-dictated heap buffer overflow.

📬 Stay Ahead of Cyber Threats

Get the latest cybersecurity news, critical vulnerabilities, threat intelligence, tutorials, and exclusive giveaways delivered straight to your inbox. No spam. Unsubscribe anytime.

Subscribe to the Newsletter →

The Anatomy of HEIC/AVIF and the ISOBMFF Container

Unlike legacy raster formats such as JPEG or PNG, which consist of a simple header followed by a contiguous stream of compressed pixel data, HEIC and AVIF are built upon the ISO Base Media File Format (ISOBMFF). This is the same container format used for MP4 video files. In ISOBMFF, an image is not a single entity but a complex graph of “items” (tiles, color planes, alpha planes, metadata) linked together by reference boxes (iref).

An image might consist of a primary color item, an auxiliary alpha item, and various derivation items (iden) that instruct the parser on how to assemble the final visual output. This graph-based architecture allows for incredible flexibility, such as image grids, overlays, and identity derivations, but it also vastly expands the attack surface. Parsers must recursively traverse this graph, allocate memory for each node, and merge the results. It is within this recursive merging process that the libheif vulnerability resides.

The Chain of Four: How Benign Behaviors Create a Critical Flaw

The heap buffer overflow in HeifPixelImage::scale_nearest_neighbor() is triggered by a precise sequence of logical oversights in the libheif codebase. Each step, in isolation, might be considered a minor code smell or a missing edge-case check. Together, they form a devastating exploit chain.

Step 1: The Missing Uniqueness Check in transfer_channel_from_image_as()

The first link in the chain involves how libheif moves image planes (channels) between internal structures. The method HeifPixelImage::transfer_channel_from_image_as() is responsible for taking a source plane, changing its designated channel type (e.g., marking it as an Alpha channel), and appending it to the destination image’s internal storage vector (m_storage).

// TODO: check that dst_channel does not exist yet // line 1037
...
plane.m_channel = dst_channel;
m_storage.push_back(plane); // line 1085 — unconditional

As the source code’s own TODO comment explicitly admits, the function fails to verify whether the destination channel already exists. It unconditionally pushes the new plane into the std::vector. Consequently, an image can end up with multiple distinct Alpha planes stored in its m_storage array, violating the implicit assumption that an image possesses only one plane per channel type.

Step 2: The Blind Spot in find_storage_for_channel()

When other methods need to query the properties of an image’s Alpha channel—such as its bit depth, width, or memory allocation—they rely on find_storage_for_channel(). This function iterates through m_storage and returns the first ComponentStorage entry that matches the requested channel type.

Because transfer_channel_from_image_as() allowed duplicate Alpha planes to exist, find_storage_for_channel() becomes blind to any subsequent duplicates. If an image has an 8-bit Alpha plane followed by a 10-bit Alpha plane, all dependent metadata methods (get_bits_per_pixel(), get_channel_memory(), get_width()) will exclusively describe the first 8-bit plane. The existence and properties of the 10-bit plane are effectively hidden from the allocator, creating a severe state desynchronization.

Step 3: Type Confusion and Underallocation in scale_nearest_neighbor()

The core memory corruption occurs when the image must be resized, invoking scale_nearest_neighbor(). The scaler must allocate a destination buffer for the resized Alpha plane. It queries the bit depth using the blind metadata function described above:

if (has_channel(heif_channel_Alpha)) {
out_img->add_channel(heif_channel_Alpha, width, height,
get_bits_per_pixel(heif_channel_Alpha), limits); // → uses first Alpha's 8-bit depth
}

Because get_bits_per_pixel() only sees the first Alpha plane (which is 8-bit), the scaler allocates a buffer assuming 1 byte per pixel. For a 128×128 image, this results in a calloc allocation of exactly 16,384 bytes.

However, the scaling loop iterates over every entry in the source m_storage vector. When the loop encounters the hidden, duplicate 10-bit Alpha plane, it enters the High Dynamic Range (HDR) planar branch. This branch casts the destination buffer pointer to a 16-bit integer pointer (uint16_t*) based on the source component’s bit depth, completely ignoring the fact that the destination buffer was allocated for 8-bit data.

// line 1949-1965 — HDR planar branch
uint16_t* out_data = out_img->get_channel_memory<uint16_t>(channel, &out_stride);
out_stride /= 2;
for (uint32_t y = 0; y < out_h; y++) {
...
out_data[y * out_stride + x] = in_data[iy * in_stride + ix]; // 2-byte write into 1-byte alloc
}

The loop writes 2 bytes per output sample into a 1-byte-per-sample allocation. Writing a 128×128 10-bit plane into the 8-bit buffer results in 32,768 bytes being written into a 16,384-byte allocation. This produces a massive, attacker-controlled heap buffer overflow of approximately 16KB. The dimensions of the output geometry control the size of the overflow, while the HEVC bitstream content controls the exact uint16_t values written into the adjacent heap memory.

Step 4: The iden Derivation Enabler and Dimension Bypass

To trigger this specific state, an attacker must force the parser to create an image with two Alpha planes of differing bit depths, and then force the scaler to run. This is achieved using the iden (Identity Derivation) item.

ImageItem_iden::decode_compressed_image() decodes a referenced item, which may already include its own attached Alpha channel. Following this, the base class ImageItem::decode_image() executes the iden item’s own alpha attachment logic, appending a second Alpha plane via the flawed transfer_channel_from_image_as() function. This successfully creates the dual-plane state.

Furthermore, ImageItem_iden::check_decoded_image_size() contains a logic flaw where it unconditionally returns Error::Ok. This allows the iden item’s declared ispe (image spatial extents) dimensions to completely mismatch the referenced item’s actual pixel dimensions. This dimension mismatch is the final key: it forces the parent item to invoke scale_nearest_neighbor() to reconcile the size difference, triggering the catastrophic underallocation and subsequent overwrite.

The Attack Flow: Manipulating the ISOBMFF Graph

To weaponize this vulnerability, an attacker constructs a malicious ISOBMFF container containing five specific items designed to traverse the exact code path outlined above.

  1. Item 1 (hvc1, 64×64, 8-bit): The base color image. It has an auxiliary Alpha reference to Item 2.
  2. Item 2 (hvc1, 64×64, 8-bit): The first Alpha plane (8-bit).
  3. Item 3 (iden, 64×64 ispe): An Identity Derivation item pointing to Item 1. It acts as the alpha auxiliary for Item 5 and possesses its own alpha reference to Item 4.
  4. Item 4 (hvc1, 64×64, 10-bit): The second Alpha plane (10-bit).
  5. Item 5 (hvc1, 128×128, 8-bit): The Primary Item, which uses Item 3 as its Alpha channel.

The Decode Sequence:
When the Next.js server attempts to decode the Primary Item (Item 5), it requests its Alpha channel (Item 3). To resolve Item 3, the parser decodes the referenced Item 1. Item 1 decodes to a 64×64 YCbCr image and attaches its 8-bit Alpha (Item 2) as Alpha #1. Control returns to Item 3, which then decodes its own 10-bit Alpha (Item 4) and appends it as Alpha #2.

Item 3 now possesses an internal m_storage array containing [Y, Cb, Cr, Alpha(8-bit), Alpha(10-bit)]. Because Item 3’s declared ispe (64×64) does not match Item 5’s required dimensions (128×128), the scaler is invoked. The scaler queries the bit depth, sees the 8-bit Alpha #1, allocates 16KB, and then iterates through the storage. When it hits Alpha #2 (10-bit), it executes the HDR branch, casting the buffer to uint16_t* and writing 32KB of data, violently overwriting the adjacent heap memory and achieving arbitrary memory corruption.

Proof of Concept: Reproducing the Heap Overflow

Security researchers have developed a robust Python-based Proof of Concept (PoC) that reliably generates the malformed ISOBMFF container required to trigger the crash. Reproducing this vulnerability requires compiling libheif with AddressSanitizer (ASan) to detect the memory violation.

CVE-2026-75604 PoC
CVE-2026-75604 PoC

Step 1: Building libheif with AddressSanitizer (ASan)

To observe the heap corruption safely, the library must be compiled with Clang’s AddressSanitizer, which instruments memory allocations and detects out-of-bounds writes in real-time.

git clone https://github.com/strukturag/libheif
cd libheif
cmake -B build \
-DCMAKE_C_COMPILER=clang \
-DCMAKE_CXX_COMPILER=clang++ \
-DCMAKE_C_FLAGS="-fsanitize=address -fno-omit-frame-pointer -g" \
-DCMAKE_CXX_FLAGS="-fsanitize=address -fno-omit-frame-pointer -g" \
-DCMAKE_EXE_LINKER_FLAGS="-fsanitize=address" \
-DCMAKE_SHARED_LINKER_FLAGS="-fsanitize=address" \
-DWITH_EXAMPLES=ON -DWITH_GDK_PIXBUF=OFF
cmake --build build --target heif-dec -j$(nproc)

Step 2: Generating the Malicious ISOBMFF Payload

The following Python script utilizes ffmpeg to generate raw HEVC bitstreams at varying bit depths, parses the NAL units, and manually constructs the malicious ISOBMFF boxes (ftyp, mdat, meta, iinf, iref, iprp, iloc) to establish the trap graph.

#!/usr/bin/env python3
"""
PoC generator for libheif heap-buffer-overflow via duplicate Alpha planes.
Constructs a HEIC file whose nested iden/auxl item graph causes
scale_nearest_neighbor() to write 16-bit samples into an 8-bit Alpha
plane allocation.
Prerequisites: Python 3.6+, ffmpeg with libx265.
"""
import struct, subprocess, tempfile, os, io
def _generate_hevc(width, height, pix_fmt):
fd, path = tempfile.mkstemp(suffix=".265")
os.close(fd)
try:
subprocess.run([
"ffmpeg", "-y", "-f", "lavfi", "-i", f"color=c=gray:s={width}x{height}",
"-frames:v", "1", "-c:v", "libx265", "-x265-params", "log-level=0",
"-pix_fmt", pix_fmt, "-f", "hevc", path,
], capture_output=True, check=True)
with open(path, "rb") as f: return f.read()
finally: os.unlink(path)
# [NAL parsing and box building functions omitted for brevity, see full source]
# ...
def build_poc():
print("[*] Generating HEVC bitstreams ...")
raw_8_64 = _generate_hevc(64, 64, "yuv420p")
raw_10_64 = _generate_hevc(64, 64, "yuv420p10le")
raw_8_128 = _generate_hevc(128, 128, "yuv420p")
# Logic to parse NALUs, construct hvcC boxes, and wire the iref graph
# linking Item 5 -> Item 3 (iden) -> Item 1, with mismatched alpha depths
# ...
return ftyp + mdat + meta
if __name__ == "__main__":
with open("poc.heic", "wb") as f:
f.write(build_poc())
print("[+] Written poc.heic")

Step 3: Triggering the Out-of-Bounds Write

Executing the compiled heif-dec binary against the generated poc.heic file yields a definitive ASan crash report, confirming the heap buffer overflow.

ASAN_OPTIONS=detect_leaks=0 ./build/examples/heif-dec poc.heic /dev/null

ASan Output Analysis:

==PID==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x... at pc 0x...
WRITE of size 2 at 0x... thread T0
#0 ... in HeifPixelImage::scale_nearest_neighbor(...) pixelimage.cc:1964
#1 ... in ImageItem::decode_image(...) image_item.cc:1070
#2 ... in HeifContext::decode_image(...) context.cc:1443
#3 ... in heif_decode_image heif_decoding.cc:258
0x... is located 0 bytes after 16399-byte region [0x...,0x...)
allocated by thread T0 here:
#0 ... in calloc
#1 ... in HeifPixelImage::ComponentStorage::alloc(...) pixelimage.cc:478
#2 ... in HeifPixelImage::add_channel(...) pixelimage.cc:379
#3 ... in HeifPixelImage::scale_nearest_neighbor(...) pixelimage.cc:1849

The report explicitly confirms that a calloc allocation of ~16KB was immediately followed by a 2-byte write (WRITE of size 2) originating from the HDR planar branch of the scaler, perfectly matching the theoretical exploit model.

From Heap Overflow to Remote Code Execution (RCE)

A common misconception among junior developers is that a “crash” or “memory leak” is merely a denial-of-service (DoS) condition. In the context of C and C++ server-side applications, a heap buffer overflow is the foundational primitive for Remote Code Execution.

When libheif writes 32KB of data into a 16KB allocation, it does not simply stop at the boundary. It overwrites the heap metadata of adjacent memory chunks (chunk header corruption) or directly overwrites adjacent C++ objects residing in the heap. Modern allocators like jemalloc or TCMalloc store critical metadata adjacent to user data. By carefully crafting the HEVC 10-bit sample values (the uint16_t payload), an attacker can overwrite function pointers, C++ virtual tables (vtables), or internal std::string pointers located in the overwritten memory region.

When the Next.js server subsequently attempts to process, free, or invoke methods on those corrupted adjacent objects, the CPU’s instruction pointer is redirected to an attacker-controlled memory address. Because the Next.js Image Optimization API runs within the main Node.js process (via N-API bindings to C++ libraries), the arbitrary code executes with the full privileges of the web server. In a containerized cloud environment, this typically results in complete cluster compromise, data exfiltration, and lateral movement. The researchers who disclosed this vulnerability confirmed successful RCE across multiple production applications utilizing the affected library stack.

Supply Chain Security: The Hidden Cost of Media Parsing

The August 2026 Next.js and libheif incident is a textbook example of modern supply chain risk. Next.js is a JavaScript framework, yet its most critical vulnerability resides in a C++ media parsing library three layers deep in its dependency tree (Next.js -> sharp -> libvips / libheif).

Media parsing libraries are historically the most vulnerable components in any software stack. Formats like ISOBMFF, TIFF, and PDF are incredibly complex, featuring decades of legacy backward-compatibility requirements, recursive data structures, and flexible memory layouts. Fuzzing these parsers is notoriously difficult due to the sheer entropy required to generate valid, deeply nested container graphs that bypass initial sanity checks.

This incident also reignites the industry debate regarding memory safety. While Rust-based image processing crates (such as the image crate) are gaining traction due to their compile-time guarantees against buffer overflows and type confusion, the performance requirements of enterprise server-side rendering still heavily favor highly optimized C/C++ libraries like libvips and libheif. Until memory-safe languages achieve parity in media decoding performance and ecosystem maturity, web frameworks will remain perpetually tethered to the memory safety practices of upstream C++ maintainers.

Actionable Remediation and Mitigation Checklist

To secure your infrastructure against the August 2026 Next.js and libheif vulnerabilities, DevOps and Security Engineering teams must execute the following remediation steps immediately:

  1. Patch Next.js Immediately: Update all Next.js applications to the patched LTS versions. This is the primary defense against the Image Optimization API exploit and the Windows filesystem RCE.# For Active LTS npm install next@16.3.3 # For Maintenance LTS npm install next@15.5.24
  2. Acknowledge AVIF Optimization Disablement: Be aware that the patched Next.js releases temporarily disable AVIF optimization. If your application relies heavily on AVIF delivery, monitor the Vercel and sharp GitHub repositories for the re-enablement of the feature once the upstream libheif fix is fully integrated.
  3. Update Upstream libheif: If your organization compiles its own media processing pipelines, uses libheif directly in backend services, or maintains custom Docker images for image manipulation, ensure libheif is updated to v1.23.2 or later.
  4. Audit Custom Image Endpoints: If your application features custom, non-Next.js image upload or processing endpoints that accept HEIC/AVIF files, ensure the underlying C++ libraries are patched. Do not rely solely on framework-level patches if you have bespoke media pipelines.
  5. Implement WAF Heuristics: While difficult to block entirely at the edge, Web Application Firewalls (WAF) can be configured to flag anomalous ISOBMFF headers, specifically looking for excessive iden derivation chains or mismatched ispe spatial extents in uploaded media payloads.

Conclusion

The August 2026 disclosure of the libheif heap buffer overflow and the subsequent Next.js RCE vulnerabilities serves as a stark reminder of the fragility of the modern web supply chain. A missing TODO comment in a C++ vector push operation, combined with the immense complexity of the ISOBMFF container specification, was all it took to expose millions of web applications to unauthenticated remote code execution.

As web frameworks continue to push the boundaries of server-side rendering and automated asset optimization, the attack surface inevitably shifts downward into the native libraries that power these features. Security cannot be treated as a framework-level afterthought; it requires rigorous auditing, memory-safe architecture transitions, and aggressive patch management across the entire dependency graph. By understanding the deep technical mechanics of exploits like GHSA-g89c-p67h-r497, engineering teams can better anticipate, detect, and neutralize the next generation of supply chain zero-days.

Buy me A Coffee!

Support The CyberSec Guru’s Mission

🔐 Fuel the cybersecurity crusade by buying me a coffee! Your contribution powers free tutorials, hands-on labs, and security resources.

Why your support matters:
  • Writeup Access: Get complete writeup access within 12 hours
  • Zero paywalls: Keep the main content 100% free for learners worldwide

Perks for one-time supporters:
☕️ $5: Shoutout in Buy Me a Coffee
🛡️ $8: Fast-track Access to Live Webinars
💻 $10: Vote on future tutorial topics + exclusive AMA access

“Your coffee keeps the servers running and the knowledge flowing in our fight against cybercrime.”☕ Support My Work

Buy Me a Coffee Button

If you like this post, then please share it:

News

Discover more from The CyberSec Guru

Subscribe to get the latest posts sent to your email!

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Discover more from The CyberSec Guru

Subscribe now to keep reading and get access to the full archive.

Continue reading