In message <http://marc.info/?l=openbsd-misc&m=141616701418506&w=1> I wrote: > Web browsers scare me: they're huge pieces of code, un-audited, they > have embedded Turing-complete interpreters, they live in a horribly > imsecure environment, [[...]] > > So, I'm thinking about how to exploit-mitigate a web browser (I'll use > firefox here for purposes of illustration, but this is basically generic > to any other web browser). This is in the context of a single-user > OpenBSD desktop (say a laptop). > [[...]] > > I can see several possible forms of exploit-mitigation: > (a) use the noscript firefox extension to block javascript > (b) use capsicum to sandbox forefox and any plugin processes > (c) run firefox in a chroot jail > (d) have firefox talk to an Xephyr(1) instance > so it's semi-isolated from the main X server > (e) maybe have firefox go through an ssh tunnel to localhost > (f) run firefox as an unpriviliged user _firefox, group _firefox, and > use Unix file permissions to deny that user access to $HOME/
Thank you to everyone who's responded! Daniel Dickman pointed to the quark formally-verified web browser. This is interesting research... but if I'm reading their paper correctly, their formally-verified security properties still permit the browser to (for example) send my ~/.ssh/ private keys to evil.com. :( I'm not sure whether these properties block the installation of keyloggers, either. Jorge Gabriel Lopez Paramount's idea of putting the web browser inside a read-only virtual machine is clever. Virtual machines have lots of security holes (http://marc.info/?l=openbsd-misc&m=119318909016582&w=1 and http://xenbits.xen.org/xsa/), but making the VM read-only and/or re-installing it often should mitigate that to some extent. I now have (e) and (f) running ok on 5.6-stable, on a Thinkpad T60 laptop (3GB RAM, 2.0GHz Intel Core Duo). My normal login is in login class 'staff', for which I've upped the memory limits to infinity. I've installed the firefox-31.0 package, and created a new unpriviliged user _firefox (group _firefox and no other groups, login class staff). Some further details: The obvious way of doing (f) is to make either the firefox binary, or a wrapper program, setuid/setgid _firefox. Unfortunately, it turns out that that doesn't drop supplementary group ids. That is, if my normal id is in group wheel, then even after executing a setuid/setgid wrapper, the process is still in group wheel. :( Since Perl (unlike shells) allows safe setuid scripts, I thought of using the Perl Proc::UID perl module. Alas, this is broken -- it won't compile on any modern perl, and the bugs in question have been open with no change in status for > 3 years. So... back to C. After a bit of poking around with setgroups(2), I found that the wrapper has to be setuid/setgid root in order to be allowed to drop the supplemental groups. Following Chen, Wagner, & Dean's paper "Setuid Demystified" http://www.cs.berkeley.edu/~daw/papers/setuid-usenix02.pdf and inspired by /usr/src/usr.sbin/ntp.c lines 145-148, I wound up with the following wrapper: --- begin wrapper --- #include <sys/types.h> #include <unistd.h> #include <stdio.h> #define ERROR_EXIT_STATUS 1 #define PROGRAM_TO_EXECUTE "/usr/bin/id" /* * This wrapper * - drops any supplementary groups * - changes to uid & gid _firefox, and * - executes another program */ int main(void) { /* FIXME: should really look these up via getpwnam(3) and getgrnam(3) */ const uid_t firefox_euid = 2000; const gid_t firefox_egid = 2000; const int my_euid = geteuid(); const int my_egid = getegid(); printf("in wrapper before dropping privs: my_euid=%d, my_egid=%d\n", (int) my_euid, (int) my_egid); printf("dropping any supplementary groups...\n"); if (setgroups(0, NULL) != 0) { perror("unable to drop supplementary groups"); return ERROR_EXIT_STATUS; } printf("setting gid to _firefox...\n"); if (setresgid(firefox_egid, firefox_egid, firefox_egid) != 0) { perror("unable to set firefox group id"); return ERROR_EXIT_STATUS; } printf("setting uid to _firefox...\n"); if (setresuid(firefox_euid, firefox_euid, firefox_euid) != 0) { perror("unable to set firefox user id"); return ERROR_EXIT_STATUS; } printf("executing %s\n", PROGRAM_TO_EXECUTE); execl(PROGRAM_TO_EXECUTE, PROGRAM_TO_EXECUTE, NULL); /* we only get to here if the execl() failed */ perror("unable to execute"); return ERROR_EXIT_STATUS; } --- end wrapper --- If compiled and made setuid-root and setgid-wheel, this successfully drops all its priviliges (including supplemental groups). But, when I changed /usr/bin/id to (say) /usr/X11R6/bin/xterm or /usr/X11R6/bin/xclock (for testing), I then found that the X server (rightfully) refuses to accept a connection from a process running with uid/gid _firefox. I played around a bit with copying my .Xauthority file to the _firefox home directory, but couldn't get that to work, so I decided to use (e) (have firefox go through an ssh tunnel) instead. This also gives a tiny bit more isolation for the firefox process -- no more shared-memory between firefox and the X server. So, configured sshd to allow X forwarding and to allow (only) user _firefox publickey authentication, added a pf rule to block outside access to sshd, and activated sshd. Then I created a new public keypair in ~/.ssh/firefox_id_rsa{,.pub}, with no passphrase, and copied the public key to ~_firefox/.ssh/authorized_keys (mode 600). I renamed /usr/local/bin/firefox to /usr/local/bin/firefox.bin and put the following script (executable, but no special priviliges) in my ~/bin/ : --- begin start-firefox script --- #!/bin/sh ssh -X -i ~/.ssh/firefox_id_rsa _firefox@localhost \ '/usr/local/bin/firefox.bin -no-remote -new-instance' \ 2>&1 >/dev/null & --- end start-firefox script --- Running this script produces a couple of warning messages that we're blocking some X progocol extensions: Xlib: extension "RANDR" missing on display "localhost:10.0". Xlib: extension "MIT-SHM" missing on display "localhost:10.0". Blocking firefox from accessing these seems like a security boost to me: according to http://en.wikipedia.org/wiki/RandR , RANDR "facilitate the ability to resize, rotate and reflect the root window of a screen", and MIT-SHM is a X protocol extension for using shared memory to communicate between a client (firefox) and the X server. After these warnings, firefox starts and runs ok as uid/gid _firefox. It seems about as (un)responsive as usual. (Perhaps a better way to phrase that would be "firefox is sluggish enough in its usual operation that the extra overhead of the ssh tunnel isn't noticable".) I haven't tried any plugins yet. Other notes: * Firefox likes $HOME/Desktop as a spooling area for saving things, so I've made ~_firefox/ and ~_firefox/Desktop/ both mode 755, so that I can copy files out of that easily. I don't see any security risk in this (given my context of a single-user desktop/laptop, with the "Desktop" directory only used as a transient spool directory). * Making my home directory mode 750 (to block firefox from having any access to it) has the unfortunate side effect of excluding all my files from locate(1). Since the locate database is built by user 'nobody', my "solution" is to add myself to that group. Is there a security risk in this? -- -- "Jonathan Thornburg [remove -animal to reply]" <jth...@astro.indiana-zebra.edu> Dept of Astronomy & IUCSS, Indiana University, Bloomington, Indiana, USA "There was of course no way of knowing whether you were being watched at any given moment. How often, or on what system, the Thought Police plugged in on any individual wire was guesswork. It was even conceivable that they watched everybody all the time." -- George Orwell, "1984"