September 26, 2026
CVE-2026-16287: The privilege boundary in Pardus Update
How a passwordless updater role reached a root-owned script through an unchecked configuration argument.

By Ahmet Sadık Şahiner
4 min read
Ahmet Sadık Şahiner · Independent Security Researcher & Information Technology Specialist
Research record: CVE-2026–16287 · TÜBİTAK BİLGEM pardus-update · CWE-78 · CVSS v3.1 7.8 High (AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H). The CNA lists 0.6.6 through versions before 0.7.0 as affected. The preserved dynamic test used 0.6.6 on Pardus 25.1. Public references checked 26 September 2026. Official CVE record.
TL;DR
The vulnerable helper accepted a free-form askconf argument and inserted it into an executable Bash template. A local member of the pardus-update group could reach that helper through the installed polkit rule. The resulting update script was written by root and intended to execute through the offline system-update service. The preserved controlled test recorded a root identity canary.
This is local privilege escalation. The demonstrated prerequisite is membership in the passwordless updater group; the evidence does not establish a remote attack path.
Product context and threat model
An updater must cross a privilege boundary to install system packages. Pardus Update exposed an "Enable Passwordless Usage" setting associated with pardus-update group membership. In the captured 0.6.6 source, the group rule returned polkit.Result.YES for the broad pardus-update-sysaction action. The policy bound the executable path to SysActions.py; it did not constrain its subcommands or argument values.
The distinction matters: permission to operate an updater should have a narrower contract than permission to supply shell syntax to a root script. The GUI normally produced configuration choices, but a caller could invoke the privileged interface directly. Validation therefore needed to live in the helper.
Root cause: data became executable text
The source-to-sink capture documents the complete chain:
- A local group member supplied arguments to
SysActions.py. - Dispatch passed
sys.argv[3]intodistupgradeofflineasaskconf. - The helper substituted the value with
contents.replace("@@askconf@@", askconf). - The template used that placeholder inside a double-quoted
Dpkg::Optionsvalue. - The helper wrote the generated script and set mode
0755. - A system service pointed
ExecStartat that script; the service template contained noUser=override.
The argument was treated as configuration by the caller and as executable text by the eventual shell. A restrictive GUI could not enforce the missing helper contract.
Controlled proof and negative control
The 10 July 2026 evidence uses the installed helper and polkit configuration in a disposable Pardus VM. The lab harness redirected update-related filesystem paths and APT, systemd and reboot effects into a private mount namespace with inert targets. It exercised the generated script without performing a real system upgrade or reboot. This is the scope of the dynamic proof; the production boot-time execution route is additionally established by the captured service source.
The positive transcript records:
POC_MODE proof
GENERATED_SCRIPT uid=0 gid=0 mode=755
CANARY uid=0(root) gid=0(root) groups=0(root)
CANARY_MARKER_COUNT 1
REAL_APT_UNCHANGED yes
REAL_SYSTEMD_UNCHANGED yesPOC_MODE proof
GENERATED_SCRIPT uid=0 gid=0 mode=755
CANARY uid=0(root) gid=0(root) groups=0(root)
CANARY_MARKER_COUNT 1
REAL_APT_UNCHANGED yes
REAL_SYSTEMD_UNCHANGED yesThe matching control used the ordinary --force-confnew input. It generated a root-owned script but recorded CANARY absent and CANARY_MARKER_COUNT 0. Holding the harness constant while changing the argument supports attribution to the template substitution path, rather than an unconditional canary in the privileged wrapper.
The post-run integrity capture also records root ownership of the generated artifacts, absence of generated files at the real host paths, and unchanged hashes for the installed helper, template and rule. The environment capture establishes group membership. A separate runtime transcript showing rejection of a non-member was not located, so that rejection is not presented here as an observed control.
Patch analysis
The inspected 0.7.0 helper accepts new or old, maps them to fixed dpkg options, and exits for an invalid value before generating the script. The template replacement receives the mapped value, askconf_arg, rather than the caller's unrestricted string. The 0.7.0 changelog documents argument validation and further action/policy changes.
The official affected-range boundary is 0.7.0. Patch inspection supports the enum-based correction; this package does not contain a full dynamic retest of the original controlled chain on 0.7.0. Those are separate claims.
Impact and defensive lessons
The observed canary proves root-context command execution in the controlled environment. The CNA's high confidentiality, integrity and availability assessment describes the potential privilege escalation impact; the test itself only wrote an identity canary.
The durable design lesson is to give privileged interfaces a small, typed contract. Validate direct calls, reject unknown values before privileged writes, and keep configuration as data where possible. Review policy grants together with every subcommand they expose: an executable-level grant can authorize more behavior than its user-facing name suggests.
Timeline and disclosure scope
- 10 July 2026: preserved version, source, positive/control and integrity captures.
- 23 July 2026: official CVE publication; researcher credit names Ahmet Sadık Şahiner.
- 26 September 2026: publication evidence and public-reference review.
The package describes an isolated, marker-only test. It records no production exploitation, reverse shell or persistence. A fresh exploit run was not performed during this publication review.
References
- Official CVE record and CNA metadata
- Pardus Update 0.7.0 privileged helper
- Pardus Update 0.7.0 changelog
- Evidence: controlled laboratory records retained by the author; selected results are shown below.
Related research
See Liman package-queue authorization for another boundary with a distinct trust contract.
Evidence and technical diagrams
The following proof panel reproduces supplied historical records. The remaining figures are conceptual explanations of the analysis above.
Real evidence: positive and negative controls from an isolated Pardus test. Exact historical excerpts; source hashes shown.
Conceptual attack chain: the updater role reaches executable root-owned script text through unchecked askconf.
Conceptual root cause: constrained configuration choices versus free-form executable template substitution.
Conceptual patch comparison: 0.7.0 maps new/old to fixed options. A full patched-version dynamic retest is not claimed.