Sudo NSS Library Hijack: From User to Root
Deep dive into Xpl0it — a PoC that exploits sudo's trust model when handling
dynamic library loading in chroot environments. How sudo -R + a crafted
nsswitch.conf delivers a root shell.
Overview
Xpl0it is a bash-based privilege escalation PoC I wrote targeting
a class of vulnerabilities in how sudo handles NSS (Name Service Switch)
library resolution when using the -R (chroot) flag.
When a user can execute sudo at all — even with a password prompt —
this technique can escalate to root by poisoning the library search path inside a
controlled chroot environment.
The tool automates the full attack chain: version fingerprinting, staging directory creation, malicious shared library generation + compilation, environment preparation, and execution. On success it drops you into a root shell or runs any command you specify.
This tool is published for educational and authorized security research only. Only run it on systems you own or have explicit written permission to test. The techniques described here are well-documented in CVE databases and used daily by red teams worldwide — understanding them is essential for defense.
The Vulnerability
Sudo's -R <dir> flag changes the root directory before executing
a command — essentially chroot(dir) + exec(cmd).
During this process, sudo must still resolve user and group information, which requires
loading NSS (Name Service Switch) libraries.
The critical flaw: sudo assumes that because it initiated the chroot, the environment
inside it is trusted. If an attacker controls the directory passed to -R,
they control the nsswitch.conf inside it — and therefore
which NSS libraries get loaded. A crafted nsswitch.conf
pointing to a malicious shared library means sudo loads attacker code
with root privileges before dropping them.
Affected Versions
Xpl0it checks the installed sudo version against this vulnerability database:
Exploit Chain
The attack runs through seven orchestrated stages:
sudo --version and match against CVE database.
Check sudo -l for NOPASSWD rules, env_keep, command restrictions.
mktemp -d sudobridge.stage.XXXXXX — creates isolated workspace.
Structures: bridge/etc/, libnss_/, build/, logs/.
Copies real /etc/passwd and /etc/group for legitimacy.
bridge/etc/nsswitch.conf with:
passwd: files /bridge90
— this tells NSS to resolve passwd entries through our custom library name.
bridge90.c with a GCC constructor function that fires
on library load: setreuid(0,0) + setregid(0,0)
+ execl("/bin/sh", ...). No main() needed.
gcc -shared -fPIC -Wl,-init,bridge -o libnss_/bridge90.so.2 bridge90.c
— position-independent shared object, init hook set to our constructor.
bridge/ dir, reads the poisoned
nsswitch.conf, loads libnss_bridge90.so.2 to resolve
user info — constructor executes as root.
uid=0 gid=0. On exit,
trap cleanup EXIT wipes the staging directory automatically.
The Malicious Library
The entire payload lives in a single C file generated at runtime.
The __attribute__((constructor)) annotation tells the dynamic linker to
run bridge() before any other code — the moment the library
is loaded by sudo's NSS resolution, root escalation fires:
Compiled with: gcc -shared -fPIC -Wl,-init,bridge -o libnss_/bridge90.so.2 bridge90.c
-shared— output is a shared library, not an executable-fPIC— position-independent code, required for shared objects-Wl,-init,bridge— sets the library init function tobridge()
The nsswitch.conf Trick
NSS uses /etc/nsswitch.conf to determine where to look up user/group data.
Each service name maps to a shared library: a service named foo loads
libnss_foo.so.2. The poisoned config file:
When sudo chroots into bridge/ and reads this config, the dynamic linker
searches for libnss_/bridge90.so.2 — exactly where we placed the
compiled payload. Library loads, constructor runs, root shell spawns.
Usage
Prerequisites
gcc— required to compile the NSS shared library on targetsudo— the target binary (versions listed above)make,grep,awk,sed— standard utils- Current user must be in the
sudogroup (can invoke sudo at all)
Why It Works
The root cause is a broken trust assumption in sudo's design:
sudo trusts the contents of the chroot directory it's pointed at.
Since the user provides the chroot path via -R, and sudo doesn't validate
the library configuration inside it, the attacker gets full control over
what code runs during NSS resolution — which happens before privilege checks complete.
Traditional sudo hardening (NOPASSWD restrictions, command allowlists, env_reset) does not prevent this attack. The library loading happens at a lower level than sudo's permission model operates.
Impact
- Full root shell — uid=0, gid=0, unrestricted filesystem access
- Bypasses sudo rules — NOPASSWD, command restrictions, env_reset all irrelevant
- Read any file —
/etc/shadow, SSH keys, credentials, secrets - Install persistent backdoors, add users, modify cron, alter SUID binaries
- Full lateral movement — pivot to anything accessible from the compromised host
Mitigation
- Update sudo immediately — patch to latest stable; most distro repos already have fixes
- Enable IMDSv2 on cloud instances to prevent metadata credential theft
- Deploy AppArmor or SELinux — MAC policies can block unauthorized library loading
- Audit
/etc/sudoers— remove users who don't strictly need sudo access - Mount writable dirs with
noexec,nosuidto prevent payload execution - Monitor for
sudo -Rinvocations in audit logs — it's rarely legitimate on production - Use
auditdto alert onsetreuid/setregidsyscalls from non-root processes
Source Code
The full tool is open source on GitHub. Pull it, audit it, test it in your lab, and use it to verify your systems are patched: