** Description changed:
- == Summary ==
-
- sudo-rs ≤ 0.2.13 (the version Ubuntu 26.04 ships as the default
/usr/bin/sudo)
- silently drops argument restrictions and trailing rule content when a
sudoers
- rule uses the standard `\<newline>` line-continuation syntax inside a
- SimpleCommand token. A narrow rule like:
+ [ Impact ]
+
+ One feature of the sudoers file, is that it can define argument restrictions
to
+ a command, e.g. the caller is only allowed to run 'systemctl' with sudo iff it
+ is invoked with specifically with the argument 'restart myapp.service', but no
+ other arguments.
+
+ sudo-rs will currently drop all argument restrictions that follow a
+ `\<newline>`:
deploy ALL=(root) NOPASSWD: /usr/bin/systemctl \
restart myapp.service
- is loaded as `(root) NOPASSWD: /usr/bin/systemctl` — i.e. systemctl with
- arbitrary args — granting local privilege escalation via any shell-escapable
- command (systemctl edit, find -exec, awk, etc.). visudo-rs flags the input
- ("garbage at end of line") and reports "invalid sudoers file", but sudo-rs
- commits the rule prefix to the active ruleset anyway.
-
- This has been disclosed to the upstream sudo-rs maintainers
- ([email protected]) on 2026-05-11. Filing here to also reach Ubuntu
- Security via Launchpad — the `[email protected]` email alias is currently
- bouncing for unknown-recipient `[email protected]` (separate
alias-
- routing issue that probably needs fixing on Canonical's MTA).
-
- == Affected ==
-
- * Ubuntu 26.04 LTS shipping sudo-rs 0.2.13-0ubuntu1 as default /usr/bin/sudo
- * Ubuntu 25.10 (same sudo-rs default switch)
- * All sudo-rs releases v0.2.5 through v0.2.13. Earlier versions if parser
code
- unchanged.
- * Other distros packaging sudo-rs are also affected when sudo-rs is used as
- the sudo backend.
-
- == Status of upstream fix ==
-
- * Parser-level fix exists: commit 7780f593 on `main` ("parser: implement
line
- continuation (fix #1571)", authored 2026-04-27).
- * Fix is NOT in any release tag. Latest release v0.2.13 (2026-03-10)
predates
- the fix and is the version Ubuntu 26.04 ships.
- * Upstream issue #1571 was filed and closed (2026-05-01) as a non-security
- parser bug; the LPE-class security implication of pre-fix releases does
not
- appear to have been recognized in the report.
- * The parser fix is necessary but incomplete as defense-in-depth. The rule
- loader commits the rule prefix to the active ruleset BEFORE the rest of
- the rule is parsed, so any other parse error on a continuation line leaves
- an over-permissive prefix active. C sudo's yacc grammar discards the
- entire rule on any per-rule parse error — sudo-rs should match this.
- == Reproducer (Ubuntu 26.04, default sudo) ==
-
- sudo tee /etc/sudoers.d/repro <<'EOF'
- testuser ALL=(ALL) NOPASSWD: /bin/echo \
- hello world
- EOF
-
- sudo -l -U testuser
- # /etc/sudoers.d/repro:3:4: garbage at end of line
- # hello world
- # ^
- # ...
- # User testuser may run the following commands:
- # (ALL) NOPASSWD: /usr/bin/echo
- # ^^^^^^^^^^^^^ argument restriction silently
dropped
-
- sudo -u testuser sudo -n /bin/echo MALICIOUS_NOT_HELLO_WORLD
- # MALICIOUS_NOT_HELLO_WORLD <-- succeeds, no password
-
- C sudo on the same input (Debian's /usr/bin/sudo 1.9.16p2) correctly rejects
- the rule.
-
- End-to-end kara→root LPE PoC is attached (`exploit.sh`). It creates a
- throwaway unprivileged user, plants a vulnerable sudoers.d rule (admin
- intent: lowpriv runs `/usr/bin/find /tmp/target` only), and demonstrates
- the unprivileged user reading /etc/shadow via `find -exec` running as root,
- then auto-cleans up. Lab-verified on Ubuntu 26.04 sudo-rs 0.2.13-0ubuntu1
- on 2026-05-11.
-
- == Suggested fix (defense-in-depth) ==
-
- The parser fix 7780f593 is the right immediate change. Additionally:
-
- 1. Buffer the entire rule before commit. Parse a rule into a tentative AST
- in src/sudoers/parse.rs; only commit to the active ruleset after the
- full rule (across all continuation lines) parses successfully.
-
- 2. Treat per-rule parse errors as fatal for that rule. When a rule's parse
- fails partway through, discard the partially-parsed prefix and resume
- with the next top-level statement.
-
- == Attachments ==
-
- * reproduce.sh — parser-level repro showing the abbreviated rule
- * exploit.sh — full LPE end-to-end demo (find -exec /bin/sh shell-escape);
- idempotent cleanup; gated behind --i-understand
- * run-on-lab.sh — multi-distro harness (we tested Debian/Alma/Fedora/Ubuntu)
- * Ubuntu-26.04.log — successful LPE transcript (find -exec ran as
- uid=0(root) gid=0(root); /etc/shadow first lines readable)
- * README.md — full writeup including affected-version matrix and the
- variant-B Cmnd_Alias analysis
-
- == References ==
-
- * Upstream: https://github.com/trifectatechfoundation/sudo-rs
- * Upstream issue #1571 (closed 2026-05-01, classified `bug`/non-security):
- https://github.com/trifectatechfoundation/sudo-rs/issues/1571
- * Fix commit 7780f593 (2026-04-27, "parser: implement line continuation"):
- https://github.com/trifectatechfoundation/sudo-rs/commit/7780f593
- * v0.2.13 release (does not contain the fix):
- https://github.com/trifectatechfoundation/sudo-rs/releases/tag/v0.2.13
- * C sudo behavior reference: https://www.sudo.ws/docs/man/sudoers.man/
- * Upstream disclosure: emailed [email protected] on 2026-05-11 — copy
- available on request.
-
- Filed by Kara Zajac, independent security researcher.
- github.com/KaraZajac · [email protected]
+ In this example, the "restart myapp.service" restriction is dropped, and users
+ can call systemctl with arbitrary commands. The visudo command will report
+ "invalid sudoers file", however the rule is still committed to sudo-rs.
+
+ The justification for backporting this fix comes from the fact that the error
+ can lead to unintended consequences. An administrator may want to allow users
+ to restart a system service, however if they incorrectly format the rule as
+ shown above, they will unknowingly be granting users the ability to
arbitrarily
+ modify any service.
+
+ This is fixed by the upload by making the following change to the sudo-rs's
+ token parser:
+
+ * If '\' is followed by '\n', check whether the the next char is a space
+ * If it is a space, treat it as line continuation (emit a space)
+ * Otherwise, reject() and treat it as the end token
+
+ [ Test Plan ]
+
+ * Detailed instructions on how to reproduce the bug.
+
+ * These should allow someone who is not familiar with the affected
+ package to reproduce the bug and verify that the updated package
+ fixes the problem.
+
+ * If other testing is appropriate to perform before landing this
+ update, this should also be described here.
+
+ Reproduce the bug
+ -----------------
+
+ * Assert that sudo-rs is the current sudo provider:
+
+ sudo --version
+
+ # Output:
+ # sudo-rs 0.2.13-0ubuntu1
+ # ~~~~~~~
+
+ * Create a test user:
+ sudo useradd -m -s /bin/bash resolute
+
+ * Create a sudoers file with an affected rule that applies to the user:
+
+ sudo tee /etc/sudoers.d/apt-update >/dev/null <<'EOF'
+ resolute ALL=(root) NOPASSWD: /usr/bin/apt \
+ update
+ EOF
+
+ * Change to the test user:
+
+ su resolute
+
+ * Verify that the test user is not able to use sudo with any other
+ command:
+
+ resolute@ubuntu:~$ sudo echo hello
+
+ # Output:
+ # /etc/sudoers.d/apt-update:2:5: garbage at end of line
+ # update
+ # ^
+ # sudo: I'm sorry resolute. I'm afraid I can't do that
+
+ * Run 'apt' against an argument outside the rule restriction (remember we
+ should only be allowed to run 'sudo apt update'):
+
+ resolute@ubuntu:~$ sudo apt install cowsay
+
+ # Output:
+ # /etc/sudoers.d/apt-update:2:5: garbage at end of line
+ # update
+ # ^
+ # Installing:
+ # cowsay
+ #
+ # Suggested packages:
+ # filters cowsay-off
+ #
+ # Summary:
+ # Upgrading: 0, Installing: 1, Removing: 0, Not Upgrading: 131
+ # Download size: 18.1 kB
+ # Space needed: 89.1 kB / 268 GB available
+
+ * Verify that the command was executed successfully despite it being outside
+ the allowed arguments
+
+ Testing proposed
+ ----------------
+
+ * Install the proposed sudo-rs:
+
+ sudo apt install -t resolute-proposed rust-sudo-rs
+
+ * Verify that the user "resolute" is no longer able to run any other command
+ with sudo than 'apt update':
+
+ resolute@ubuntu:~$ sudo apt install cowsay
+
+ # Output:
+ # sudo: I'm sorry resolute. I'm afraid I can't do that
+
+ resolute@ubuntu:~$ sudo apt update
+
+ # Output:
+ # Hit:1 http://security.ubuntu.com/ubuntu resolute-security InRelease
+ # Hit:3 http://archive.ubuntu.com/ubuntu resolute InRelease
+ # Hit:4 http://archive.ubuntu.com/ubuntu resolute-updates InRelease
+ # Hit:5 http://archive.ubuntu.com/ubuntu resolute-backports InRelease
+ # Hit:6 http://archive.ubuntu.com/ubuntu resolute-proposed InRelease
+
+ [ Where problems could occur ]
+
+ * The primary risks associated with this change is how it affects the generic
+ token parse function which is used by all token types, not just command line
+ lists. One example where this can have side-effects is when a token has
+ `ALLOW_ESCAPE = true`, there is a risk that two rules might be joined even
+ if they are separate rules.
+
+ * Previously broken sudoers files will now parse differently. For instance if
+ an administrator has specified `command + arg` in the sudoers file in the
+ affected way, that would previously have allowed all arguments. The argument
+ restriction is now honored correctly with the fix, which may be surprising,
+ even though it is technically correct.
+
+ [ Other Info ]
+
+ * The bug was first marked as a believed vulnerability, however this was
+ decided not to be the case as it requires sudo access in the first place
+ to edit the sudoers file.
+
+ * This SRU is part of a larger merge proposal for 26.04.1
+
+ * The fix is also natively part of the 0.2.14-1 upload to Stonking, currently
+ in proposed-migration
+
+
+ [ Original Description ]
+
+ == Summary ==
+
+ sudo-rs ≤ 0.2.13 (the version Ubuntu 26.04 ships as the default
/usr/bin/sudo)
+ silently drops argument restrictions and trailing rule content when a
sudoers
+ rule uses the standard `\<newline>` line-continuation syntax inside a
+ SimpleCommand token. A narrow rule like:
+
+ deploy ALL=(root) NOPASSWD: /usr/bin/systemctl \
+ restart myapp.service
+
+ is loaded as `(root) NOPASSWD: /usr/bin/systemctl` — i.e. systemctl with
+ arbitrary args — granting local privilege escalation via any shell-escapable
+ command (systemctl edit, find -exec, awk, etc.). visudo-rs flags the input
+ ("garbage at end of line") and reports "invalid sudoers file", but sudo-rs
+ commits the rule prefix to the active ruleset anyway.
+
+ This has been disclosed to the upstream sudo-rs maintainers
+ ([email protected]) on 2026-05-11. Filing here to also reach Ubuntu
+ Security via Launchpad — the `[email protected]` email alias is currently
+ bouncing for unknown-recipient `[email protected]` (separate
alias-
+ routing issue that probably needs fixing on Canonical's MTA).
+
+ == Affected ==
+
+ * Ubuntu 26.04 LTS shipping sudo-rs 0.2.13-0ubuntu1 as default /usr/bin/sudo
+ * Ubuntu 25.10 (same sudo-rs default switch)
+ * All sudo-rs releases v0.2.5 through v0.2.13. Earlier versions if parser
code
+ unchanged.
+ * Other distros packaging sudo-rs are also affected when sudo-rs is used as
+ the sudo backend.
+
+ == Status of upstream fix ==
+
+ * Parser-level fix exists: commit 7780f593 on `main` ("parser: implement
line
+ continuation (fix #1571)", authored 2026-04-27).
+ * Fix is NOT in any release tag. Latest release v0.2.13 (2026-03-10)
predates
+ the fix and is the version Ubuntu 26.04 ships.
+ * Upstream issue #1571 was filed and closed (2026-05-01) as a non-security
+ parser bug; the LPE-class security implication of pre-fix releases does
not
+ appear to have been recognized in the report.
+ * The parser fix is necessary but incomplete as defense-in-depth. The rule
+ loader commits the rule prefix to the active ruleset BEFORE the rest of
+ the rule is parsed, so any other parse error on a continuation line leaves
+ an over-permissive prefix active. C sudo's yacc grammar discards the
+ entire rule on any per-rule parse error — sudo-rs should match this.
+ == Reproducer (Ubuntu 26.04, default sudo) ==
+
+ sudo tee /etc/sudoers.d/repro <<'EOF'
+ testuser ALL=(ALL) NOPASSWD: /bin/echo \
+ hello world
+ EOF
+
+ sudo -l -U testuser
+ # /etc/sudoers.d/repro:3:4: garbage at end of line
+ # hello world
+ # ^
+ # ...
+ # User testuser may run the following commands:
+ # (ALL) NOPASSWD: /usr/bin/echo
+ # ^^^^^^^^^^^^^ argument restriction silently
dropped
+
+ sudo -u testuser sudo -n /bin/echo MALICIOUS_NOT_HELLO_WORLD
+ # MALICIOUS_NOT_HELLO_WORLD <-- succeeds, no password
+
+ C sudo on the same input (Debian's /usr/bin/sudo 1.9.16p2) correctly rejects
+ the rule.
+
+ End-to-end kara→root LPE PoC is attached (`exploit.sh`). It creates a
+ throwaway unprivileged user, plants a vulnerable sudoers.d rule (admin
+ intent: lowpriv runs `/usr/bin/find /tmp/target` only), and demonstrates
+ the unprivileged user reading /etc/shadow via `find -exec` running as root,
+ then auto-cleans up. Lab-verified on Ubuntu 26.04 sudo-rs 0.2.13-0ubuntu1
+ on 2026-05-11.
+
+ == Suggested fix (defense-in-depth) ==
+
+ The parser fix 7780f593 is the right immediate change. Additionally:
+
+ 1. Buffer the entire rule before commit. Parse a rule into a tentative AST
+ in src/sudoers/parse.rs; only commit to the active ruleset after the
+ full rule (across all continuation lines) parses successfully.
+
+ 2. Treat per-rule parse errors as fatal for that rule. When a rule's parse
+ fails partway through, discard the partially-parsed prefix and resume
+ with the next top-level statement.
+
+ == Attachments ==
+
+ * reproduce.sh — parser-level repro showing the abbreviated rule
+ * exploit.sh — full LPE end-to-end demo (find -exec /bin/sh shell-escape);
+ idempotent cleanup; gated behind --i-understand
+ * run-on-lab.sh — multi-distro harness (we tested Debian/Alma/Fedora/Ubuntu)
+ * Ubuntu-26.04.log — successful LPE transcript (find -exec ran as
+ uid=0(root) gid=0(root); /etc/shadow first lines readable)
+ * README.md — full writeup including affected-version matrix and the
+ variant-B Cmnd_Alias analysis
+
+ == References ==
+
+ * Upstream: https://github.com/trifectatechfoundation/sudo-rs
+ * Upstream issue #1571 (closed 2026-05-01, classified `bug`/non-security):
+ https://github.com/trifectatechfoundation/sudo-rs/issues/1571
+ * Fix commit 7780f593 (2026-04-27, "parser: implement line continuation"):
+ https://github.com/trifectatechfoundation/sudo-rs/commit/7780f593
+ * v0.2.13 release (does not contain the fix):
+ https://github.com/trifectatechfoundation/sudo-rs/releases/tag/v0.2.13
+ * C sudo behavior reference: https://www.sudo.ws/docs/man/sudoers.man/
+ * Upstream disclosure: emailed [email protected] on 2026-05-11 — copy
+ available on request.
+
+ Filed by Kara Zajac, independent security researcher.
+ github.com/KaraZajac · [email protected]
** Summary changed:
- [security] sudo-rs ≤ 0.2.13: silent drop of argument restrictions on
\<newline> line continuation (LPE)
+ [SRU] sudo-rs ≤ 0.2.13: silent drop of argument restrictions on \<newline>
line continuation (LPE)
** Description changed:
[ Impact ]
One feature of the sudoers file, is that it can define argument restrictions
to
a command, e.g. the caller is only allowed to run 'systemctl' with sudo iff it
is invoked with specifically with the argument 'restart myapp.service', but no
other arguments.
sudo-rs will currently drop all argument restrictions that follow a
`\<newline>`:
- deploy ALL=(root) NOPASSWD: /usr/bin/systemctl \
- restart myapp.service
+ deploy ALL=(root) NOPASSWD: /usr/bin/systemctl \
+ restart myapp.service
In this example, the "restart myapp.service" restriction is dropped, and users
can call systemctl with arbitrary commands. The visudo command will report
"invalid sudoers file", however the rule is still committed to sudo-rs.
The justification for backporting this fix comes from the fact that the error
can lead to unintended consequences. An administrator may want to allow users
to restart a system service, however if they incorrectly format the rule as
shown above, they will unknowingly be granting users the ability to
arbitrarily
modify any service.
This is fixed by the upload by making the following change to the sudo-rs's
token parser:
* If '\' is followed by '\n', check whether the the next char is a space
* If it is a space, treat it as line continuation (emit a space)
* Otherwise, reject() and treat it as the end token
[ Test Plan ]
- * Detailed instructions on how to reproduce the bug.
-
- * These should allow someone who is not familiar with the affected
- package to reproduce the bug and verify that the updated package
- fixes the problem.
-
- * If other testing is appropriate to perform before landing this
- update, this should also be described here.
-
Reproduce the bug
-----------------
* Assert that sudo-rs is the current sudo provider:
sudo --version
# Output:
# sudo-rs 0.2.13-0ubuntu1
# ~~~~~~~
* Create a test user:
sudo useradd -m -s /bin/bash resolute
* Create a sudoers file with an affected rule that applies to the user:
sudo tee /etc/sudoers.d/apt-update >/dev/null <<'EOF'
resolute ALL=(root) NOPASSWD: /usr/bin/apt \
- update
+ update
EOF
* Change to the test user:
su resolute
* Verify that the test user is not able to use sudo with any other
command:
resolute@ubuntu:~$ sudo echo hello
# Output:
# /etc/sudoers.d/apt-update:2:5: garbage at end of line
# update
# ^
# sudo: I'm sorry resolute. I'm afraid I can't do that
* Run 'apt' against an argument outside the rule restriction (remember we
- should only be allowed to run 'sudo apt update'):
+ should only be allowed to run 'sudo apt update'):
resolute@ubuntu:~$ sudo apt install cowsay
# Output:
# /etc/sudoers.d/apt-update:2:5: garbage at end of line
# update
# ^
# Installing:
# cowsay
#
# Suggested packages:
# filters cowsay-off
#
# Summary:
# Upgrading: 0, Installing: 1, Removing: 0, Not Upgrading: 131
# Download size: 18.1 kB
# Space needed: 89.1 kB / 268 GB available
* Verify that the command was executed successfully despite it being outside
- the allowed arguments
+ the allowed arguments
Testing proposed
----------------
* Install the proposed sudo-rs:
sudo apt install -t resolute-proposed rust-sudo-rs
* Verify that the user "resolute" is no longer able to run any other command
- with sudo than 'apt update':
+ with sudo than 'apt update':
resolute@ubuntu:~$ sudo apt install cowsay
# Output:
# sudo: I'm sorry resolute. I'm afraid I can't do that
resolute@ubuntu:~$ sudo apt update
# Output:
# Hit:1 http://security.ubuntu.com/ubuntu resolute-security InRelease
# Hit:3 http://archive.ubuntu.com/ubuntu resolute InRelease
# Hit:4 http://archive.ubuntu.com/ubuntu resolute-updates InRelease
# Hit:5 http://archive.ubuntu.com/ubuntu resolute-backports InRelease
# Hit:6 http://archive.ubuntu.com/ubuntu resolute-proposed InRelease
[ Where problems could occur ]
* The primary risks associated with this change is how it affects the generic
- token parse function which is used by all token types, not just command line
- lists. One example where this can have side-effects is when a token has
- `ALLOW_ESCAPE = true`, there is a risk that two rules might be joined even
- if they are separate rules.
+ token parse function which is used by all token types, not just command line
+ lists. One example where this can have side-effects is when a token has
+ `ALLOW_ESCAPE = true`, there is a risk that two rules might be joined even
+ if they are separate rules.
* Previously broken sudoers files will now parse differently. For instance if
- an administrator has specified `command + arg` in the sudoers file in the
- affected way, that would previously have allowed all arguments. The argument
- restriction is now honored correctly with the fix, which may be surprising,
- even though it is technically correct.
+ an administrator has specified `command + arg` in the sudoers file in the
+ affected way, that would previously have allowed all arguments. The argument
+ restriction is now honored correctly with the fix, which may be surprising,
+ even though it is technically correct.
[ Other Info ]
* The bug was first marked as a believed vulnerability, however this was
- decided not to be the case as it requires sudo access in the first place
- to edit the sudoers file.
+ decided not to be the case as it requires sudo access in the first place
+ to edit the sudoers file.
* This SRU is part of a larger merge proposal for 26.04.1
* The fix is also natively part of the 0.2.14-1 upload to Stonking, currently
- in proposed-migration
-
+ in proposed-migration
[ Original Description ]
== Summary ==
sudo-rs ≤ 0.2.13 (the version Ubuntu 26.04 ships as the default
/usr/bin/sudo)
silently drops argument restrictions and trailing rule content when a
sudoers
rule uses the standard `\<newline>` line-continuation syntax inside a
SimpleCommand token. A narrow rule like:
deploy ALL=(root) NOPASSWD: /usr/bin/systemctl \
restart myapp.service
is loaded as `(root) NOPASSWD: /usr/bin/systemctl` — i.e. systemctl with
arbitrary args — granting local privilege escalation via any shell-escapable
command (systemctl edit, find -exec, awk, etc.). visudo-rs flags the input
("garbage at end of line") and reports "invalid sudoers file", but sudo-rs
commits the rule prefix to the active ruleset anyway.
This has been disclosed to the upstream sudo-rs maintainers
([email protected]) on 2026-05-11. Filing here to also reach Ubuntu
Security via Launchpad — the `[email protected]` email alias is currently
bouncing for unknown-recipient `[email protected]` (separate
alias-
routing issue that probably needs fixing on Canonical's MTA).
== Affected ==
* Ubuntu 26.04 LTS shipping sudo-rs 0.2.13-0ubuntu1 as default /usr/bin/sudo
* Ubuntu 25.10 (same sudo-rs default switch)
* All sudo-rs releases v0.2.5 through v0.2.13. Earlier versions if parser
code
unchanged.
* Other distros packaging sudo-rs are also affected when sudo-rs is used as
the sudo backend.
== Status of upstream fix ==
* Parser-level fix exists: commit 7780f593 on `main` ("parser: implement
line
continuation (fix #1571)", authored 2026-04-27).
* Fix is NOT in any release tag. Latest release v0.2.13 (2026-03-10)
predates
the fix and is the version Ubuntu 26.04 ships.
* Upstream issue #1571 was filed and closed (2026-05-01) as a non-security
parser bug; the LPE-class security implication of pre-fix releases does
not
appear to have been recognized in the report.
* The parser fix is necessary but incomplete as defense-in-depth. The rule
loader commits the rule prefix to the active ruleset BEFORE the rest of
the rule is parsed, so any other parse error on a continuation line leaves
an over-permissive prefix active. C sudo's yacc grammar discards the
entire rule on any per-rule parse error — sudo-rs should match this.
== Reproducer (Ubuntu 26.04, default sudo) ==
sudo tee /etc/sudoers.d/repro <<'EOF'
testuser ALL=(ALL) NOPASSWD: /bin/echo \
hello world
EOF
sudo -l -U testuser
# /etc/sudoers.d/repro:3:4: garbage at end of line
# hello world
# ^
# ...
# User testuser may run the following commands:
# (ALL) NOPASSWD: /usr/bin/echo
# ^^^^^^^^^^^^^ argument restriction silently
dropped
sudo -u testuser sudo -n /bin/echo MALICIOUS_NOT_HELLO_WORLD
# MALICIOUS_NOT_HELLO_WORLD <-- succeeds, no password
C sudo on the same input (Debian's /usr/bin/sudo 1.9.16p2) correctly rejects
the rule.
End-to-end kara→root LPE PoC is attached (`exploit.sh`). It creates a
throwaway unprivileged user, plants a vulnerable sudoers.d rule (admin
intent: lowpriv runs `/usr/bin/find /tmp/target` only), and demonstrates
the unprivileged user reading /etc/shadow via `find -exec` running as root,
then auto-cleans up. Lab-verified on Ubuntu 26.04 sudo-rs 0.2.13-0ubuntu1
on 2026-05-11.
== Suggested fix (defense-in-depth) ==
The parser fix 7780f593 is the right immediate change. Additionally:
1. Buffer the entire rule before commit. Parse a rule into a tentative AST
in src/sudoers/parse.rs; only commit to the active ruleset after the
full rule (across all continuation lines) parses successfully.
2. Treat per-rule parse errors as fatal for that rule. When a rule's parse
fails partway through, discard the partially-parsed prefix and resume
with the next top-level statement.
== Attachments ==
* reproduce.sh — parser-level repro showing the abbreviated rule
* exploit.sh — full LPE end-to-end demo (find -exec /bin/sh shell-escape);
idempotent cleanup; gated behind --i-understand
* run-on-lab.sh — multi-distro harness (we tested Debian/Alma/Fedora/Ubuntu)
* Ubuntu-26.04.log — successful LPE transcript (find -exec ran as
uid=0(root) gid=0(root); /etc/shadow first lines readable)
* README.md — full writeup including affected-version matrix and the
variant-B Cmnd_Alias analysis
== References ==
* Upstream: https://github.com/trifectatechfoundation/sudo-rs
* Upstream issue #1571 (closed 2026-05-01, classified `bug`/non-security):
https://github.com/trifectatechfoundation/sudo-rs/issues/1571
* Fix commit 7780f593 (2026-04-27, "parser: implement line continuation"):
https://github.com/trifectatechfoundation/sudo-rs/commit/7780f593
* v0.2.13 release (does not contain the fix):
https://github.com/trifectatechfoundation/sudo-rs/releases/tag/v0.2.13
* C sudo behavior reference: https://www.sudo.ws/docs/man/sudoers.man/
* Upstream disclosure: emailed [email protected] on 2026-05-11 — copy
available on request.
Filed by Kara Zajac, independent security researcher.
github.com/KaraZajac · [email protected]
** Description changed:
[ Impact ]
One feature of the sudoers file, is that it can define argument restrictions
to
a command, e.g. the caller is only allowed to run 'systemctl' with sudo iff it
is invoked with specifically with the argument 'restart myapp.service', but no
other arguments.
sudo-rs will currently drop all argument restrictions that follow a
`\<newline>`:
deploy ALL=(root) NOPASSWD: /usr/bin/systemctl \
restart myapp.service
In this example, the "restart myapp.service" restriction is dropped, and users
can call systemctl with arbitrary commands. The visudo command will report
"invalid sudoers file", however the rule is still committed to sudo-rs.
The justification for backporting this fix comes from the fact that the error
can lead to unintended consequences. An administrator may want to allow users
to restart a system service, however if they incorrectly format the rule as
shown above, they will unknowingly be granting users the ability to
arbitrarily
modify any service.
This is fixed by the upload by making the following change to the sudo-rs's
token parser:
* If '\' is followed by '\n', check whether the the next char is a space
* If it is a space, treat it as line continuation (emit a space)
* Otherwise, reject() and treat it as the end token
[ Test Plan ]
Reproduce the bug
-----------------
* Assert that sudo-rs is the current sudo provider:
sudo --version
# Output:
# sudo-rs 0.2.13-0ubuntu1
# ~~~~~~~
* Create a test user:
+
sudo useradd -m -s /bin/bash resolute
* Create a sudoers file with an affected rule that applies to the user:
sudo tee /etc/sudoers.d/apt-update >/dev/null <<'EOF'
resolute ALL=(root) NOPASSWD: /usr/bin/apt \
update
EOF
* Change to the test user:
su resolute
* Verify that the test user is not able to use sudo with any other
command:
resolute@ubuntu:~$ sudo echo hello
# Output:
# /etc/sudoers.d/apt-update:2:5: garbage at end of line
# update
# ^
# sudo: I'm sorry resolute. I'm afraid I can't do that
* Run 'apt' against an argument outside the rule restriction (remember we
should only be allowed to run 'sudo apt update'):
resolute@ubuntu:~$ sudo apt install cowsay
# Output:
# /etc/sudoers.d/apt-update:2:5: garbage at end of line
# update
# ^
# Installing:
# cowsay
#
# Suggested packages:
# filters cowsay-off
#
# Summary:
# Upgrading: 0, Installing: 1, Removing: 0, Not Upgrading: 131
# Download size: 18.1 kB
# Space needed: 89.1 kB / 268 GB available
* Verify that the command was executed successfully despite it being outside
the allowed arguments
Testing proposed
----------------
* Install the proposed sudo-rs:
sudo apt install -t resolute-proposed rust-sudo-rs
* Verify that the user "resolute" is no longer able to run any other command
with sudo than 'apt update':
resolute@ubuntu:~$ sudo apt install cowsay
# Output:
# sudo: I'm sorry resolute. I'm afraid I can't do that
resolute@ubuntu:~$ sudo apt update
# Output:
# Hit:1 http://security.ubuntu.com/ubuntu resolute-security InRelease
# Hit:3 http://archive.ubuntu.com/ubuntu resolute InRelease
# Hit:4 http://archive.ubuntu.com/ubuntu resolute-updates InRelease
# Hit:5 http://archive.ubuntu.com/ubuntu resolute-backports InRelease
# Hit:6 http://archive.ubuntu.com/ubuntu resolute-proposed InRelease
[ Where problems could occur ]
* The primary risks associated with this change is how it affects the generic
token parse function which is used by all token types, not just command line
lists. One example where this can have side-effects is when a token has
`ALLOW_ESCAPE = true`, there is a risk that two rules might be joined even
if they are separate rules.
* Previously broken sudoers files will now parse differently. For instance if
an administrator has specified `command + arg` in the sudoers file in the
affected way, that would previously have allowed all arguments. The argument
restriction is now honored correctly with the fix, which may be surprising,
even though it is technically correct.
[ Other Info ]
* The bug was first marked as a believed vulnerability, however this was
decided not to be the case as it requires sudo access in the first place
to edit the sudoers file.
* This SRU is part of a larger merge proposal for 26.04.1
* The fix is also natively part of the 0.2.14-1 upload to Stonking, currently
in proposed-migration
[ Original Description ]
== Summary ==
sudo-rs ≤ 0.2.13 (the version Ubuntu 26.04 ships as the default
/usr/bin/sudo)
silently drops argument restrictions and trailing rule content when a
sudoers
rule uses the standard `\<newline>` line-continuation syntax inside a
SimpleCommand token. A narrow rule like:
deploy ALL=(root) NOPASSWD: /usr/bin/systemctl \
restart myapp.service
is loaded as `(root) NOPASSWD: /usr/bin/systemctl` — i.e. systemctl with
arbitrary args — granting local privilege escalation via any shell-escapable
command (systemctl edit, find -exec, awk, etc.). visudo-rs flags the input
("garbage at end of line") and reports "invalid sudoers file", but sudo-rs
commits the rule prefix to the active ruleset anyway.
This has been disclosed to the upstream sudo-rs maintainers
([email protected]) on 2026-05-11. Filing here to also reach Ubuntu
Security via Launchpad — the `[email protected]` email alias is currently
bouncing for unknown-recipient `[email protected]` (separate
alias-
routing issue that probably needs fixing on Canonical's MTA).
== Affected ==
* Ubuntu 26.04 LTS shipping sudo-rs 0.2.13-0ubuntu1 as default /usr/bin/sudo
* Ubuntu 25.10 (same sudo-rs default switch)
* All sudo-rs releases v0.2.5 through v0.2.13. Earlier versions if parser
code
unchanged.
* Other distros packaging sudo-rs are also affected when sudo-rs is used as
the sudo backend.
== Status of upstream fix ==
* Parser-level fix exists: commit 7780f593 on `main` ("parser: implement
line
continuation (fix #1571)", authored 2026-04-27).
* Fix is NOT in any release tag. Latest release v0.2.13 (2026-03-10)
predates
the fix and is the version Ubuntu 26.04 ships.
* Upstream issue #1571 was filed and closed (2026-05-01) as a non-security
parser bug; the LPE-class security implication of pre-fix releases does
not
appear to have been recognized in the report.
* The parser fix is necessary but incomplete as defense-in-depth. The rule
loader commits the rule prefix to the active ruleset BEFORE the rest of
the rule is parsed, so any other parse error on a continuation line leaves
an over-permissive prefix active. C sudo's yacc grammar discards the
entire rule on any per-rule parse error — sudo-rs should match this.
== Reproducer (Ubuntu 26.04, default sudo) ==
sudo tee /etc/sudoers.d/repro <<'EOF'
testuser ALL=(ALL) NOPASSWD: /bin/echo \
hello world
EOF
sudo -l -U testuser
# /etc/sudoers.d/repro:3:4: garbage at end of line
# hello world
# ^
# ...
# User testuser may run the following commands:
# (ALL) NOPASSWD: /usr/bin/echo
# ^^^^^^^^^^^^^ argument restriction silently
dropped
sudo -u testuser sudo -n /bin/echo MALICIOUS_NOT_HELLO_WORLD
# MALICIOUS_NOT_HELLO_WORLD <-- succeeds, no password
C sudo on the same input (Debian's /usr/bin/sudo 1.9.16p2) correctly rejects
the rule.
End-to-end kara→root LPE PoC is attached (`exploit.sh`). It creates a
throwaway unprivileged user, plants a vulnerable sudoers.d rule (admin
intent: lowpriv runs `/usr/bin/find /tmp/target` only), and demonstrates
the unprivileged user reading /etc/shadow via `find -exec` running as root,
then auto-cleans up. Lab-verified on Ubuntu 26.04 sudo-rs 0.2.13-0ubuntu1
on 2026-05-11.
== Suggested fix (defense-in-depth) ==
The parser fix 7780f593 is the right immediate change. Additionally:
1. Buffer the entire rule before commit. Parse a rule into a tentative AST
in src/sudoers/parse.rs; only commit to the active ruleset after the
full rule (across all continuation lines) parses successfully.
2. Treat per-rule parse errors as fatal for that rule. When a rule's parse
fails partway through, discard the partially-parsed prefix and resume
with the next top-level statement.
== Attachments ==
* reproduce.sh — parser-level repro showing the abbreviated rule
* exploit.sh — full LPE end-to-end demo (find -exec /bin/sh shell-escape);
idempotent cleanup; gated behind --i-understand
* run-on-lab.sh — multi-distro harness (we tested Debian/Alma/Fedora/Ubuntu)
* Ubuntu-26.04.log — successful LPE transcript (find -exec ran as
uid=0(root) gid=0(root); /etc/shadow first lines readable)
* README.md — full writeup including affected-version matrix and the
variant-B Cmnd_Alias analysis
== References ==
* Upstream: https://github.com/trifectatechfoundation/sudo-rs
* Upstream issue #1571 (closed 2026-05-01, classified `bug`/non-security):
https://github.com/trifectatechfoundation/sudo-rs/issues/1571
* Fix commit 7780f593 (2026-04-27, "parser: implement line continuation"):
https://github.com/trifectatechfoundation/sudo-rs/commit/7780f593
* v0.2.13 release (does not contain the fix):
https://github.com/trifectatechfoundation/sudo-rs/releases/tag/v0.2.13
* C sudo behavior reference: https://www.sudo.ws/docs/man/sudoers.man/
* Upstream disclosure: emailed [email protected] on 2026-05-11 — copy
available on request.
Filed by Kara Zajac, independent security researcher.
github.com/KaraZajac · [email protected]
--
You received this bug notification because you are a member of Ubuntu
Bugs, which is subscribed to Ubuntu.
https://bugs.launchpad.net/bugs/2152220
Title:
[SRU] sudo-rs ≤ 0.2.13: silent drop of argument restrictions on
\<newline> line continuation (LPE)
To manage notifications about this bug go to:
https://bugs.launchpad.net/ubuntu/+source/rust-sudo-rs/+bug/2152220/+subscriptions
--
ubuntu-bugs mailing list
[email protected]
https://lists.ubuntu.com/mailman/listinfo/ubuntu-bugs