Monday, May 5, 2008

Restricted Accounts And Vi(m) Tricks in Linux And Unix

Greetings,

Today we're going to take a look at restricted shells on Linux and Unix and a few ways to break out of those shells. We'll also be looking at the ways these security holes can be plugged so that any sysadmin can make his/her restricted shell more bullet-proof.

To keep this article in scope (and under 10,000 words ;) we're going to limit the potential "escapes," and their countermeasures, to restricted shells and the vi (or vim) editor (We'll leave chroot'ing, and all the other ways to make life difficult, for another time. If you're interested in an alternative method of making a shell available for you to goof with, check out our previous post on using perl to bind a shell to a Linux or Unix network socket :)

Of course, the first thing you'll want to do to moderately secure your system against a user that you want to keep contained, is to make sure that their default shell is restricted. Most distributions of Linux and Unix already come with predefined links to /usr/bin/rbash (the restricted version of /bin/bash) or /bin/rksh (the restricted version of /bin/ksh), etc. In the instances where these links aren't installed, you can still create them with the same effect (using symlinks to the real shell, like: ln -s /bin/ksh /bin/rksh), since the shell's restricted mode is "turned on" by the manner in which it's called. That is to say that /bin/ksh and /bin/rksh are both the same executable; the shell just contains initialization code that makes it start up differently when it notes the name under which it's being invoked.

At first, the restricted shell seems like it's the only thing you'll ever need, since the user can't even move and (in most cases) can't run fully qualified commands, as shown below:

rbash # mkdir b
rbash # cd b
-rbash: cd: restricted
rbash # ls /tmp
-rbash: /bin/ls: restricted: cannot specify `/' in command names


But they're far from secure to start out. Below, we'll go through a few ways users can escape the restricted shell using vi (or vim), and ways to counter each potential breaking point.

1a. Using the systems predictable temporary files, expreserve and the IFS.

This hack has been around forever, and still comes back every once and a while. The breakout basically consists of the user setting the IFS (Internal Field Separator, which is normally a space, tab or newline) and then using an editor to execute files in his/her local directory using the "preserve" function of vi. It's only really a trivial hack if the system is setup improperly.

Example below, with the assumption that "[esc]:pre" uses /bin/mail to notify the user of a vi crash.

rbash # touch bin mail <--- These files would be edited to include bad commands and made executable
rbash # chmod 777 bin mail
rbash # export IFS="/"
rbash # vi
[esc]:pre
File preserved.
[esc]:q!


Now, when the [esc]:pre function runs /bin/mail, it would (theoretically) be running "bin" and "mail" in the user's home directory, since he/she changed the IFS to "/".

1b. How to easily counter this attack (assuming your system is vulnerable)

There's one huge step you can take to prevent this from happening forever (just in case your system ever has been vulnerable and you don't want to have to worry about it anymore). By default, your restricted shell should not let the user redefine the IFS variable. Here's the one thing you can do to make it impossible for this hack to work (although it will come at some cost, since no one will be able to preserve crashed vi files. And, we should note that part of this vulnerability is that this command used to be setuid root!):

host # chmod a-x /usr/lib/expreserve

Now, no one can execute that command by running [esc]:pre in vi or vim! (Note also that this file may be in a different directory depending on your distro, and may also be differently titled. For instance, the "elvis" program - a vi clone - uses the "elvprsv" command to do its preserves.)

2a. The shell escape is available by default in vi and vim, even if you are running in a restricted shell (the short version).

This allows a user to run [esc]:shell within vi and escape to a subshell within vi.

2b. The easiest fix, if it's available, is included in some versions of vi and in most versions of vim.

If you invoke them as rvi or rvim, the shell escape is disabled by default :) In some instances, invoking vi as rvi will dump your user to the "ex" editor. That's bordering on cruelty ;)

3a. Assuming you can't run vi, or vim, securely, your user can set his/her shell variable from within the editor.

All they have to do is type:

[esc]:set shell=/bin/bash

and then execute [esc]:shell from within vi to get to a regular, unrestricted bash shell. Most restricted shells won't fall for this cheap trick, but some still do.

The user can also access the shell indirectly, via the

[esc]:! /bin/whatever

type of one-line command execution.

3b. Here's a very simple way to ensure this will never happen (at least, not the unoriginal way ;)

First, set the restricted user's home directory up with an .exrc file. Normally, these are considered bad to have, but, in fact, it's generally better practice to have an existing .exrc file, that the user can't access, than none at all (since that leaves it for them to create if they can figure out a way).

bash # touch /home/restricted/.exrc
bash # chown root:root /home/restricted/.exrc
bash # chmod 644 /home/restricted/.exrc
bash # vi /home/restricted/.exrc
set exrc
set shell=/bin/false


And you're all set. Now, whenever your user uses vi (or vim - the same method is used to implement the above), the .exrc file will get processed and the default "shell" will be set to /bin/false. So, when they type:

[esc]:shell

It will knock them right back to the editor. As an added bonus, this will prevent them from being able to use the [esc]:! one-line command functionality, since it will try to exec that command using /bin/false. This will fail if /bin/false exists (because it returns failure no matter what) and also if it doesn't (because the invocation of the non-existent shell will return failure also). If you want to play it safe, make sure your system has a /bin/false before you set this (to avoid any bizarre unanticipated OS behaviour). Another common "useless" shell is /sbin/nologin, but /bin/false should be on almost every flavor and distro of Linux and Unix out there.

Hopefully you found some of these tips helpful in fixing things that, again hopefully, may not be broken yet :)

Best wishes,

, Mike