Per-Process Namespaces

Posted by Utumno on Wed 18 Feb 2009 at 11:34

Our main development servers at work use almost 100% free software; however, recently I had a rare pleasure of having to install a piece of a binary blob. The Blob reared its ugly head as soon as I tried its installation routine. Read on to see how per-process namespaces can help defeat The Blob.

dev_server# ./blob_install
Your Linux distribtion 'Debian GNU/Linux 5.0 \n \l' is unsupported. 
Supported distributions: Fedora 8, 9 and 10; Ubuntu 8.04 and 8.10. Exiting.

Attack

The Blob's only function is to check out pre-compiled software from one of our subcontractor's SVN servers, create workspace ( read: create a bunch of directories, move some files around, maybe set up a few links, check availability of some tools ). In short: it sets up environment for development. It is purely a console software, has no graphical parts, for sure does not interact with any other processes or daemons, does not use any advanced libraries and as such, should easily work in Debian or any other reasonably recent distribution.

So let's install it anyway: run it under strace and see it open /etc/issue:
dev_server# cat /etc/issue
Debian GNU/Linux 5.0 \n \l
Now open blob_install with a hex editor and notice that it expects "Ubuntu 8.10 \n \l" there. Temporarily change /etc/issue and watch The Blob install flawlessly.

Blob Strikes Back

At this point I thought I could just change /etc/issue back to its Debian version and start using The Blob; unfortunately, it turned out I underestimated its stubbornness: it actually checks the distribution every single time it performs any action.

It thus became clear that I would either have to permanently keep /etc/issue in its Ubuntu form (which I don't want to do) or try to fool The Blob in some more clever way.

Regrouping

Fortunately I stumbled upon Mike Hommey's excellent write-up about per-process namespaces.

Per-process namepaces let one selectively 'unshare' any resources that were being shared at the time of creation of the process. This feature, among other things, allows each process to have a different set of mount points. Combined with bind mounts, it can allow some useful setups: we can create a new namespace, bind-mount something, run a process and only this process (and its children) will see the new mount. Furthermore, as soon as the process which created the namespace exits, the namespace and mounts inside it disappear.

Let's see an example. First we have to create a new namespace. This can be achieved with help of Mike's helper program 'newns':
#include <sched.h>
#include <syscall.h>
#include <unistd.h>

int main(int argc, char *argv[]) {
  syscall(SYS_unshare, CLONE_NEWNS);
  if (argc > 1)
    return execvp(argv[1], &argv[1]);
  return execv("/bin/sh", NULL);
}
Here we used the unshare(2) syscall to create the namespace - we have to do it this way because Lenny's glibc does not implement the syscall.

Compile this , create two files
dev_server# echo FIRST > first
dev_server# echo SECOND > second
and try
dev_server# ./newns
dev_server# mount -n --bind second first
dev_server# cat first
SECOND
Now in a different console
dev_server# cat first
FIRST
You can see that the bind-mount is only seen by the process which called 'unshare' and its children. More concise way to see the above is
dev_server# ./newns sh -c "mount -n --bind second first; cat first"
SECOND
dev_server# cat first
FIRST

Counterattack

Armed with this knowledge we can attack The Blob again:

1) Create /etc/issue.ubuntu with the proper Ubuntu string inside
2) move The Blob's binaries to /usr/local/bin/theblob
3) create a script /usr/local/bin/ubuntize:
#!/bin/sh

/usr/local/bin/newns sh -c "mount -n --bind /etc/issue.ubuntu /etc/issue; /usr/local/bin/theblob/$0 $@"
4) for each binary, create a link:
dev_server# ln -s /usr/local/bin/ubuntize /usr/local/bin/blob_install
dev_server# ln -s /usr/local/bin/ubuntize /usr/local/bin/blob_checkout
dev_server# ln -s /usr/local/bin/ubuntize /usr/local/bin/blob_create
... and so on ...

And we're done! The Blob's binaries think they are running on a Ubuntu 8.10 system, and everything else continues to work normally. Victory!

 

 


Posted by Anonymous (164.143.xx.xx) on Wed 18 Feb 2009 at 12:50
Would it have been easier to hex edit the appropriate string in the blob?

--
Charles Darke
http://digitalconsumption.com

[ Parent | Reply to this comment ]

Posted by Anonymous (64.218.xx.xx) on Wed 18 Feb 2009 at 14:46
I initially thought that too. But then, reconsidering, it makes more sense to do this. TheBlob probably gets occasional updates, and setting it up this way makes it much easier to handle those (instead of hex editing each time you update). It's also easier to update, document, and script, IMHO.

[ Parent | Reply to this comment ]

Posted by Steve (2001:0xx:0xx:0xxx:0xxx:0xxx:xx) on Wed 18 Feb 2009 at 20:30
[ Send Message | View Steve's Scratchpad | View Weblogs ]

I admit I'd have used a hex-editor myself, but this per-process stuff looks very very cool.

I can think of lots of uses for it already.

Steve

[ Parent | Reply to this comment ]

Posted by Utumno (220.133.xx.xx) on Thu 19 Feb 2009 at 00:32
[ Send Message | View Utumno's Scratchpad | View Weblogs ]
The hex editor would have worked, of course, but The Blob gets updated pretty frequently - and moreover, I would have to edit 18 binaries ( each one checks the distribution! )

For a more advanced example of what per-process namespaces can do, take a look here:

http://glandium.org/blog/?p=224

[ Parent | Reply to this comment ]

Posted by Anonymous (84.246.xx.xx) on Sat 21 Feb 2009 at 12:20
Maybe a dump question;
but could this a approach to setup a protected environment for guest users or processes.
something like a cheap chroot ?

[ Parent | Reply to this comment ]

Posted by Utumno (220.133.xx.xx) on Sat 21 Feb 2009 at 17:40
[ Send Message | View Utumno's Scratchpad | View Weblogs ]
Not really. Per-process namespaces do not have much in common with a chroot.

[ Parent | Reply to this comment ]

Posted by Anonymous (84.133.xx.xx) on Sun 22 Feb 2009 at 14:04
You are searching for the "vserver" or "openvz" kernel images :-) I prefer vserver as it is the "truly free" project of both.

These do basically exacly what you want: Create namespaces (called "security context" in here, but basically the same), chroot and run the "init" process of any kernel-compatible distro you copied over. There's also some sweet "newvserver" script which installs a fresh Debian (stable, unstable, your choice) inside a new vserver and applies some cosmetics so it starts and shuts down without error messages.

You can, however, use every piece on it's own as well, which comes in handy sometimes. You can run any process in any context with "chcon" (available only on the root server) or limit a process to certain network addresses ("chbind").

In this example I run "iotop" in context 1 (special superior context) to see all processes of all vservers:

chcontext --ctx 1 iotop

Thus seeing where all that disk IO comes from.

[ Parent | Reply to this comment ]

Posted by Anonymous (190.51.xx.xx) on Sat 21 Feb 2009 at 21:24
Damn it's a good hack, maybe now I can make true my dream of having 2 distros working completely in parallel sharing almost all the files...sort of xP.

[ Parent | Reply to this comment ]

Posted by AJxn (130.243.xx.xx) on Tue 7 Apr 2009 at 21:05
[ Send Message | View Weblogs ]
Yes. An impressive hack and good presentation.

You might have a look at vserver, as described earlier.

[ Parent | Reply to this comment ]

Posted by Anonymous (80.156.xx.xx) on Fri 27 Feb 2009 at 14:29
Can this be used to fool time-limitations software?
I have a certain program that was gpl, still in debian packages but contains a segfault since 2005.
Since then, the commercial version has corrected it but not in the gpl one..
And now, the commercial one is not maintained anymore.. Great.
Otherwise, I'll try to track the bug..

[ Parent | Reply to this comment ]

Posted by Utumno (60.248.xx.xx) on Wed 4 Mar 2009 at 05:32
[ Send Message | View Utumno's Scratchpad | View Weblogs ]
Yes, you could have your time-restricted software use a different libc which would report earlier time.

[ Parent | Reply to this comment ]

Posted by Anonymous (88.175.xx.xx) on Mon 20 Apr 2009 at 22:55
Per-processes are quite interesting but there would be another well-known way to bypass the /etc/issue check with LD_PRELOAD if the binary isn't statically linked and if it doesn't have the setuid bit.

fake_issue.c :

#include <syscall.h>
#include <string.h>

int
open (const char *pathname, mode_t mode) {
return syscall (SYS_open, !strcmp (pathname, "/etc/issue") ?
"/etc/fake_issue" : pathname, mode);
}

$ gcc -shared -o fake_issue.so fake_issue.c
$ LD_PRELOAD=$PWD/fake_issue.so bad_program

[ Parent | Reply to this comment ]

Posted by Anonymous (89.179.xx.xx) on Thu 30 Apr 2009 at 18:49
As man 1 fakeroot says, `open' function is used in libc internally, overridden in it multiple times and such, so it isn't good to simply point it to syscall.

[ Parent | Reply to this comment ]

Sign In

Username:

Password:

[Register|Advanced]

 

Flattr

 

Current Poll

Which init system are you using in Debian?






( 1037 votes ~ 6 comments )