Disclaimer: This is NOT a zero-day. This is a learning experience from my journey into secure code review, where I accidentally rediscovered a vulnerability that was patched back in 2014 (CVE-2014-9495).
I’m sharing this to help others who are also learning and want to understand how vulnerabilities work in real-world code.
The Backstory⌗
I’m currently learning secure code review and wanted to pick a real-world open-source project to practice on. I chose libpng the widely-used C library for handling PNG images.
While going through the code, I found a few interesting things. But one stood out.
At first glance, it looked like a serious vulnerability: the code seemed to calculate memory based on user-controlled values like width and bit depth, and there weren’t any obvious safety checks in the version I was reviewing.
So I tried to exploit it… and it worked.
Well, kind of.
Instead of crashing, libpng stopped me in my tracks with an error. That’s when I realized this bug was already discovered and patched… back in 2014!
But hey I still learned a lot, and now I’m sharing that story.
The Code I Wrote to Trigger It⌗
I crafted a special PNG file using Python. This PNG had:
- A ridiculously large width (0x80000000, or 2GB)
- A bit depth of 16
This combination was enough to overflow an internal memory calculation, which could potentially lead to a heap buffer overflow.
Here’s the script I used:
And Then… It Blew Up When I opened this PNG with libpng, here’s what I got:
libpng error: PNG unsigned integer out of range libpng error during init_ioThat’s not just a crash that’s libpng detecting something fishy and refusing to process it. Basically:
“Nice try, hacker. Not today.”
What Was the Actual Bug? Let’s break it down.
In older versions of libpng, the code responsible for calculating how much memory was needed per image row looked like this:
rowbytes = width * (bit_depth * channels + 7) / 8;This line multiplies user-supplied values like width and bit_depth.
Now imagine:
width = 2,147,483,648 (2GB)
bit_depth = 16
The multiplication overflows a 32-bit unsigned integer. This silently wraps around to a smaller number, so libpng allocates less memory than needed.
That's a classic vulnerability:Integer overflow ➜ Miscalculated buffer size ➜ Heap buffer overflow
The Patch (CVE-2014-9495) In 2014, this exact issue was discovered and patched under CVE-2014-9495.
Patch Summary Added range checks before doing any memory calculations Ensured that values like width * bit_depth * channels don’t overflow Returned an error if something looked suspicious Now, the fixed version throws an error like:
libpng error: PNG unsigned integer out of rangeSimplified Patch Logic
if (width > PNG_UINT_31_MAX || (width * bit_depth * channels) > PNG_SIZE_MAX) png_error(png_ptr, "Image width is too large for this architecture");Patch for the bug is over here : patch
What I Learned This was a cool moment in my learning journey.
Key Takeaways: Always audit from source to sink vulnerable looking code might be safe if it’s validated somewhere else. Integer overflows in C are sneaky and dangerous know how they behave! Just because something doesn’t crash doesn’t mean it’s not worth exploring it might teach you something important.
Even though I didn’t find a brand-new 0-day, this experience helped me level up in secure code review and that’s a win in itself.
Wrapping Up If you’re learning security or bug hunting like me, don’t be afraid to dig into old projects and experiment. Even if you don’t find new bugs, you’ll uncover real-world lessons just like I did with this 11-year-old vulnerability.
Stay curious. Keep exploring.
.png)

