← Back to Feed CACHED · 2026-05-17 09:42:19 · cache_key CVE-2025-29912
CVE-2026-34757 · CWE-416 · Disclosed 2026-04-09

LIBPNG is a reference library for use in applications that read

ASSESSED — NOISGATE V0.5
Vendor
Reassessed
Verdict:
01 · The Real Story

This is less a booby-trapped image and more a library caller shooting its own foot with a borrowed pointer

CVE-2026-34757 is a use-after-free in libpng's chunk setter path. If an application reads an internal pointer with png_get_PLTE(), png_get_tRNS(), or png_get_hIST() and then passes that same pointer back into the matching setter on the same png_struct/png_info, libpng frees the internal buffer and then copies from the now-dangling pointer. Upstream says the broad affected range is >= 1.0.9 because of png_set_hIST, while the PLTE and tRNS variants are much narrower regressions tied to 1.6.56; fixed upstream in 1.6.57 and 1.8.0 trunk.

The vendor's MEDIUM label is technically fair in a vacuum, but for enterprise patch prioritization it overstates real-world urgency. This is not a generic malicious-image parser bug and cannot be triggered by a crafted PNG file alone; the application has to implement a very specific getter-then-setter aliasing pattern on the same structures. That prerequisite crushes reachable population, makes exposure highly app-specific, and turns this into dependency hygiene rather than emergency patching.

"Real bug, tiny attack surface: this needs application misuse, not just a malicious PNG."
02 · The Attack Path

4 steps from start to impact.

STEP 01

Reach a libpng-using code path

The attacker first needs an application feature that reads or rewrites PNG metadata using libpng. There is no standalone network daemon or universal remote surface here; this starts inside an application workflow that already processes PNG chunks. Public references are the upstream advisory and Issue #836, not a weaponized exploit kit.
Conditions required:
  • Target software must use vulnerable libpng
  • Attacker must reach a PNG-processing feature in that software
  • The feature must touch PLTE, tRNS, or hIST chunk handling
Where this breaks in practice:
  • Most enterprise inventories know they have libpng somewhere, but not which apps actually exercise these chunk APIs
  • Many consumers only decode images and never round-trip these specific chunks
Detection/coverage: SCA/SBOM tools can flag vulnerable libpng versions, but they cannot prove the dangerous API usage pattern exists in the consuming app.
STEP 02

Depend on application-level API misuse

The exploit condition is not malformed input alone; the application must call png_get_*() and then feed the returned internal pointer back into png_set_*() on the same struct pair. Upstream explicitly states this sequence is required, and Issue #837 documents the aliasing pattern for png_set_tRNS() in detail.
Conditions required:
  • Application or binding must reuse getter-returned pointers
  • Getter and setter must operate on the same png_struct/png_info pair
Where this breaks in practice:
  • This is a narrow developer-usage bug, not a normal parser state reached by arbitrary files
  • Many well-written callers copy data into caller-owned buffers or simply never re-set unchanged chunk data
Detection/coverage: Code scanning can sometimes catch getter-returned pointer reuse in source, but binary-only applications are hard to assess without vendor guidance.
STEP 03

Trigger the dangling-pointer copy

Once the bad call sequence occurs, the setter frees internal storage and then copies from the stale pointer. Issue #836 walks through the sequence for png_set_PLTE(), including the free and subsequent memcpy from the caller-supplied pointer.
Conditions required:
  • Matching chunk data must already be present so the getter returns a live internal pointer
  • The app must invoke the corresponding setter after the getter
Where this breaks in practice:
  • The input image does not need to be malformed, but it still must carry the relevant chunk
  • Not every image-processing path touches these metadata chunks at all
Detection/coverage: Runtime memory tooling like ASan/Valgrind will catch this in testing; production endpoint tooling usually will not unless the consuming app crashes or emits diagnostic telemetry.
STEP 04

Harvest low-grade impact

The practical outcomes are low-grade confidentiality and integrity issues: stale or newly reallocated heap bytes can be copied into chunk structures, producing silent metadata corruption or heap-content leakage through later getters or rewritten output files. Upstream does not claim code execution, and the published CVSS omits availability impact entirely.
Conditions required:
  • Application must later expose or serialize the corrupted/leaked chunk data
  • Leaked heap contents must be useful to the attacker in that app context
Where this breaks in practice:
  • Impact is narrow and application-specific
  • No public evidence shows reliable RCE or broad in-the-wild abuse
Detection/coverage: Little to no network detection value. Best evidence is crash telemetry, memory sanitizers in CI, or content-integrity anomalies in rewritten PNG outputs.
03 · Intelligence Metadata

The supporting signals.

In-the-wild statusNo known active exploitation found. CISA KEV search returns no entry for CVE-2026-34757, and the catalog does not list it as of the latest check. Source: CISA KEV catalog.
Proof-of-concept availabilityPublic bug reports, not a mature exploit ecosystem. Upstream references GitHub Issues #836 and #837, both opened by @Iv4n550, which document trigger conditions and code paths.
EPSS0.00006 from the user-provided intel block; that is effectively floor-level exploitation probability. I did not find a primary FIRST per-CVE page exposing a percentile during this check, so percentile is undetermined from primary sources.
KEV statusNot KEV-listed. No CISA KEV entry found for this CVE during verification.
CVSS vector reality checkCVSS:3.1/AV:L/AC:L/PR:N/UI:N/S:U/C:L/I:L/A:N is directionally right because this is an API misuse issue with local/library semantics. In real enterprise terms, the bigger downgrade comes from the hidden prerequisite: the app must call the API incorrectly.
Affected versionsNVD and the GitHub advisory describe >= 1.0.9 to < 1.6.57, but upstream clarifies the broad exposure is mainly the hIST path, while PLTE and tRNS are regressions in 1.6.56 and 1.8.0 trunk. Sources: NVD, GHSA, libpng home page.
Fixed versionsUpstream fix is 1.6.57; GitHub advisory also notes 1.8.0 trunk as patched. Downstreams show backports rather than that exact version string, e.g. SUSE marks the issue resolved via multiple advisories and package updates. Sources: GHSA, SUSE CVE page.
Exposure/scanning realityNo meaningful Shodan/Censys/GreyNoise exposure story. This is a library flaw, not an internet-exposed service fingerprint. The important exposure question is internal: which apps bundle or link libpng and whether they perform getter-to-setter round-trips.
Disclosure timelineGitHub advisory published 2026-04-08; NVD published 2026-04-09; upstream release 1.6.57 was announced 2026-04-09. Sources: GHSA, NVD, oss-sec release post.
Reporter / researcherReported in upstream issues by @Iv4n550; release/advisory text published by Cosmin Truta for libpng. Sources: Issue #836, GHSA.
04 · The Call

noisgate verdict.

Final Verdict
DOWNGRADED to LOW (2.4/10)

The decisive factor is application-misuse dependency: a crafted PNG is not enough, because the consuming program must make a very specific getter-then-setter aliasing call on the same libpng structures. That turns a wide dependency into a narrow reachable population with low-grade impact, which is backlog-hygiene territory rather than priority patching.

HIGH Exploit preconditions are narrow and clearly documented by upstream
MEDIUM Enterprise exposure population is hard to quantify because SBOMs show libpng presence, not dangerous API usage

Why this verdict

  • Downward adjustment: requires application misuse, not just attacker-controlled content. Upstream explicitly says the flaw cannot be triggered by a crafted PNG file alone; the app must call the getter and setter in sequence on the same struct pair.
  • Downward adjustment: effective attacker position is post-feature-reach and app-specific. Even if the attacker can supply a PNG, they still need a consumer that round-trips PLTE/tRNS/hIST metadata in the unsafe pattern.
  • Downward adjustment: blast radius is low. Published impacts are low confidentiality and low integrity only; no availability hit, no demonstrated RCE chain, and no active exploitation evidence.

Why not higher?

There is no evidence that ordinary image viewing or parsing alone hits this bug. The exploit chain depends on a non-default API misuse pattern in the consuming application, which sharply reduces the real exposed population. No KEV listing, no reported campaigns, and negligible EPSS all reinforce the downgrade.

Why not lower?

It is still a real memory-safety flaw in a very widely distributed library, and some wrappers or image-transformation pipelines may absolutely perform this kind of metadata round-trip. Because the dangerous pattern can be invisible in third-party binaries and bundled software, it deserves tracking and eventual patching rather than dismissal.

05 · Compensating Control

What to do — in priority order.

  1. Inventory libpng consumers — Map where libpng is linked or bundled, then separate simple image decoders from applications that rewrite PNG metadata. For a LOW verdict there is no mitigation SLA; do this as backlog hygiene so you can focus patch effort on the handful of apps that may actually exercise the vulnerable API sequence.
  2. Review code for getter-to-setter aliasing — Search internal code and bindings for png_get_PLTE, png_get_tRNS, or png_get_hIST followed by the matching setter on the same object pair. Remove redundant setter calls or copy into caller-owned buffers first; for a LOW verdict this is normal engineering cleanup, not emergency work.
  3. Prefer downstream backports where available — On distro-managed systems, use vendor-packaged fixes instead of forcing a source upgrade everywhere. SUSE and other downstreams may already have backported fixes, which is the lowest-friction way to close exposure during routine maintenance.
  4. Add sanitizer coverage in CI — Build image-processing services with ASan/UBSan in pre-production tests to catch this and similar API misuse bugs. This won't protect production endpoints directly, but it is the fastest way to verify whether your own code path is actually reachable.
What doesn't work
  • A WAF does nothing here because this is not an HTTP parser edge case and not a network-reachable service signature.
  • Email or web content filtering is weak compensation because the file alone is insufficient; the consuming application's API pattern is the real gate.
  • EDR alone is unlikely to give strong signal unless the app crashes or memory tooling is enabled; the typical outcome is low-grade corruption or leakage, not loud process behavior.
06 · Verification

Crowdsourced verification payload.

Run this on the target host or inside the application container/CI image where libpng is installed. Invoke it as python3 check_libpng_cve_2026_34757.py for autodetect, or python3 check_libpng_cve_2026_34757.py --version 1.6.56 to test a known package version; no admin rights are required unless your environment restricts library/package metadata access.

noisgate-verify.py
PYTHONREAD-ONLYSAFE
#!/usr/bin/env python3
# Check exposure to CVE-2026-34757 in libpng
# Exit codes:
#   0 = PATCHED
#   1 = VULNERABLE
#   2 = UNKNOWN

import argparse
import os
import re
import shutil
import subprocess
import sys
from ctypes.util import find_library

PATCHED_VERSION = (1, 6, 57)


def parse_version(text):
    if not text:
        return None
    m = re.search(r'(\d+)\.(\d+)\.(\d+)', text)
    if not m:
        return None
    return tuple(int(x) for x in m.groups())


def version_to_str(v):
    return '.'.join(str(x) for x in v) if v else 'UNKNOWN'


def run_cmd(cmd):
    try:
        p = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, timeout=8)
        if p.returncode == 0:
            return (p.stdout or p.stderr).strip()
    except Exception:
        pass
    return None


def detect_versions():
    findings = []

    # 1) explicit version argument is handled outside this function

    # 2) pkg-config
    if shutil.which('pkg-config'):
        out = run_cmd(['pkg-config', '--modversion', 'libpng'])
        v = parse_version(out)
        if v:
            findings.append(('pkg-config:libpng', v))
        else:
            out = run_cmd(['pkg-config', '--modversion', 'libpng16'])
            v = parse_version(out)
            if v:
                findings.append(('pkg-config:libpng16', v))

    # 3) libpng-config
    if shutil.which('libpng-config'):
        out = run_cmd(['libpng-config', '--version'])
        v = parse_version(out)
        if v:
            findings.append(('libpng-config', v))

    # 4) common package managers
    if shutil.which('dpkg-query'):
        for pkg in ['libpng16-16', 'libpng16-16t64', 'libpng', 'libpng-dev', 'libpng16-dev']:
            out = run_cmd(['dpkg-query', '-W', '-f=${Version}', pkg])
            v = parse_version(out)
            if v:
                findings.append((f'dpkg:{pkg}', v))

    if shutil.which('rpm'):
        for pkg in ['libpng', 'libpng16', 'libpng-devel']:
            out = run_cmd(['rpm', '-q', pkg, '--qf', '%{VERSION}-%{RELEASE}'])
            v = parse_version(out)
            if v:
                findings.append((f'rpm:{pkg}', v))

    if shutil.which('apk'):
        out = run_cmd(['apk', 'info', '-v'])
        if out:
            for line in out.splitlines():
                if line.startswith('libpng-') or line.startswith('libpng-dev-'):
                    v = parse_version(line)
                    if v:
                        findings.append((f'apk:{line.split('-')[0]}', v))

    # 5) try to infer from library filename/path
    lib = find_library('png16') or find_library('png')
    if lib:
        v = parse_version(lib)
        if v:
            findings.append((f'ctypes:{lib}', v))

    # de-duplicate while preserving order
    seen = set()
    unique = []
    for src, v in findings:
        key = (src, v)
        if key not in seen:
            seen.add(key)
            unique.append((src, v))
    return unique


def classify(version):
    # CVE affects >=1.0.9 and <1.6.57 according to upstream/NVD.
    # Note: some distros backport fixes without bumping to 1.6.57, so package-manager results may need vendor advisory confirmation.
    if version is None:
        return ('UNKNOWN', 2)
    if version < (1, 0, 9):
        return ('PATCHED', 0)
    if version < PATCHED_VERSION:
        return ('VULNERABLE', 1)
    return ('PATCHED', 0)


def main():
    ap = argparse.ArgumentParser(description='Check libpng version exposure to CVE-2026-34757')
    ap.add_argument('--version', help='Override autodetect and test a specific libpng version, e.g. 1.6.56')
    args = ap.parse_args()

    if args.version:
        v = parse_version(args.version)
        if not v:
            print('UNKNOWN - could not parse --version argument')
            sys.exit(2)
        status, code = classify(v)
        print(f'{status} - supplied version {version_to_str(v)} vs patched threshold 1.6.57')
        sys.exit(code)

    findings = detect_versions()
    if not findings:
        print('UNKNOWN - could not detect libpng version via pkg-config, libpng-config, package manager, or library path')
        sys.exit(2)

    # Prefer the highest detected semantic version as the best installed candidate.
    highest = max(v for _, v in findings)
    status, code = classify(highest)

    print(f'Detected libpng candidates:')
    for src, v in findings:
        print(f'  - {src}: {version_to_str(v)}')
    print(f'Assessment basis: highest detected version {version_to_str(highest)}; patched threshold 1.6.57')

    if code == 1:
        print('VULNERABLE - note that vendor backports may still fix this without changing the upstream version string; confirm with your distro advisory')
    elif code == 0:
        print('PATCHED')
    else:
        print('UNKNOWN')
    sys.exit(code)


if __name__ == '__main__':
    main()
07 · Bottom Line

If you remember one thing.

TL;DR
Monday morning, do not burn emergency change budget on this one. Use SBOM/SCA to find where libpng is present, then quickly separate plain decoders from applications that actually rewrite PNG metadata; there is no noisgate mitigation SLA for a LOW verdict, and there is likewise no noisgate remediation SLA beyond normal backlog hygiene, so roll the upstream 1.6.57 fix or distro backport into your routine dependency refresh cycle and reserve priority patching for evidence that a business-critical app really performs the dangerous getter-to-setter round-trip.

Sources

  1. NVD CVE-2026-34757
  2. GitHub Security Advisory GHSA-6fr7-g8h7-v645
  3. libpng upstream security notes
  4. Upstream issue #836
  5. Upstream issue #837
  6. oss-sec release announcement for 1.6.57
  7. SUSE CVE page
  8. CISA KEV catalog
Peer Review

What defenders are saying.

Submit a review attribution: handle + country only
0 flags selected · stored anonymously
Validation Results

Crowdsourced verification outputs.

Results submitted by users who ran the verification payload against their environment.