Someone left an eval() call in a ham-radio hobby app's CSV parser, and it got a CVSS score meant for enterprise software
CVE-2026-78136 is an eval injection in CHIRP, a free, open-source desktop application used by amateur radio hobbyists to program frequencies into handheld and mobile radios. The flaw lives in _clean_tmode within drivers/kenwood_itm.py — specifically the Kenwood ITM CSV import path. When a user opens a crafted CSV file, unsanitized fields are passed to Python's eval(), granting the attacker arbitrary code execution as the user running CHIRP. All versions before commit 39178dbfc4fece083ab9ed20286d6ae3a91a718e are affected.
The vendor HIGH / 7.8 score is mathematically correct per CVSS 3.1 (AV:L/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H), but it dramatically overstates the real-world risk to any enterprise. CHIRP is a hobbyist desktop utility, not enterprise infrastructure. It doesn't run as a service, doesn't listen on a port, and has no server component. In a 10,000-host fleet it is likely installed on zero hosts — or at most on one radio technician's workstation. The CVSS score tells you what happens *if* exploitation succeeds; it tells you nothing about the probability, which in any enterprise is vanishingly small.
3 steps from start to impact.
Craft malicious Kenwood ITM CSV
_clean_tmode. Because the function passes field values through eval(), any valid Python expression will execute. No special tooling is required — a text editor suffices.- Knowledge of CHIRP's Kenwood ITM CSV schema
- Ability to deliver a file to the target
- Attacker must know the target uses CHIRP *and* imports Kenwood ITM files — an extremely narrow intersection
Social-engineer the victim into opening the file
- Victim has CHIRP installed
- Victim uses the Kenwood ITM driver specifically
- Victim manually opens the crafted file
- Requires active user interaction — no drive-by or auto-execution path
- Email gateways and endpoint protection will scan attachments
- Ham radio operators typically exchange CSV files only within trusted communities
Arbitrary code execution as the desktop user
- Steps 1 and 2 succeeded
- Execution context is an unprivileged desktop user, not SYSTEM or root
- No lateral movement capability unless chained with a separate escalation bug
- On a managed workstation, application allowlisting (AppLocker/WDAC) would block spawned processes
The supporting signals.
| In-the-Wild Exploitation | None observed. No reports of active exploitation. Not listed on CISA KEV. |
|---|---|
| Proof-of-Concept | No public PoC exploit identified. Crafting one is trivial (embed Python in a CSV field), but the target population is too small to attract exploit developers. |
| EPSS | Not yet scored (disclosed 2026-08-23). Expected to be very low given the niche attack surface. |
| KEV Status | Not listed. No indication of government or threat-actor interest. |
| CVSS Vector | CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H — Local vector with required user interaction. The 7.8 score reflects impact *if* exploited, not likelihood. |
| Affected Versions | All CHIRP versions before commit 39178dbfc4fece083ab9ed20286d6ae3a91a718e. This includes the current stable release 2026.1.30 available on Chocolatey. |
| Fixed Version | Any build including commit 39178db or later. No tagged release yet — fix is at the git HEAD level. |
| Scanning / Exposure | CHIRP has no network footprint. Shodan/Censys/GreyNoise are irrelevant — this is a local desktop application with no listening ports. |
| Disclosure Date | 2026-08-23 |
| Reporter | Not publicly attributed. Discovered via code audit of the eval() call in kenwood_itm.py. |
noisgate verdict.
The single most decisive factor is near-zero enterprise installed base — CHIRP is a hobbyist amateur radio programming tool that has no plausible presence in a managed enterprise fleet. The vulnerability's blast radius is capped at a single unprivileged desktop user session on a workstation that almost certainly does not exist in the environment.
Why this verdict
- Near-zero enterprise footprint: CHIRP is amateur radio hobbyist software. In a 10,000-host enterprise fleet, the expected install count is zero. Even organizations with two-way radio programs (public safety, utilities) would have CHIRP on at most 1–2 technician workstations — well below 0.01% of hosts.
- User interaction required (UI:R): Exploitation demands the victim manually import a crafted CSV file through CHIRP's GUI. There is no auto-execution, protocol handler, or drive-by path. This is social engineering against a hobbyist, not an enterprise attack surface.
- Unprivileged execution context: Code runs as the desktop user, not SYSTEM or root. No built-in privilege escalation. On a managed workstation with EDR and application control, lateral movement from this foothold is impractical.
- Role multiplier: not applicable. CHIRP does not occupy any high-value enterprise role. It is not a server, identity provider, hypervisor, backup system, CI/CD component, security agent, or network appliance. It is a standalone desktop GUI. The blast radius is a single user's workstation session — no domain, fleet, or supply-chain impact.
- Narrow driver scope: The vulnerable code path is in the Kenwood ITM driver specifically. Even among CHIRP users, only those importing Kenwood ITM CSV files trigger the affected function.
Why not higher?
Upgrading to MEDIUM or above would require either meaningful enterprise deployment or a high-value-role multiplier. CHIRP has neither. It is not enterprise software, has no server component, no network exposure, and occupies no infrastructure role. The 7.8 vendor score reflects pure CVSS impact math and ignores that the reachable population in any enterprise is effectively zero.
Why not lower?
The underlying flaw — eval() on untrusted input — is a textbook code execution primitive with low complexity. If CHIRP *is* installed and the user *does* open a malicious file, exploitation is reliable and grants full user-context code execution. That real (if improbable) impact keeps it above IGNORE.
What to do — in priority order.
- Confirm CHIRP is not installed in your fleet — Run a software inventory query (SCCM, Intune, osquery, or your EDR's installed-programs telemetry) for 'CHIRP' or 'chirpmyradio'. If zero results — which is the expected outcome — document it and close the ticket. No mitigation SLA applies for LOW-severity findings.
- If found: remove or update to a post-39178db build — If any installs exist, update to a CHIRP build containing commit 39178db or later. If no tagged release is available yet, remove the application or block it via application control policy. This is backlog hygiene, not an emergency.
- Block CSV imports from untrusted sources via user guidance — If the application must remain, instruct the user not to import CSV files from untrusted sources. This is defense-in-depth for a low-probability scenario.
- Network segmentation / firewall rules — irrelevant, CHIRP has no network attack surface; the vector is a local file open.
- WAF / IDS / IPS — cannot inspect local file operations on a desktop application.
- Email attachment blocking for .csv — overly broad and will break legitimate business workflows for a near-zero-probability threat.
Crowdsourced verification payload.
Run on any host where you want to check for a vulnerable CHIRP installation. Execute with python3 check_chirp_cve_2026_78136.py. No elevated privileges required — it inspects installed Python packages and CHIRP source files.
#!/usr/bin/env python3
"""Check for CVE-2026-78136 — CHIRP eval injection in kenwood_itm.py.
Outputs: VULNERABLE / PATCHED / UNKNOWN
Exit codes: 1=VULNERABLE, 0=PATCHED, 3=UNKNOWN
"""
import subprocess, sys, os, glob, re
def find_chirp():
# Check common install locations
candidates = []
if sys.platform == 'win32':
for base in [os.environ.get('PROGRAMFILES',''), os.environ.get('LOCALAPPDATA','')]:
if base:
candidates.extend(glob.glob(os.path.join(base, '**', 'chirp*', 'drivers', 'kenwood_itm.py'), recursive=True))
else:
candidates.extend(glob.glob('/usr/**/chirp*/**/kenwood_itm.py', recursive=True))
candidates.extend(glob.glob(os.path.expanduser('~') + '/**/chirp*/**/kenwood_itm.py', recursive=True))
# Also check pip
try:
r = subprocess.run([sys.executable, '-m', 'pip', 'show', '-f', 'chirp'], capture_output=True, text=True, timeout=10)
if r.returncode == 0:
loc = ''
for line in r.stdout.splitlines():
if line.startswith('Location:'):
loc = line.split(':', 1)[1].strip()
if loc:
p = os.path.join(loc, 'chirp', 'drivers', 'kenwood_itm.py')
if os.path.isfile(p):
candidates.append(p)
except Exception:
pass
return candidates
def check_file(path):
try:
with open(path, 'r', errors='ignore') as f:
content = f.read()
# Look for eval() usage in _clean_tmode
if '_clean_tmode' in content and 'eval(' in content:
return 'VULNERABLE'
elif '_clean_tmode' in content:
return 'PATCHED'
else:
return 'UNKNOWN'
except Exception as e:
return 'UNKNOWN'
files = find_chirp()
if not files:
print('UNKNOWN — CHIRP does not appear to be installed on this host.')
sys.exit(3)
for f in files:
result = check_file(f)
print(f'{result} — {f}')
if result == 'VULNERABLE':
sys.exit(1)
elif result == 'PATCHED':
sys.exit(0)
print('UNKNOWN')
sys.exit(3)If you remember one thing.
39178db or remove the application within the noisgate remediation SLA of 365 days. There is no active exploitation, no KEV listing, and no urgency. Do not let this consume cycles that belong to real enterprise vulnerabilities.Sources
What defenders are saying.
Crowdsourced verification outputs.
Results submitted by users who ran the verification payload against their environment.