javadockercontainer-securitycve

3 CVEs Triaged, 1 Package Removed: Fixing CVE-2025-69644 in the Temurin JDK Base Image

Share X in

The JDK base image behind our Java services carried three CVEs. Two melted away with an upgrade. The third — CVE-2025-69644 in binutils — had no fixed Alpine build at all, so the only remediation was removing the package. The scanner never even flagged it.

3CVEs triaged
0Findings after fix
+0.2MB registry cost
73Packages in final image

Scope: quay.io/hellodk/base-image:temurin-4.0.2-java21-base → ...:temurin-4.0.2-java21-base2. Base OS Alpine 3.24.2, Trivy 0.70.0, vulnerability DB dated 2026-10-11.

1. Scan

Step one was refreshing the scanner database and scanning three images with the same tool and the same severity set, so the numbers are comparable:

trivy image --download-db-only
trivy image --scanners vuln \
  --severity UNKNOWN,LOW,MEDIUM,HIGH,CRITICAL --format json <image>
ImageResult (2026-10-11 DB)
eclipse-temurin:21.0.12.1_1-jdk-alpine (upstream)2 MEDIUM
quay.io/hellodk/base-image:temurin-4.0.2-java21-base (old tag)1 MEDIUM
quay.io/hellodk/base-image:temurin-4.0.2-java21-base2 (fixed tag)0 findings

A secret scan of the fixed tag was also clean. The old tag‘s single finding was new — the 2026-10-11 database knows about a zlib CVE the 2026-10-06 database did not flag. Comparing scans across different database dates measures the database, not the image.

2. Triage

Three distinct CVEs touched this image. Two were ordinary patchable findings; one was not.

CVEPackageInstalled → FixedSeverityRemediation
CVE-2026-46675 use-after-free in png_read_endlibpng1.6.58-r1 → 1.6.59-r0MEDIUMapk upgrade
CVE-2026-85091 heap buffer overflow in gz_vacate()zlib1.3.2-r0 → 1.3.2-r1MEDIUMapk upgrade
CVE-2025-69644 objdump DoS via malformed DWARF loclists, CVSS 5.0binutils2.45.1-r1 → none on Alpine 3.24MEDIUMremove package

3. The Unpatchable One

CVE-2025-69644 is an unbounded loop in objdump when it parses crafted DWARF location-list headers — a local DoS via a malicious binary. Upstream fixed it in binutils 2.46. Alpine 3.24, the image‘s OS, ships binutils 2.45.1-r1 and offers nothing newer: a full apk upgrade on 2026-10-11 left the version unchanged while moving seven other packages.

Why the scanner stayed silent

Trivy reported zero binutils findings on the vulnerable image. Its Alpine advisory feed carries no entry mapping this CVE to Alpine‘s binutils build, so there was nothing to match. The CVE was found the old-fashioned way: listing installed packages and comparing versions against the CVE record (binutils < 2.46 affected). Lesson: a clean scan means “no known-to-the-feed findings”, not “no CVEs”. Package-version inspection still earns its keep.

Checking removal is safe

Before deleting anything, two checks on a live container of the upstream image:

# Anything depend on it?
apk info -r binutils
# binutils-2.45.1-r1 is required by:   (empty - nothing)

# Does Java survive without it?
apk del binutils --no-cache
javac -version  # javac 21.0.12.1
java -version   # openjdk 21.0.12.1 - works

No reverse dependencies, and both javac and java run fine afterwards. (The removal also purged orphaned jansson, libstdc++ and libgcc — Java still started, so they were binutils-only baggage.) The JRE variant of Temurin never shipped binutils at all, which corroborates that a Java runtime does not need it — but switching this JDK base to JRE would drop javac, a breaking change, so removal was the surgical fix.

4. Fix

Fix Pipeline

Upstream JDK 2 MEDIUM + binutils Apk Upgrade libpng + zlib fixed Del Binutils CVE-2025-69644 gone Base2 Pushed 0 findings Step 1: Scan Step 2: Upgrade Step 3: Remove Step 4: Verify
  1. Refreshed the scanner DB (Trivy vuln + Java DB, 2026-10-11) so findings reflect today‘s feeds, not last week‘s.
  2. Scanned three images — upstream JDK, old base tag, fixed candidate — with identical flags for a like-for-like comparison.
  3. Triaged per CVE: apk upgrade clears libpng and zlib; binutils has no Alpine fix, so it must be removed.
  4. One-line Dockerfile change in base-images/Dockerfile.temurin:
 FROM eclipse-temurin:21.0.12.1_1-jdk-alpine
# CVE-2025-69644: objdump DoS in binutils < 2.46. Alpine 3.24 ships
# binutils 2.45.1-r1 with no fixed build available, and nothing in this
# image depends on it, so remove it. javac/java verified working without it.
 RUN apk upgrade --no-cache && apk del --no-cache binutils && rm -rf /var/cache/apk/*
  1. Rebuilt from scratch (docker build --pull --no-cache) so no cached layer could hide a stale package, then pushed a new tag — temurin-4.0.2-java21-base2 — leaving the blog-pinned base digest untouched.

5. Verify

Verification ran against the registry copy, not the local build — pulled back by digest, then scanned and boot-checked:

CheckResult
Re-pull sha256:b9890614… from registryOK
Trivy vuln, all severities incl. UNKNOWN0 findings
Trivy secret scanclean
binutils installed / objdump presentabsent / absent
libpng 1.6.59-r0, zlib 1.3.2-r1fixed versions
java / javac run, user app, /app has no JAROK

6. Reproduce

# Scan any tag, split nothing - base images carry no JAR
trivy image --scanners vuln --format json \
  quay.io/hellodk/base-image:temurin-4.0.2-java21-base2 \
  | jq '[.Results[] | .Vulnerabilities[]? | .Severity]
        | group_by(.) | map({(.[0]): length}) | add'
# null  (no vulnerabilities key at all = zero findings)

# Confirm the unfixable package is gone, runtime intact
docker run --rm --entrypoint sh \
  quay.io/hellodk/base-image:temurin-4.0.2-java21-base2 \
  -c 'which objdump; java -version 2>&1 | head -n 1'

References: CVE-2025-69644 (binutils < 2.46, objdump DWARF DoS) · CVE-2026-46675 (libpng png_read_end use-after-free) · CVE-2026-85091 (zlib gz_vacate() heap overflow, published 2026-09-03). Related write-up: the 2026-10-02 Java base-image comparison that first triaged the libpng finding.

Share this post X in

Enjoyed this post?

Get the next one in your inbox — only when I ship something worth reading.

Newsletter form not configured.

Or follow on Substack for the newsletter.

Comments via GitHub Discussions

Comments not configured. Set GISCUS env vars to enable.