What CWE-95 really is
The short version: an application takes input that is partially attacker-controlled and
passes it into a function that parses the input as executable code. eval()
in Python. Invoke-Expression in PowerShell. system() in shell
wrappers. Templating engines that expose expression evaluation. Even a
json.loads followed by getattr() on the result, in certain ORM
patterns.
When sanitization is incomplete — or, more commonly, when a developer thinks they have sanitized but missed an edge case — the attacker breaks out of the data context and supplies instructions the interpreter happily runs.
Why it keeps happening in enterprise products
Three recurring patterns:
- Convenience over security. Eval-style evaluation lets a developer build a flexible configuration language in one afternoon instead of writing a proper parser in a week. The shortcut becomes permanent because nobody is paid to revisit it.
- "We already validate on the client." Client-side validation stops honest users from making mistakes. It stops zero attackers. This belief still costs careers.
- Trust boundaries drift. Code written for an internal-only admin interface gets exposed, over years, to less privileged users. What was safe when the caller was always root becomes dangerous when the caller is an operator account on a multi-tenant device.
The F5OS example
The recent F5OS CVE-2025-61955 is a textbook CWE-95: a local, authenticated attacker on an affected F5OS platform can slip crafted input into an evaluator and escalate privilege on the management plane. F5 requires local access, so it is not a drive-by — but in multi-tenant and multi-operator appliances, any cross-boundary escalation is real.
The fix landed in F5's official advisory K000156771. The interesting part is not the fix — it is the question: how many other components in how many other vendors' management planes have the same shape of bug, undiscovered?
What defenders should do
- Detect locally. Log process lineage on your appliances. An evaluator process spawning
/bin/shis your best lagging indicator. - Segment access. Management-plane privileges should not be reachable from general-purpose user tenants. Bastion-only, MFA-required, least-privilege.
- Patch promptly, verify once patched. Do not assume the fix applied. Re-test the exploit vector after patching if you can.
- Watch your own code. Every time your team writes
eval(user_input), ask whether a parser and a whitelist would be safer. Almost always, the answer is yes.
Why this class will outlive all of us
CWE-95 is older than I am. It is not going anywhere because the forces that create it — time pressure, flexibility, legacy code, drift in trust boundaries — are not going anywhere either. The best you can do is assume it is in your stack somewhere and plan your detection and response accordingly.
The teams that get hurt by CWE-95 advisories are the ones who treat each one as an isolated patch event. The teams that handle them well treat each one as a reminder to re-check their own management-plane exposure.
Written by Sari Taher.