CVE-2026-71168
| Field | Value |
|---|---|
| CVE | CVE-2026-71168 |
| Advisory | DSA-2026-324 (2026-10-01) |
| Product | Dell System Update (DSU) for Linux, versions prior to 2.3.0.0 |
| Tested | DSU 2.2.0.1 A00, usr/lib/dsulib/libdsmcabinet.so |
| Class | CWE-22 path traversal, arbitrary root write, local privilege escalation (lab) |
| Dell CVSS v3.1 | 7.3 AV:L/AC:L/PR:L/UI:R/S:U/C:H/I:H/A:H |
Executive summary. Dell System Update is a root-owned Linux update agent whose job includes unpacking vendor archives. In DSU 2.2, libdsmcabinet's MinUnzipImpl::Extract does chdir(dest) and then opens zip member names verbatim. There is no path jail. Cipher Security Labs demonstrated an end-to-end lab chain: unprivileged plant of a zip, privileged extract via the vendor library, write outside dest as root, and local privilege escalation via a root-sourced file. Dell assigned CVE-2026-71168, credited Cipher Security Labs, and remediated the issue in DSU 2.3.0.0 under DSA-2026-324.
1. Introduction
Dell System Update applies Dell Update Packages and catalog content, typically elevated. If unpack is not path-jailed, extract is a root write primitive.
In DSU 2.2, MinUnzipImpl::Extract changes into the destination directory and opens each zip member name as a relative path against that cwd. ZIP member names may contain relative path components. A secure extractor must explicitly reject traversal outside the destination directory. chdir is not that jail.
- Bug: unsafe extraction (CWE-22).
- Primitive: arbitrary file write as root.
- Demonstrated impact: local privilege escalation via a root-sourced file in the tested Linux environment (
/etc/profile.das an execution sink).
We demonstrated the exploitation chain end-to-end at the vulnerable extractor boundary: unprivileged plant of a zip, root MinUnzipImpl::Open/Extract via a dlopen harness against libdsmcabinet.so, write outside dest, then an unprivileged user with euid=0 after the lab sourced the dropped script. Marker: LPE_SHELL:OK.
That proves the extractor is unsafe and that a root write can be turned into LPE with a chosen gadget. It does not prove that every DSU deployment exposes the same placement or trigger. The harness models in-product callers (FetchAndApplyUpdate / catalog extract / ExtractZip); it is not a full DSU apply workflow or GUI session. Customer catalog pipelines are an operations question, not a second bug.
The interesting part is not the class name. It is that the sink still sat in privileged systems-management tooling, and that we could prove it on a large installer by calling the vendor .so directly.
2. Background
DSU's distinctive surface is parsers of vendor artifacts: zip, cabinet-adjacent libraries, XML catalogs. Vendors treat extract as plumbing behind signatures. Attackers treat unzip as a parser of bytes. If those bytes can be influenced - writable staging, a side-loaded DUP, a shared drop, an admin applying a supplied package - extract policy is the boundary. A signature on an archive does not jail member paths.
The unit of patching is libdsmcabinet's extract path.
3. Method
Software-only lab (dell-lab). No BMC, no live iDRAC. Library under test: libdsmcabinet.so from DSU 2.2.0.1 A00.
Static. MinUnzipImpl::Extract at approximately 0x5f90 performs chdir(dest) then opens the entry path with no traversal check. Other DSU wrappers may normalize; this function does not. A safe caller does not redeem an unsafe callee if any apply path still reaches Extract.
Dynamic (proof boundary). A harness uses dlopen on the .so and calls construct, Open, Extract, and destroy. It allocates a 4096-byte aligned object, passes zip and dest as std::string, and prints return codes. We used that shape because DSU is a large installer: proving that this .so writes escaped members as root is the claim. Non-claim: every customer GUI always reaches that .so. The PSIRT report named the callers we believed were relevant; we did not pretend the harness is those callers.
Successful construction, extraction, and file creation provide practical evidence that the harness object layout was sufficient for the tested calls.
Negative control. The same wave tried the same idea against DSU's tar unpacker (DSMTAR::Extract). The crafted ustar returned -1. We did not report it. Format parsers diverge on magic, path rules, and early rejection. Assuming zip and tar share a jail is how you ship false positives. That failure is part of the result, not an omitted footnote.
4. Root cause
MinUnzipImpl lives in libdsmcabinet.so. The backing parser is in the minizip family. Zip-the-format is not the vulnerability. Extract policy is.
Two steps:
chdirto the destination (ambient cwd becomes the intended jail).- Open each zip member name as a relative path against that cwd.
There is no rejection of .., no rejection of absolute members, no realpath prefix check, and no openat from a directory fd rooted at dest.
Reconstructed policy, not a decompiler dump:
Extract(dest):
chdir(dest) # ambient cwd := dest
for each member in zip:
open(member.local_name) # relative to cwd; name is attacker-controlled
write payload
A correct extractor never uses ambient cwd as a sandbox. Recommended policy (what we would accept in review - not a claim about Dell's 2.3.0.0 binary, which we have not reversed):
Extract(dest):
dirfd = open(dest, O_DIRECTORY | O_RDONLY)
for each member in zip:
rel = canonicalize_zip_name(member.local_name)
if rel is absolute or contains ".." or NUL:
fail closed
fd = openat(dirfd, rel, O_WRONLY|O_CREAT|O_EXCL|O_NOFOLLOW)
write payload
chdir plus open loses the directory fd. The kernel will create a traversed path if the process is root and parents exist. Linux does not implement "you chdir'd here, therefore you may not walk out." That is userspace policy. DSU 2.2 did not implement it in Extract.
realpath(dest + "/" + member) is still fragile (symlink dest, missing parents). The robust version is openat from a held fd, component by component.
In 2.2 the "is this path under dest?" check is missing: Open zip → chdir dest → open member name verbatim → write outside dest as uid 0. A correct extractor fails closed when the member path escapes.
CWE-22 is the extractor bug. A later root-sourced file is impact, not a second CWE in unzip.
Open=0
Extract=0
Those two lines are the dynamic proof that Dell's implementation accepted the archive and wrote members.
5. Threat model
| Actor | Has | Does not need |
|---|---|---|
| Local unprivileged user | Ability to plant or control the zip that will be passed to Extract (staging, shared drop, supplied package) | Privileged DSU itself |
| Trigger | Privileged MinUnzipImpl::Open then Extract | — |
UI:R is real. Someone or something privileged must extract that zip. An admin pointing DSU at an operator-supplied package is user interaction. Unattended automation from writable staging raises practical risk. We have no fleet telemetry. The PSIRT report named "staged update package / shared extract input."
Dell's advisory text says the issue can lead to "Remote execution," at 7.3 with AV:L. Trust the vector string. This write-up: local arbitrary file write as root, then local code execution if a root-sourced gadget is used.
If DSU only ever extracts Dell-built archives from a root-only directory, planting is hard. The bug in Extract remains. Practical exploitability therefore depends on both the vulnerable sink and attacker influence over the extracted input.
6. Exploitation chain (lab)
Logic bug, not memory corruption: hostile member name into Extract as root, then a write target that can become code.
Phase A - plant. Unprivileged zip whose member path walks out of dest. Member construction is not published.
Phase B - privileged extract (the primitive). Root Extract writes through the escaped path. First confirmation: /tmp/DSU_ZIPSLIP_PWN as uid 0.
=== Wave6 cascade 2026-07-24T18:57:14Z uid=0 ===
--- DSU zip extract ---
Open=0
Extract=0
REACHABILITY:OK
VULN CONFIRMED: MinUnzipImpl::Extract wrote /tmp/DSU_ZIPSLIP_PWN as uid=0
The same log records DSMTAR::Extract=-1.
A later probe wrote /etc/cron.d/dell_dsu_zipslip_poc as uid 0. That is still only a write: cron needs valid crontab syntax.
Phase C - execution sink (gadget, not the bug). In the tested Linux environment, /etc/profile.d/*.sh was a clean gadget: a shell body the lab then sourced as root. We do not claim that every orchestration path on every distro sources that directory. The lab installed a setuid helper as a stand-in; the snippet is redacted.
Phase D - LPE. Unprivileged nobody obtained euid=0:
=== escalate DSU zip extract ===
Open=0
Extract=0
profile_drop: [REDACTED - root-sourced shell snippet omitted]
REACHABILITY:OK
VULN CONFIRMED: path traversal to profile.d, root code exec, mode=6755
LPE_SHELL:OK nobody euid=0
This is not a no-interaction LPE unless automation consumes the zip. Other DSU wrappers may normalize; MinUnzipImpl::Extract does not.
| Member class | Intended | Actual if unsanitized |
|---|---|---|
payload.bin | under dest | same |
escaped .. into /etc/profile.d/ | under dest | /etc/profile.d/<name>.sh as uid 0 |
escaped .. into /tmp/ (wave6 probe) | under dest | /tmp/DSU_ZIPSLIP_PWN as uid 0 |
7. The tar unpacker
The tar unpacker (DSMTAR::Extract) looked like the same class of bug. Dynamically, Extract returned -1 on the crafted ustar, so we did not report a tar path-traversal issue. Format parsers diverge: magic, path rules, early rejection. Assuming zip and tar share a jail is how you ship false positives. Keep that next to the zip result: one extractor wrote escaped members; the other refused the analogous tar.
At disclosure we checked published DSU issues covering certificate, credential, DoS, and search-path bugs. None described path traversal in this MinUnzipImpl sink.
8. Advisory and scoring
Remediation: DSU 2.3.0.0 or later (driver J9TK1).
Dell's published CVSS v3.1 is 7.3: AV:L/AC:L/PR:L/UI:R/S:U/C:H/I:H/A:H. UI:R matches a privileged extract of attacker-influenced input. PR:L means a local account that is not already root - the interesting case for an update agent. Unattended staging from a writable drop can make the operational risk feel higher than a wizard click; that does not change the root cause.
Patch internals. We have not reversed libdsmcabinet.so from 2.3.0.0. A correct fix jails Extract (or uses a safe unzip API on every caller). A weak fix wraps one GUI path and leaves the .so as unsafe as 2.2. That question needs a diff. We will not invent one. The openat sketch in section 4 is a review bar, not a description of Dell's patch.
Acknowledgement: "CVE-2026-71168: Dell would like to thank Nir Yehoshua of Cipher Security Labs for reporting this issue."
9. Fixing privileged extractors
Engineering requirements, independent of whatever 2.3.0.0 actually contains:
- Never
chdir+ relativeopenas a sandbox. - Rooted
openat.O_DIRECTORYfd ondest; reject.., absolute paths, NULs;O_NOFOLLOWor extract-then-renameatin-jail. - Fail closed on odd extra fields, overlong names, and outbound symlinks.
- Private staging (root-owned
0700), then rename - do not extract onto live/etc. - Signatures do not jail paths. Path policy is a separate fail-closed check.
- Regression tests for
..walks, absolute members, symlink dest, long chains. This product's tar path already rejected our crafted ustar; zip must fail closed too.
Defenders. Upgrade DSU versions before 2.3.0.0. Audit writable staging used by DSU. Hunt unexpected root-owned files under /etc/profile.d.
10. Broader lesson
Systems-management suites concentrate root plus parsers. The playbook was archive-path extract on DSU, not a new memory-corruption primitive.
A memory-safe language would not have saved this. It is a policy bug. Rust open of a zip-supplied string after chdir is the same CVE.
dlopen of a vendor .so is a legitimate way to prove a sink on a 200 MB installer if the write-up labels harness versus in-product trigger.
11. Disclosure timeline
| Date | Event |
|---|---|
| 2026-07-24 | MinUnzipImpl::Extract root write confirmed; DSMTAR::Extract rejected the analogous tar case |
| 2026-07-24 | Lab escalation to euid=0 for an unprivileged user |
| 2026-08-01 | Revalidation; report submitted to Dell Product Security |
| 2026-10-01 | DSA-2026-324 / CVE-2026-71168; credit Cipher Security Labs; fix 2.3.0.0 |
| 2026-10-06 | This post |
12. Conclusion
MinUnzipImpl::Extractfailed to jail archive paths (chdir(dest)then a raw member open).- That is an arbitrary file write as root; in the lab it became LPE via a root-sourced file (
/etc/profile.din the tested environment). - Dell fixed DSU in 2.3.0.0. We have not reversed that library. Dell recommends upgrading to DSU 2.3.0.0 or later.
A memory-safe language would not have saved this. It is a policy bug.
References
- Dell Security Advisory DSA-2026-324 - Dell System Update, fixed in 2.3.0.0+.
- CVE record: CVE-2026-71168 (CWE-22).