Skip to content

Instantly share code, notes, and snippets.

@pirate
Last active January 29, 2024 08:07
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save pirate/b33bad004c8e17618b4c6ebe7d973377 to your computer and use it in GitHub Desktop.
Save pirate/b33bad004c8e17618b4c6ebe7d973377 to your computer and use it in GitHub Desktop.
Remote control a Docker container from another container in the same network using netcat, ssh, or pure python
# Three ways to control a docker container remotely from another docker container or host: Python, SSH, or ncat/nc/socat.
#
# Useful if you have two containers in a Docker Compose project and you want
# container1 to be able to run commands in container2 without having to share /var/run/docker.sock
#
# Further Reading: https://www.revshells.com/#bind
### 1. Using pure Python, the cleanest option
# supports dbus/x11/etc as it doesnt spawn a new login shell, correctly handles exiting after finished commands without hacky workarounds
# Server: Open a socket on 0.0.0.0:2222 and run the received text in a subprocess, feed output line-by-line in realtime and close the socket on exit
docker run -p 2222:2222 python:3.12 python3 -c 'exec("""
import socket as s
import subprocess as sp
s1 = s.socket(s.AF_INET, s.SOCK_STREAM)
s1.setsockopt(s.SOL_SOCKET, s.SO_REUSEADDR, 1)
s1.bind(("0.0.0.0", 2222))
s1.listen(1)
print("Listening on 0.0.0.0:2222", flush=True)
conn, addr = s1.accept()
while True:
cmd = conn.recv(1024).decode()
if not cmd:
conn, addr = s1.accept()
continue
print(cmd, flush=True)
with sp.Popen(cmd, shell=True, stdout=sp.PIPE, stderr=sp.STDOUT, stdin=sp.PIPE, bufsize=1, universal_newlines=True) as p:
for line in p.stdout:
print(line.strip(), flush=True)
conn.sendall(line.encode("utf-8"))
conn.close()
conn, addr = s1.accept()
""")'
# Client: simply pipe a command into netcat and it'll run on the server, it will close automatically on exit
echo whoami | netcat 127.0.0.1 2222
### 2. Using SSH, listens on port 2222 for connections to root@container_ip with an empty password
# hacky, spawns a new login shell in the container for each client command, so dbus/x11/etc wont work
# Server: install sshd, set root password to emptystring, allow root ssh login with empty pass, start sshd server
docker run -it -p 2222:22 ubuntu:22.04 /bin/bash -c "\
apt-get update -qq; \
apt-get install -y openssh-server; \
mkdir /var/run/sshd; \
printf '%s\n' 'root:U6aMy0wojraho' | chpasswd -e; \
echo 'PermitRootLogin yes' >> /etc/ssh/sshd_config; \
echo 'PermitEmptyPasswords yes' >> /etc/ssh/sshd_config; \
echo 'PasswordAuthentication yes' >> /etc/ssh/sshd_config; \
sed 's@session\s*required\s*pam_loginuid.so@session optional pam_loginuid.so@g' -i /etc/pam.d/sshd; \
/usr/sbin/sshd -D"
# Client: connect to server on 2222 with an empty password and run whoami, printing result stdout/stderr on client machine
ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -q -p 2222 root@127.0.0.1 /bin/bash -c 'whoami'
### 3. Using ncat, listens on port 2222 for netcat connections
# hackier, but it supports dbus/x11/etc as it doesnt spawn a new login shell for each command run by the client
# Server: install ncat (not nc or netcat, because we want --sh-exec), open ncat server listening on 2222 that runs each command with /bin/bash
docker run -it -p 2222:22 ubuntu:22.04 /bin/bash -c "\
apt-get update -qq; \
apt-get install -y ncat; \
ncat -lk 22 --sh-exec /bin/bash
"
# Client: open a netcat tunnel to 127.0.0.1:2222, pass in "whoami" command + magic string to watch for on completion, loop looking for magic string and close pipe on match or timeout
while read -r i; do
echo "$i";
if [[ "$i" == "DONEDONEDONE" ]]; then
break;
fi;
done < <(echo "whoami; echo DONEDONEDONE" | timeout 10 netcat 127.0.0.1 2222)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment