# Root Cause Analysis: CVE-2026-4480 — Samba Printing Subsystem OS Command Injection via %J

## Summary

CVE-2026-4480 is an unauthenticated remote code execution vulnerability in Samba's printing subsystem. When a Samba server is configured with a non-CUPS/non-iPrint printing backend (e.g., `printing = sysv`) and a `print command` that includes the `%J` substitution character (job description), a remote attacker can submit a print job whose document name contains shell metacharacters. Samba substitutes the client-controlled job name into the `print command` string and executes it via `system()` with only partial sanitization — the characters `$ \` " ' ; %` are stripped, but `& ( ) # | < >` and others survive, enabling OS command injection and arbitrary code execution as the Samba service account.

## Impact

- **Package/component affected:** Samba `smbd` printing subsystem — `source3/printing/print_generic.c`, function `generic_job_submit()` → `print_run_command()` → `smbrun_no_sanitize()` → `system()`
- **Affected versions:** All Samba versions prior to 4.22.10, 4.23.8, and 4.24.3 (released 26 May 2026). Verified vulnerable on 4.22.9.
- **Risk level:** Critical (CVSS 3.1 base 10.0 — AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H). Unauthenticated, remote, default guest print access.
- **Consequences:** Arbitrary OS command execution with the privileges of the Samba service (typically `nobody` or the configured `guest account`). Full server compromise possible.

## Impact Parity

- **Disclosed/claimed maximum impact:** Remote code execution (unauthenticated, network-reachable)
- **Reproduced impact from this run:** Remote code execution confirmed — the `id` command executed on the Samba server and its output (`uid=65534(nobody) gid=65534(nogroup) groups=65534(nogroup)`) was captured in the server-side log, plus an arbitrary file (`marker`) was created in the spool directory by the injected `touch` command. All via an unauthenticated guest SMB connection over TCP 445.
- **Parity:** `full`
- **Not demonstrated:** A full reverse shell or privilege escalation to root was not demonstrated (the Samba guest account runs as `nobody`), but the core claim of unauthenticated remote code execution is fully reproduced.

## Root Cause

### Vulnerable code path

When a client opens a file on a printable SMB share, `smbd` processes it as a print job:

1. `smbd/open.c` → `print_spool_open(fsp, filename, vuid)` — the client-supplied filename becomes the document name (`docname = "Remote Downlevel Document <filename>"`)
2. `print_spool_open()` → `print_job_start(docname)` → `fstrcpy(pjob.jobname, docname)` — the docname is stored as the job name with **no sanitization**
3. On file close → `print_job_end()` → `generic_job_submit()`
4. `generic_job_submit()` in `source3/printing/print_generic.c`:
   ```c
   jobname = talloc_strdup(ctx, pjob->jobname);
   jobname = talloc_string_sub(ctx, jobname, "'", "_");  // only replaces ' → _
   ...
   ret = print_run_command(snum, ..., lp_print_command(snum), NULL,
       "%s", p, "%J", jobname, ...);
   ```
5. `print_run_command()` substitutes `%J` with the jobname using `talloc_string_sub()`:
   ```c
   syscmd = talloc_string_sub(ctx, syscmd, arg, value);  // arg="%J", value=jobname
   ```
6. `talloc_string_sub()` calls `talloc_string_sub2()` with `remove_unsafe_characters=true`, which replaces only `$ \` " ' ; % \r \n` with `_` in the **insert** (jobname) string
7. The resulting command string is passed to `smbrun_no_sanitize()` → `execl("/bin/sh", "sh", "-c", cmd, NULL)` — **no shell escaping**

### The gap

The `talloc_string_sub2()` unsafe-character list is:
```c
case '$': case '`': case '"': case '\'': case ';': case '%': case '\r': case '\n':
```

This misses critical shell metacharacters: `&`, `|`, `<`, `>`, `(`, `)`, `#`, space, `!`, etc. The `&` character (and `&&`) is a valid command separator in POSIX shells and is **not** in the sanitized set. An attacker can inject `&&` in the job description to chain arbitrary commands.

### The fix (commit `b80131fcf582`)

The fix in Samba 4.22.10 adds `replace_print_cmd_J()` which:
- Defines `STRING_SUB_UNSAFE_CHARACTERS "$\`\"';%|&<>"` (includes `&`, `|`, `<`, `>`)
- Masks ALL unsafe characters (plus `/` and `\`) to `_`
- Wraps the `%J` substitution in single quotes (or falls back to a fixed `__CVE-2026-4480_FallbackJobname__` string for mixed-quoting configurations)
- Pre-substitutes `%J` in the print command before passing to `print_run_command`, removing the raw `%J` → jobname path

Fix commit: `b80131fcf582ecc8e8c1b97e6051bb324bb8bef8` (Samba master), backported to 4.22.10, 4.23.8, 4.24.3.

## Reproduction Steps

1. **Reference:** `bundle/repro/reproduction_steps.sh`
2. **What the script does:**
   - Builds vulnerable Samba 4.22.9 and fixed Samba 4.22.10 from source (or reuses cached builds from the project cache)
   - Configures `smbd` with `printing = sysv`, a `printcap` entry for printer `testprn`, and a `print command` referencing unquoted `%J`: `(echo %J) > /tmp/samba_printlog 2>&1`
   - Starts the real `smbd` daemon listening on TCP port 445 with guest-accessible printing
   - Uses an impacket-based Python SMB client to connect over TCP 445 (guest/anonymous login), open a file named `PWN&&id&&touch marker&&END` on the `[testprn]` printable share, write print data, and close it
   - The server substitutes the document name (becomes `%J`) into the print command and executes it via `system()` — the `&&` survives sanitization, causing `id` and `touch marker` to execute
   - Repeats the same exploit against the fixed Samba 4.22.10 as a negative control
3. **Expected evidence of reproduction:**
   - **Vulnerable (4.22.9):** `/tmp/samba_printlog` contains `uid=65534(nobody)...` (output of injected `id` command) and a `marker` file is created in the spool directory
   - **Fixed (4.22.10):** `/tmp/samba_printlog` contains the literal masked string `PWN__id__touch marker__END` (no `uid=`, no command execution) and no `marker` file is created

## Evidence

### Vulnerable Samba 4.22.9 — RCE confirmed

**Print log** (`bundle/artifacts/smb-vuln/printlog.txt`):
```
Remote Downlevel Document PWN
uid=65534(nobody) gid=65534(nogroup) groups=65534(nogroup)
sh: 1: END: not found
```
The `uid=65534(nobody)` line is the output of the injected `id` command — **arbitrary code execution proven**.

**Marker file** (`bundle/artifacts/smb-vuln/marker_check.txt`):
```
-rw-rw-rw- 1 nobody nogroup 0 Jul 12 21:47 /tmp/sambatest/spool/marker
```
Created by the injected `touch marker` command running as `nobody` (the Samba guest account).

**smbd log** (`bundle/artifacts/smb-vuln/smbd_command_log.txt`):
```
Running the command `(echo Remote Downlevel Document PWN&&id&&touch marker&&END) > /tmp/samba_printlog 2>&1' gave 127
```
The `&&` is intact in the executed command — no shell escaping applied.

### Fixed Samba 4.22.10 — injection blocked (negative control)

**Print log** (`bundle/artifacts/smb-fixed/printlog.txt`):
```
Remote Downlevel Document PWN__id__touch marker__END
```
The `&&` was masked to `__` and the jobname was wrapped in single quotes — **no command execution**.

**Marker file** (`bundle/artifacts/smb-fixed/marker_check.txt`):
```
no marker (correct)
```

**smbd log** (`bundle/artifacts/smb-fixed/smbd_command_log.txt`):
```
Running the command `(echo 'Remote Downlevel Document PWN__id__touch marker__END') > /tmp/samba_printlog_fixed 2>&1' gave 0
```
The jobname is single-quoted and masked — safe execution.

### Environment
- Samba built from source: `samba-4.22.9` (vulnerable, commit `ff3dd69`) and `samba-4.22.10` (fixed, commit `0abface`)
- Build: `--bundled-libraries=ALL --without-ad-dc --disable-python` (standalone file/print server)
- OS: Ubuntu 26.04 LTS, 32 cores
- Client: impacket 0.13.1 SMBConnection over TCP 445, guest/anonymous authentication

## Recommendations / Next Steps

1. **Upgrade immediately** to Samba 4.22.10, 4.23.8, or 4.24.3 (or later)
2. **Remove `%J`** from `print command` in `smb.conf`, or wrap it in single quotes (`'%J'`) as a temporary mitigation
3. **Switch to CUPS** (`printing = cups`) — CUPS/iPrint backends bypass the vulnerable `generic_job_submit()` path
4. **Disable guest printer access** — require authentication for print shares
5. **Audit** existing Samba deployments for `print command` entries containing `%J`

## Additional Notes

- **Idempotency:** The reproduction script cleans all state directories between runs and re-creates them fresh. Each vulnerable/fixed test uses a separate base directory.
- **SMB filename restrictions:** The classic SMB print path restricts the filename (and thus the jobname) to characters valid in SMB/CIFS names (no `/ \ : * ? " < > |`). The `&` character is allowed in SMB filenames and is not sanitized by the vulnerable `talloc_string_sub()`, making it the key injection vector. The spoolss RPC path (`StartDocPrinter` with `pDocName`) is not subject to SMB filename restrictions and allows even more characters, but the same `&` injection works through both paths.
- **Why `&&` and not `;` or `$()`:** The vulnerable `talloc_string_sub2()` sanitizes `;`, `$`, and backtick but NOT `&`. The `&&` operator is a valid POSIX shell command separator that passes through the sanitization, enabling command chaining.
- **The `echo %J >> file` vs `(echo %J) > file 2>&1` difference:** The subshell in the print command captures all output (including the injected commands' stdout/stderr) in the log file, providing clear RCE evidence.
