Advanced Namespace Tools blog 24 December 2016

Security Implications of Writable /proc/pid/ns?

A Sample Use-case

While investigating aspects of the 2nd edition of Plan 9 released back in 1995, I happened to have a good use for the ANTS kernel modification which allows process namespace to be modified by writing namespace operations to the /proc/pid/ns file. It was relatively trivial, but still a decent practical example.

The Plan9 dossrv utility is a server which runs in the background and gives you an entry in /srv, usually as /srv/dos. To use it to read a filesystem, you enter a command like:

mount /srv/dos /n/flop /usr/glenda/2nded.disk1

Which should result in you being able to read the files from 2nded.disk1 at /n/flop. Because I had started the dossrv in early boot to access files in the 9fat partition, it was not running in the full standard namespace, so I got this error:

mount: mount /n/flop: '/usr/glenda/2nd_ed.disk1' does not exist

The standard thing to do would probably be to start a new instance of dossrv within the standard namespace, so there would be two copies of it running with different pipes available in /srv. Rather than run an extra server, I did:

ps -a |grep dossrv
echo 'mount /srv/boot /net.alt' >/proc/120/ns
mount /srv/dos /n/flop /net.alt/usr/glenda/2nd_ed.disk1

In this example, net.alt is just a random unused mountpoint for dossrv to put the main filesystem into its namespace. This is one of the simplest ways that writable proc/ns is useful: it lets you modify the namespace of long-running background processes and services to access data that they couldn't reach otherwise.

Discussing Possible Security Implications

I mentioned this in IRC, and it provoked a discussion about whether or not this mechanism changes or violates any of the assumptions that underlie the Plan 9 security model. Many people have an instinctive feeling that it does - after all, usually a process is only permitted to modify its own namespace (and any other processes within the same namespace group as determined by how rfork is used). You can even import /proc from a remote machine and modify the namespace of remote processes, if they are owned by your user. Doesn't that open new holes in the security model?

I don't believe it does, because the Plan 9 security model already allows you to attach a debugger such as Acid to the /proc of remote machines, and directly control/modify running code and memory of processes you own. This is inherently more powerful and invasive than what the new proc/ns control interface allows. Assuming that things are coded correctly to follow the standard permissions model and special cases such as RFNOMNT and private memory setting, there shouldn't be any exploitable loopholes created by writing namespace commands to proc/pid/ns.

Special Treatment of 'None' User and a Tiny Patch

The discussion did lead to one change, triggered by the question of whether or not the kernel treats the user 'none' as a special case. I had a recollection of seeing some code in the kernel which did check for whether or not the user was 'none', and a quick grep showed this was the case for some things in devproc.c:

/*
 *  none can't read or write state on other
 *  processes.  This is to contain access of
 *  servers running as none should they be
 *  subverted by, for example, a stack attack.
 */
static void
nonone(Proc *p)
{
	if(p == up)
		return;
	if(strcmp(up->user, "none") != 0)
		return;
	if(iseve())
		return;
	error(Eperm);
}

I had not included this special case handling of user 'none' in my code for ns operations. Fixing this required adding a single line to my modified devproc.c:

case Qns:
//	print("procnsreq on p, %s, %ld\n", a, n);
	if(p->debug.locked == 1)
		qunlock(&p->debug);
+	nonone(p);
	procnsreq(p, va, n);
	break;

And with that change in place, user none cannot modify the namespace of other processes owned by user none. Thanks to hiro for the valuable discussion and directing my attention to possible issues with the 'none' user!