nullp0tr

Ahmed Alsharif

Wrapping programs in seccomp filters

I was recently in need of blocking a program’s internet access without any root access. It wasn’t out of security reasons per say, and so there was no need for a full blown sandboxing solution.

First option was using unshare, which is a really cool tool that would’ve been perfect for the job if it didn’t have the following issue:

$ id
uid=1000(nullp0tr) gid=1000(nullp0tr)...
$ unshare -nr id
uid=0(root) gid=0(root) groups=0(root),65534(nogroup)
$ unshare -n id
unshare: unshare failed: Operation not permitted

Since I couldn’t use unshare I decided to just use seccomp and bpf to deny all syscalls in the socket family, and that worked out pretty well. So I wrote a little program called filt that can be used in the following way:

$ filt socket -- id
uid=1000(nullp0tr) gid=1000(nullp0tr)...
$ filt getuid socket -- id
uid=4294967283 gid=1000(nullp0tr)...

This all good unless you do something very bad like in the last line from above. What’s up with uid=4294967283? well getuid should never fail, and thus it’s always used by programs in the following manner:

uid_t ruid = getuid();
use_uid(ruid);

What’s happening here is that filt makes the specified syscalls return -EACCES which on my system was -13. Since uid_t is at least an unsigned long, that -13 turns into 4294967283, which id prints directly since id has no way to check if getuid failed.

You can download filt from sr.ht if you want.