fake_kworker: stop firing CRITICAL on genuine Linux kernel images - #1696
Merged
egibs merged 2 commits intoSep 7, 2026
Merged
Conversation
Fixes chainguard-dev#1695. Every uncompressed Linux kernel image carries kernel/workqueue.c's own worker-naming format strings, so fake_kworker flags real kernels as "Pretends to be a kworker kernel thread". A kernel is not pretending to be a kworker; it is the thing that creates them. The rule already recognised one member of this family: $not_rescue = "kworker/R-%s" which is itself a kernel string -- the rescuer-thread name. A kernel carries four such spellings and only that one was subtracted, so the count always exceeded the exclusions and the rule fired on every kernel, on every rebuild. Adding its three siblings from the same source file makes a genuine kernel fully explained instead of one quarter explained. Counted, not suppressed, matching how the existing exclusions work: a sample carrying the kernel's format strings *and* a rendered name still exceeds the exclusions and still fires. The distinction the rule now draws is between format strings with % conversions and the rendered names malware copies to blend into ps output. Verified with yara 4.5.5 against the existing samples: target before after ---------------------------- --------- --------- kernel strings only FIRES clean kernel strings + [kworker/0:0] FIRES FIRES 2024.kworker_pretenders/gafgyt FIRES FIRES 2024.kworker_pretenders/emp3r0r.agent FIRES FIRES 2024.kworker_pretenders/aclocal.m4 clean clean Both real masquerade samples are still detected. Diffing every rule in the file across all five targets, the only behavioural change is fake_kworker on the kernel-strings input. The separate `kworker: medium` rule still matches, deliberately left alone: "Mentions kworker" is accurate for a kernel, and its filesize < 1MB gate puts real kernel images out of scope anyway. Observed on linux-firecracker-6.18, a kernel built from source in chainguard-dev/stereo. Only uncompressed images are affected -- a sibling package shipping compressed vmlinuz reports no findings in the same scan, because the strings are unreadable until decompression, so today the CRITICAL depends on compression rather than on anything security-relevant. Happy to add a kernel sample to malcontent-samples if that is wanted; it would be a large binary, so I have not assumed.
egibs
approved these changes
Sep 7, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #1695.
Every uncompressed Linux kernel image carries
kernel/workqueue.c's own worker-naming format strings, sofake_kworkerflags real kernels as CRITICAL "Pretends to be a kworker kernel thread". A kernel is not pretending to be a kworker; it is the thing that creates them.Why it fires
The rule already recognised one member of this family:
which is itself a kernel string — the rescuer-thread name. A kernel carries four such spellings and only that one was subtracted, so the count always exceeded the exclusions and the rule fired on every kernel, on every rebuild. This adds its three siblings from the same source file, so a genuine kernel is fully explained rather than one quarter explained.
Counted, not suppressed, matching how the existing exclusions already work. The distinction the rule now draws is between format strings carrying
%conversions and the rendered names malware copies to blend intopsoutput.Verification
yara 4.5.5, against the existing samples in
tests/linux/2024.kworker_pretenders:[kworker/0:0]gafgytemp3r0r.agentaclocal.m4Both real masquerade samples are still detected. Row two is the one that matters for the tradeoff: because the exclusions are counted rather than suppressed, a sample that ships the kernel's format strings and a rendered name still exceeds the exclusions and still fires — so this does not hand anything a bypass by embedding kernel strings.
Diffing every rule in the file across all five targets, the only behavioural change is
fake_kworkeron the kernel-strings input.The separate
kworker: mediumrule still matches, deliberately left alone: "Mentions kworker" is accurate for a kernel, and itsfilesize < 1MBgate puts real kernel images out of scope anyway.Origin
Observed on
linux-firecracker-6.18, a kernel built from source inchainguard-dev/stereo, where it fails the scan on every version bump — so the finding is permanently red and gets routinely ignored, which is the failure mode worth avoiding for a CRITICAL rule.Only uncompressed images are affected. A sibling package shipping compressed
vmlinuzreports no findings in the same scan, because the strings are unreadable until decompression — so today whether the CRITICAL fires depends on compression rather than on anything security-relevant.Happy to add a kernel sample to
malcontent-samplesif that is wanted; it would be a large binary, so I have not assumed.