Skip to main content
Back to blog
Brian

Vendor says it is fixed. Your scanner says it is vulnerable. Now what?

Red Hat Enterprise Linux and RHEL-family vulnerability results can look contradictory because enterprise Linux vendors backport fixes. Here is how EVRs, advisories, scanner logic, and runtime state fit together.

Linux HardeningVulnerability ManagementPatch OperationsRHEL

If you have managed Red Hat® Enterprise Linux® (RHEL) or RHEL-family systems for long enough, you have probably seen this argument.

The scanner says a package is vulnerable.

The Linux admin checks the host and says the package is patched.

The vendor advisory says the fix shipped.

The dashboard still has a red finding.

Then everyone loses time translating between scanner language, vendor language, package versions, and whatever evidence the team has from the host.

This is especially common with packages like OpenSSL, glibc, curl, libxml2, krb5, kernel packages, and other libraries that show up all over the system. It is not always because the scanner is bad. It is not always because the host is unpatched. Usually the problem is that different tools are answering different questions.

The scanner may be asking:

Does this installed software appear older than the upstream fixed version?

The vendor advisory is asking:

Did this distro package receive the backported security fix?

The operator is asking:

Can I close this vulnerability without lying to myself?

Those are not the same question.

Enterprise Linux does not always move like upstream

Upstream projects usually talk in upstream versions.

For example, a vulnerability might be fixed in an upstream release like:

OpenSSL 3.0.x
curl 8.x
kernel 6.x

Enterprise Linux distributions often do something different. They keep a stable package version for the life of a major release and backport security fixes into that package.

That means a RHEL-family package can look old if you only glance at the upstream version number, but still contain the vendor’s security fix.

That is not trickery. It is one of the reasons people run enterprise Linux. Stable ABI/API expectations matter. Large fleets do not want every security fix to behave like a surprise feature upgrade. Vendors patch the security issue while preserving the distribution’s packaging and compatibility model.

The catch is that vulnerability tooling has to understand that model.

If a scanner only compares upstream semantic versions, it can make a fixed enterprise package look vulnerable. If an operator only says “the vendor backported it” without showing the exact package/advisory evidence, security has to take that on trust. Neither side feels great about that.

The useful middle ground is boring:

Show the installed package EVR.
Show the vendor-fixed EVR.
Show the advisory or CVE mapping.
Then decide whether the host is actually remediated.

EVR is the part people skip

For RPM-based systems, the package version is not just the upstream version.

It is usually evaluated as:

epoch:version-release

That is the EVR: epoch, version, release.

A package might look like this illustrative example:

openssl-libs-1:3.0.7-27.el9.x86_64

In that example:

name:    openssl-libs
epoch:   1
version: 3.0.7
release: 27.el9
arch:    x86_64

The upstream-looking part is 3.0.7, but the distro fix may live in the release part, such as 27.el9. That release number is not decoration. It is often where the vendor’s backported fix becomes visible.

This is why two package builds with the same upstream version can have different security posture:

openssl-libs-1:3.0.7-18.el9
openssl-libs-1:3.0.7-27.el9

Both say 3.0.7. They are not the same package build.

If a vendor advisory says a CVE is fixed in 1:3.0.7-27.el9, then 1:3.0.7-18.el9 is below the fixed build and 1:3.0.7-27.el9 is at the fixed build. Treat the numbers here as an EVR example, not as a substitute for the advisory metadata for your distribution and release.

The evidence should preserve that distinction.

On a host, start with the package query:

rpm -q openssl-libs

If you want the fields separated:

rpm -q --qf '%{NAME} %{EPOCH}:%{VERSION}-%{RELEASE}.%{ARCH}\n' openssl-libs

If epoch is not set, RPM may print (none) for the epoch. That is fine. The point is to capture the full package identity instead of copying only the upstream version.

Advisory context matters

The next question is:

Fixed according to whom?

For enterprise Linux, the answer should come from distro/vendor security metadata, not a random upstream version comparison.

On systems with the right repository metadata available, dnf updateinfo can be useful:

dnf updateinfo list --cve CVE-2024-1086

or:

dnf updateinfo info --cve CVE-2024-1086

You can also inspect package changelogs, though changelogs should be treated as supporting evidence rather than the only source of truth:

rpm -q --changelog openssl-libs | grep -i CVE-20XX-NNNN

Depending on the distro, subscription state, repository configuration, and mirror metadata, these commands may not always return everything you want. That is part of the operational annoyance. The absence of convenient local metadata is not the same thing as proof that the package is vulnerable or fixed.

For clean vulnerability evidence, I want the result normalized into something like this:

CVE: CVE-20XX-NNNN
package: openssl-libs
distro: rhel
distro_version: 9
installed_evr: 1:3.0.7-18.el9
vendor_fixed_evr: 1:3.0.7-27.el9
status: installed package is below vendor-fixed EVR

Or:

CVE: CVE-20XX-NNNN
package: openssl-libs
distro: rhel
distro_version: 9
installed_evr: 1:3.0.7-27.el9
vendor_fixed_evr: 1:3.0.7-27.el9
status: vendor-fixed package is installed

That is the package-state answer.

It is much better than:

OpenSSL 3.0.7 looks old.

Why scanners disagree

Scanner disagreement usually comes from one of a few places.

Upstream version comparison

Some findings are based on the upstream version where a CVE was fixed. That works well for some ecosystems. It gets rough on enterprise Linux when the vendor backports the fix without changing the upstream version string in the way the scanner expects.

This is where “false positive” conversations usually start.

But I try not to lead with that phrase. Sometimes the scanner is wrong. Sometimes the scanner is missing vendor context. Sometimes the host really is behind. Sometimes the data is stale. “False positive” is a conclusion, not the starting point.

Start with evidence.

Missing distro context

The same package name and upstream version can mean different things across RHEL-compatible and other RPM-based enterprise Linux environments, including Rocky Linux, AlmaLinux, Oracle Linux, Amazon Linux, CentOS Stream, and SLES.

The scanner needs to know the distro and release. The operator needs to show it too.

Evidence that says:

openssl-libs 3.0.7

is incomplete.

Evidence that says:

Rocky Linux 9.4
openssl-libs-1:3.0.7-27.el9.x86_64
vendor-fixed EVR: 1:3.0.7-27.el9

is much harder to misread.

Stale scan data

Sometimes the finding is not a backport problem. Sometimes the scanner has not scanned since the update. Sometimes an agent missed the host. Sometimes the package changed after the last inventory sync. Sometimes the security dashboard is looking at yesterday’s evidence while the operator is looking at today’s host.

That is a boring explanation, but it happens constantly.

This is why every useful finding needs a collection timestamp.

Package installed but not relevant

A package can be installed because another package depends on it. That does not always mean the vulnerable code path is exposed, reachable, or running in a meaningful way.

Be careful here. “Installed but not used” should not be hand-waved. It needs evidence.

At minimum, you want to know:

  • is the package installed?
  • what owns the files?
  • are any related libraries mapped by running processes?
  • is a related service listening?
  • is the host exposed?
  • is there a KEV or high-likelihood signal that changes priority?

That does not make the CVE disappear, but it changes how the work should be prioritized.

Fixed on disk is still not the end

Here is the part that gets missed in both scanner and backport arguments:

Even if the vendor-fixed package is installed, the host may not be remediated yet.

Package state can be correct while runtime state is still stale.

For a kernel:

uname -r
rpm -q --last kernel-core 2>/dev/null | head -1

The newest installed kernel package is not necessarily the running kernel. If the fixed kernel is installed but uname -r still shows the older kernel, the host needs a reboot before that kernel vulnerability is actually gone from the running system.

For shared libraries:

sudo grep -H '(deleted)' /proc/[0-9]*/maps 2>/dev/null | \
  grep -E '/(usr/)?lib(64)?/.*\.so'

If a long-running process still maps an old deleted library object, then the package can be fixed on disk while the process still uses old code in memory.

That gives you a different status:

vendor-fixed package is installed
runtime evidence: nginx still maps old libssl object
required action: restart nginx

This is the difference between “the scanner is wrong” and “the scanner is incomplete.”

The installed package may be fixed. The live system may still need a restart or reboot.

Both can be true.

A better way to classify the finding

When a scanner and a RHEL-family host disagree, I like to avoid jumping straight to “open” or “closed.”

Use more precise buckets.

Package update required

The installed EVR is below the vendor-fixed EVR.

installed_evr < vendor_fixed_evr
action: update package

This is straightforward. The package needs to move.

Vendor-fixed on disk

The installed EVR is at or above the vendor-fixed EVR.

installed_evr >= vendor_fixed_evr
action: package update is not the remaining work

This is where many scanner disagreements should land. The package-state finding may be satisfied, but you still need runtime verification.

Restart required

The package is fixed on disk, but a running process still maps old code.

package fixed on disk
old mapped object still live
action: restart affected service

This is common with long-running daemons.

Reboot required

The fixed kernel is installed, but the host is still running an older kernel.

fixed kernel installed
running kernel old
action: reboot host

This is one of the cleanest examples of applied-versus-live drift.

Review required

The data is incomplete or the risk depends on exposure, ownership, container context, or reachability.

action: review before closure

This is better than calling something clean because the evidence is inconvenient.

Verified fixed

The vendor-fixed package is installed and runtime evidence does not show the vulnerable code still live.

package fixed
runtime clean
verified_at: timestamp

This is the closure state everyone wants. It should be earned with evidence.

A practical evidence shape

For a RHEL-family finding, useful evidence looks like this:

Host:
Distro:
Checked at:

CVE:
Package:
Installed EVR:
Vendor-fixed EVR:
Advisory source:

Package-state result:
Runtime-state result:
Service / process context:
Recommended action:
Verification result:

Example:

Host: rhel-app-01
Distro: Rocky Linux 9.4
Checked at: 2026-07-17T14:12:40Z

CVE: CVE-20XX-NNNN
Package: openssl-libs
Installed EVR: 1:3.0.7-27.el9
Vendor-fixed EVR: 1:3.0.7-27.el9
Advisory source: vendor security metadata

Package-state result: vendor-fixed package is installed
Runtime-state result: nginx still maps an old deleted libssl object
Service / process context: nginx workers on web service
Recommended action: restart nginx during approved service window
Verification result: re-check process maps after restart

That is much more useful than arguing whether the scanner is “right.”

It shows which layer is satisfied and which layer still needs work.

Be careful with the word clean

There are a few traps worth avoiding.

Do not call a finding clean just because the upstream version looks okay.

Do not call a finding clean just because the vendor-fixed package is installed.

Do not call a scanner finding false just because the version string looks old.

Do not ignore a vendor advisory because the upstream version number did not change.

Do not treat missing runtime evidence as proof that no vulnerable code exists.

The safe language is more precise:

Fixed package installed.
Restart still required.
Reboot still required.
Scanner appears to lack vendor backport context.
Runtime evidence not collected.
Verified fixed at this timestamp.

That language is not as satisfying as a green checkmark, but it is much more useful.

The real goal is shared evidence

The scanner is trying to reduce risk.

The Linux team is trying to keep systems patched without breaking production.

Security is trying to know what is still exposed.

Compliance may need evidence that the remediation process is real.

Those groups do not need a debate over who is technically right. They need a shared record of what was found, what the vendor fixed, what is installed, what is running, and what action remains.

For RHEL-family systems, that shared record starts with EVR and advisory context.

Then it has to keep going into runtime state.

Because the package can be fixed and the process can still be old.

That is the part a lot of dashboards still flatten.

If you want to see the evidence model in report form, the sample Patch Truth report shows how package state, runtime state, and next action fit together. For one supported RHEL-family host, the one-host snapshot is a quick way to test the same flow on a real system.

Trademark note: Red Hat and Red Hat Enterprise Linux are trademarks or registered trademarks of Red Hat, Inc. oxharden is not affiliated with, sponsored by, or endorsed by Red Hat.