← Back to Feed CACHED · 2026-10-01 13:34:10 · CACHE_KEY CVE-2026-86035
CVE-2026-86035 · CWE-78 · Disclosed 2026-09-29

Weblate is a web-based continuous localization platform used to manage software translations.

ASSESSED — NOISGATE
Vendor
—
—
—
Reassessed
—
—
—
Verdict: —
Do you agree?
01 · The Real Story

A loaded gun in a room almost nobody enters

CVE-2026-86035 is an argument-injection flaw in Weblate's Mercurial VCS backend. Versions 4.11.1 through 2026.7.1 pass repository-controlled filenames to hg cat without the -- option terminator, so a filename beginning with - is parsed as a Mercurial flag rather than a path. An authenticated user with project-scoped component.edit permission can exploit this to inject Mercurial configuration values via --config, replacing the expected hg cat call with an attacker-controlled shell command. The payload fires when Weblate next processes that component through the Update RESX files add-on — delivering full RCE as the Weblate service account. The bug is an incomplete remediation of CVE-2022-23915, which attempted the same -- fix four years ago but missed the get_file() code path.

The vendor rates this HIGH / 8.5 with AC:H and S:C, which is fair *on paper* — changed scope plus full CIA impact is a nasty CVSS profile. But CVSS does not capture the compounding deployment prerequisites: the attacker must hit a Weblate instance that (a) uses the Mercurial backend in an era where Git dominates >99% of VCS usage, (b) has the .NET RESX file format configured, and (c) has the Update RESX files add-on enabled on that Mercurial component. Each prerequisite slices the reachable population further. In practice, the set of Weblate instances simultaneously meeting all three conditions is vanishingly small. The vendor's 8.5 is an honest CVSS calculation, but it overstates real-world risk for defenders managing typical Weblate deployments.

"Mercurial+RESX prerequisite chain makes this RCE nearly theoretical for most Weblate shops"
02 · The Attack Path

5 steps from start to impact.

STEP 01

Obtain authenticated access with component.edit

The attacker needs a valid Weblate account that carries the project-scoped component.edit permission. This is not the default role for translators — it is a privileged permission typically granted to project managers or integration accounts. The attacker either compromises such an account or registers one on an open-registration instance and is promoted.
Conditions required:
  • Valid Weblate account
  • Project-scoped component.edit permission
Where this breaks in practice:
  • Default translator role does not carry component.edit
  • Most production instances restrict registration or use SSO
  • Promotion to component.edit requires admin action
Detection/coverage: Audit Weblate's component.edit permission grants; review user activity logs for unexpected component modifications.
STEP 02

Identify or create a Mercurial-backed RESX component

The target component must use the Mercurial VCS backend and the .resx (XML resource) file format. The attacker either finds an existing Mercurial+RESX component or creates one via their component.edit permission. Creating a new component pointing to an attacker-controlled Mercurial repository makes this step self-service — if Mercurial is installed on the server at all.
Conditions required:
  • Mercurial (hg) binary installed on the Weblate server
  • Weblate configured to allow Mercurial VCS backend
  • RESX file format in use or creatable
Where this breaks in practice:
  • Mercurial usage has collapsed industry-wide since Bitbucket dropped it in 2020 — <1% of new projects use it
  • Most Weblate Docker deployments do not ship with hg installed
  • RESX is a .NET-specific format; most Weblate users localize web/mobile apps with PO, XLIFF, or JSON
Detection/coverage: Check whether hg is present on the Weblate host (which hg). Audit components for Mercurial VCS type.
STEP 03

Enable the Update RESX files add-on

The attacker enables the *Update RESX files* add-on on the Mercurial+RESX component. This add-on triggers hg cat during the RESX update cycle, which is the vulnerable code path in HgRepository.get_file(). Without this add-on, the argument injection has no trigger.
Conditions required:
  • Add-on installation permission (implied by component.edit)
  • Update RESX files add-on available in the Weblate installation
Where this breaks in practice:
  • The add-on is niche — only relevant for .NET RESX workflows
  • Enabling add-ons on existing components may trigger admin notifications or change-tracking alerts
Detection/coverage: Monitor Weblate add-on activation events in the audit log.
STEP 04

Craft a malicious filename to inject hg arguments

The attacker commits a file to the Mercurial repository with a name starting with -, such as --config=hooks.pre-cat=<shell command>. When Weblate calls hg cat <filename> without a -- separator, Mercurial interprets the filename as a --config flag and executes the injected hook. The payload runs as the Weblate service account (typically weblate or www-data).
Conditions required:
  • Write access to the Mercurial repository backing the component
  • Knowledge of Mercurial argument injection techniques (well-documented since CVE-2022-23915)
Where this breaks in practice:
  • Attacker must control or write to the upstream Mercurial repo — not just the Weblate UI
  • Some Weblate deployments use read-only repo clones
Detection/coverage: File-integrity monitoring on the Weblate working directory for filenames starting with -. EDR/auditd on the Weblate host for unexpected child processes of the Weblate worker.
STEP 05

Trigger repository update to fire the payload

The attacker triggers a repository update (manually or waits for the scheduled poll). Weblate processes the RESX component, calls HgRepository.get_file() on the malicious filename, and the injected Mercurial config hook executes the shell command with the privileges of the Weblate service account. The attacker achieves RCE on the Weblate host.
Conditions required:
  • Repository update cycle must run (automatic or manual trigger)
Where this breaks in practice:
  • If the Weblate worker is containerized with a read-only filesystem or restricted seccomp profile, post-exploitation is limited
  • Network segmentation may limit lateral movement from the Weblate host
Detection/coverage: Process monitoring for unexpected commands spawned by the Weblate worker process. Container runtime security (Falco, Sysdig) for anomalous syscalls.
03 · Compensating Control

1
MEDIUM 5.5→IGNORE 0.0
SEVERITY REDUCED
Remove or disable the Update RESX files add-on on all Mercurial-backed components — This breaks the trigger mechanism entirely — without this add-on, the vulnerable HgRepository.get_file() code path is never reached during repository processing. This is the single most effective workaround and can be applied in minutes via the Weblate admin UI. No mitigation SLA applies at MEDIUM — go straight to the 365-day remediation window, but this workaround is trivial enough to deploy immediately.
2
MEDIUM 5.5→IGNORE 0.0
SEVERITY REDUCED
Uninstall Mercurial (hg) from the Weblate server — If the hg binary is not present, Weblate cannot use the Mercurial backend at all, eliminating the entire attack surface. Migrate any Mercurial-backed components to Git first. Most Weblate deployments already use Git exclusively.
3
MEDIUM 5.5→LOW 3.0
SEVERITY REDUCED
Restrict component.edit permission to trusted administrators only — Audit and tighten Weblate's role assignments. Remove component.edit from all non-admin users and service accounts. This prevents the attacker from creating or modifying components to set up the attack chain. Review via Weblate Admin → Groups → Roles.
4
MEDIUM 5.5→IGNORE 0.0
SEVERITY REDUCED
Upgrade to Weblate 2026.8+ — The definitive fix. The patch adds a -- option terminator before filenames in all Mercurial command invocations, ensuring dash-prefixed filenames are treated as literal paths. Deploy within the 365-day noisgate remediation SLA for MEDIUM findings, though the fix is a straightforward version bump.
What doesn't work
  • WAF rules — The injection occurs server-side during Mercurial command execution, not via HTTP request parameters. A WAF inspecting HTTP traffic will never see the malicious filename payload.
  • Network segmentation / firewall — The attacker exploits the vulnerability through legitimate Weblate UI actions (editing components, pushing to repos). Blocking network ports does not prevent authenticated abuse of application functionality.
  • Read-only container filesystem — While this limits *post-exploitation* actions, the initial command injection still fires. The attacker can still exfiltrate data or establish reverse shells via network I/O even without filesystem writes.
04 · Intelligence Metadata

The supporting signals.

In-the-Wild ExploitationNo evidence. Not listed on CISA KEV. No known campaigns or threat-actor usage as of 2026-10-01. Disclosed only 2 days ago.
Proof-of-ConceptNo public PoC. Checked pocindex.io, GitHub, ExploitDB — no dedicated repos or exploit code for CVE-2026-86035. The technique is well-understood from CVE-2022-23915 and generic Mercurial argument injection research, lowering the bar for a skilled attacker to write one. Reporter: Shawky0-0 via HackerOne.
EPSS ScoreNot yet scored. FIRST EPSS API returns empty data — expected for a CVE disclosed <72 hours ago. Expect a score in the next model update cycle.
KEV StatusNot listed. No CISA KEV entry as of 2026-10-01.
CVSS VectorCVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:C/C:H/I:H/A:H — Network-accessible, high complexity, low privilege, no user interaction, changed scope. The S:C (changed scope) reflects that RCE on the Weblate host can affect systems beyond Weblate itself (e.g., connected repositories).
Affected VersionsWeblate ≥ 4.11.1 and < 2026.8 (approximately 4.5 years of releases). Docker images, pip installs, and distro packages all affected.
Fixed VersionWeblate 2026.8 (commit f60a975, PR #20768). NixOS backport: nixpkgs#568728. No other distro backports observed yet.
Internet ExposureWeblate is used by ~2,500+ projects across 165 countries per vendor claims. No Shodan/Censys dork returns significant exposure data specific to Weblate. Most instances sit behind authentication and are not directly indexed. The subset using Mercurial+RESX is a fraction of a fraction.
Disclosure TimelineDisclosed 2026-09-29 via GitHub Security Advisory GHSA-327h-qqgm-qv55. Fix released same day in Weblate 2026.8.
Researcher / ReporterShawky0-0 (GitHub) via HackerOne. Fix by nijel (Weblate maintainer).

Sources.

  1. GitHub Security Advisory GHSA-327h-qqgm-qv55
  2. HackerOne Report #3874004
  3. NixOS Backport PR #568728
  4. NVD — CVE-2022-23915 (Original Incomplete Fix)
  5. Strix AI — CVE-2026-86035 Analysis
  6. The Hacker Wire — CVE-2026-86035 Analysis & Fix
  7. Weblate Version Control Integration Docs
05 · The Call

Final Verdict
↓ DOWNGRADED to MEDIUM (5.5/10)

Why this verdict

  • Mercurial backend is near-extinct friction: Mercurial adoption has collapsed since Bitbucket dropped support in 2020. Well under 1% of new software projects use Mercurial, and Weblate's own documentation and community overwhelmingly reference Git workflows. The fraction of Weblate instances with a functioning Mercurial backend is likely in the low single-digit percentages, dramatically narrowing the vulnerable population.
  • RESX add-on compounds the narrowing: Even among the rare Mercurial-backed instances, the attack requires the .NET RESX file format AND the Update RESX files add-on to be enabled. RESX is specific to .NET resource bundles — irrelevant to the web, mobile, and Python/JS projects that dominate Weblate's user base. This second prerequisite reduces the reachable set to a near-negligible fraction.
  • Authentication + elevated permission required: The attacker needs component.edit — a project-management-tier permission, not the default translator role. This rules out opportunistic unauthenticated exploitation entirely and restricts the threat to insiders or compromised privileged accounts.
  • AC:H already embedded in vendor score: The vendor's own CVSS vector sets Attack Complexity to High, acknowledging that exploitation requires specific conditions. The deployment-layer prerequisites (Mercurial + RESX + add-on) add further real-world complexity that CVSS cannot represent.
  • Role multiplier: Weblate is a localization platform — it does NOT canonically occupy a high-value role (not an IdP, hypervisor, CI/CD engine, backup system, PAM, CA, or network edge appliance). When the chain succeeds, blast radius is host-level: the attacker gets a shell as the Weblate service account. Lateral pivot to connected repos is *possible* but requires the service account to hold write credentials to production repositories with no branch protections — an additional failure mode outside this CVE. Weblate does not meet the ≥1% high-value-role threshold that would floor the verdict at HIGH.

Why not higher?

The chain *does* end in RCE, and Weblate service accounts often hold repository credentials (SSH keys, tokens) that could enable lateral movement to source code repositories. If Weblate is deployed as part of a CI/CD pipeline with write access to production repos, the blast radius could extend to supply-chain impact. However, this extended scenario requires multiple additional failures beyond the CVE itself (write-capable creds, no branch protection, no code review), and Weblate is not canonically a CI/CD component. The RCE potential prevents dropping below MEDIUM.

Why not lower?

Despite the narrow prerequisite chain, the vulnerability is a real OS command injection that delivers full RCE when triggered. The technique is well-documented from CVE-2022-23915, meaning a motivated attacker with access to a qualifying instance could weaponize it quickly. The 4.5-year affected version window means many unpatched instances exist. Dropping to LOW would understate the impact for the (small) population of instances that do meet all prerequisites.

06 · Verification

Crowdsourced verification payload.

Run on the Weblate host (or inside the Weblate container) as any user with read access to the Python environment. Example: bash check_cve_2026_86035.sh. No elevated privileges required.

noisgate-verify.sh
BASHREAD-ONLYSAFE
#!/usr/bin/env bash
# check_cve_2026_86035.sh — Detect CVE-2026-86035 (Weblate Mercurial argument injection)
# Run on the Weblate host or inside its container.
# Exit codes: 0 = VULNERABLE, 1 = PATCHED, 2 = UNKNOWN

set -euo pipefail

VULN_FLOOR="4.11.1"
FIXED="2026.8"

# Attempt to get Weblate version
VERSION=""
if command -v weblate &>/dev/null; then
  VERSION=$(weblate --version 2>/dev/null | grep -oP '[0-9]+\.[0-9]+(\.[0-9]+)?' | head -1)
elif python3 -c 'import weblate; print(weblate.VERSION_BASE)' 2>/dev/null; then
  VERSION=$(python3 -c 'import weblate; print(weblate.VERSION_BASE)' 2>/dev/null)
elif pip3 show weblate 2>/dev/null | grep -q Version; then
  VERSION=$(pip3 show weblate 2>/dev/null | grep '^Version:' | awk '{print $2}')
fi

if [ -z "$VERSION" ]; then
  echo "UNKNOWN — Weblate not detected on this host."
  exit 2
fi

echo "Detected Weblate version: $VERSION"

# Check if Mercurial is installed (attack prerequisite)
HG_PRESENT="no"
if command -v hg &>/dev/null; then
  HG_PRESENT="yes"
  echo "WARNING: Mercurial (hg) is installed — attack prerequisite met."
else
  echo "INFO: Mercurial (hg) not installed — attack prerequisite NOT met."
fi

# Version comparison using sort -V
version_gte() {
  [ "$(printf '%s\n' "$1" "$2" | sort -V | head -1)" = "$2" ]
}

version_lt() {
  [ "$(printf '%s\n' "$1" "$2" | sort -V | head -1)" = "$1" ] && [ "$1" != "$2" ]
}

if version_lt "$VERSION" "$VULN_FLOOR"; then
  echo "PATCHED — Version $VERSION predates the vulnerable range (4.11.1+)."
  exit 1
elif version_gte "$VERSION" "$FIXED"; then
  echo "PATCHED — Version $VERSION includes the fix (>= $FIXED)."
  exit 1
else
  echo "VULNERABLE — Version $VERSION is in the affected range ($VULN_FLOOR to <$FIXED)."
  if [ "$HG_PRESENT" = "no" ]; then
    echo "NOTE: Mercurial is not installed, so exploitation is not currently possible."
  fi
  exit 0
fi
Peer Review

What defenders are saying.

Submit a review attribution: handle + country only
0 flags selected · stored anonymously