diff --git a/labs/remotebrowser/Dockerfile b/labs/remotebrowser/Dockerfile index 6924e04..158cbce 100644 --- a/labs/remotebrowser/Dockerfile +++ b/labs/remotebrowser/Dockerfile @@ -7,9 +7,11 @@ ENV DEBIAN_FRONTEND=noninteractive \ DISPLAY=:99 \ VNC_PORT=5900 \ NOVNC_PORT=6080 \ + NOVNC_HTTPS_PORT=6443 \ PROXY_PORT=8080 \ CHROME_REMOTE_DEBUGGING_PORT=9222 \ - PROXY_HOST=127.0.0.1 + PROXY_HOST=127.0.0.1 \ + NOVNC_ENABLE_HTTPS=false # --------------------------------------------------------------------------- # 1. Base packages @@ -121,6 +123,14 @@ RUN NOVNC_WEB=$(cat /opt/novnc_path) && cat > "${NOVNC_WEB}/index.html" << 'HTML overflow:hidden; background:#000; } #screen { position:fixed; top:0; left:0; width:100vw; height:100vh; background:#000; } + /* Force the injected noVNC canvas to fill the container exactly */ + #screen canvas, #noVNC_canvas { + display:block!important; + position:absolute!important; + top:0!important; left:0!important; + width:100%!important; height:100%!important; + border:none!important; outline:none!important; + } /* Nuke every noVNC chrome element by ID and class */ #noVNC_control_bar_anchor, #noVNC_control_bar, #noVNC_control_bar_handle, #noVNC_hint_anchor, @@ -166,18 +176,30 @@ RUN NOVNC_WEB=$(cat /opt/novnc_path) && cat > "${NOVNC_WEB}/index.html" << 'HTML function connect() { strip(); msg('Connecting...', true); - const url = 'ws://' + location.hostname + ':' + (location.port||'6080') + '/websockify'; + const proto = location.protocol === 'https:' ? 'wss:' : 'ws:'; + // Use the actual port from the browser URL, or default based on protocol + const port = location.port || (location.protocol === 'https:' ? '443' : '80'); + const url = proto + '//' + location.hostname + ':' + port; rfb = new RFB(scrn, url, { credentials: {password:''} }); rfb.scaleViewport = true; rfb.resizeSession = true; rfb.clipViewport = false; rfb.showDotCursor = false; rfb.background = '#000000'; - rfb.addEventListener('connect', () => { strip(); msg('TLSDebug Active'); }); + rfb.addEventListener('connect', () => { + strip(); + msg('TLSDebug Active'); + // Immediately sync session size to the actual viewport + rfb.resizeSession = true; + }); rfb.addEventListener('disconnect', () => { msg('Reconnecting...', true); setTimeout(connect, 3000); }); rfb.addEventListener('credentialsrequired', () => rfb.sendCredentials({password:''})); } + // Keep session size in sync whenever the browser window is resized + new ResizeObserver(() => { if (rfb) rfb.resizeSession = true; }) + .observe(document.documentElement); + connect();
+ +@@ -301,6 +323,44 @@ log "=== CA installation complete ===" SCRIPT RUN chmod +x /opt/install-ca.sh +# --------------------------------------------------------------------------- +# 6b. generate-novnc-cert.sh — Self-signed certificate for noVNC HTTPS +# --------------------------------------------------------------------------- +RUN cat > /opt/generate-novnc-cert.sh << 'SCRIPT' +#!/bin/bash +set -e + +CERT_DIR="${1:-/opt/novnc-certs}" +CERT_FILE="${CERT_DIR}/novnc.pem" + +log() { echo "[cert] $*"; } + +mkdir -p "${CERT_DIR}" + +if [ -f "${CERT_FILE}" ]; then + log "Certificate already exists: ${CERT_FILE}" + log "Expires: $(openssl x509 -noout -enddate -in "${CERT_FILE}" 2>/dev/null || echo 'unknown')" + exit 0 +fi + +log "Generating self-signed certificate for noVNC..." + +openssl req -x509 -nodes -newkey rsa:2048 \ + -keyout "${CERT_FILE}" \ + -out "${CERT_FILE}" \ + -days 365 \ + -subj "/C=US/ST=State/L=City/O=TLSDebug/CN=localhost" \ + -addext "subjectAltName=DNS:localhost,DNS:*.local,IP:127.0.0.1" \ + 2>/dev/null + +chmod 600 "${CERT_FILE}" + +log "Certificate generated: ${CERT_FILE}" +log "Subject: $(openssl x509 -noout -subject -in "${CERT_FILE}" 2>/dev/null)" +log "Expires: $(openssl x509 -noout -enddate -in "${CERT_FILE}" 2>/dev/null)" +SCRIPT +RUN chmod +x /opt/generate-novnc-cert.sh + # 7. start-chrome.sh # --------------------------------------------------------------------------- RUN cat > /opt/start-chrome.sh << 'SCRIPT' @@ -433,7 +493,7 @@ tlsproxy \ -port "${PROXY_PORT}" \ -certdir "${CERTDIR}" \ -skip-install \ - 2>&1 | tee -a "${LOG_DIR}/tlsproxy.log" & + 2>&1 | grep -v 'SSLV3_ALERT_CERTIFICATE_UNKNOWN' | tee -a "${LOG_DIR}/tlsproxy.log" & PROXY_PID=$! log "[*] Waiting for CA certificate ..." @@ -463,27 +523,59 @@ x11vnc \ -shared \ -nopw \ -rfbport "${VNC_PORT}" \ - -o "${LOG_DIR}/x11vnc.log" \ - -bg \ - -noipv6 \ - -xkb \ - -noxrecord \ - -noxfixes \ -noxdamage \ + -noxfixes \ + -noxcomposite \ -cursor arrow \ - -wait 5 \ - -defer 5 + -loop \ + -repeat \ + -xkb \ + >> "${LOG_DIR}/x11vnc.log" 2>&1 & +sleep 2 wait_port "${VNC_PORT}" "x11vnc" 40 # ---- 5. noVNC -------------------------------------------------------------- -log "[*] Starting noVNC websockify on port ${NOVNC_PORT} ..." +# Check for custom certificate or generate self-signed +CERT_DIR="/opt/novnc-certs" +CUSTOM_CERT="/certs/novnc.pem" +CERT_FILE="${CERT_DIR}/novnc.pem" + +if [ "${NOVNC_ENABLE_HTTPS}" = "true" ]; then + log "[*] HTTPS mode enabled" + + # Use custom cert if provided, otherwise generate self-signed + if [ -f "${CUSTOM_CERT}" ]; then + log "[*] Using custom certificate: ${CUSTOM_CERT}" + mkdir -p "${CERT_DIR}" + cp "${CUSTOM_CERT}" "${CERT_FILE}" + chmod 600 "${CERT_FILE}" + else + log "[*] No custom cert found, generating self-signed..." + /opt/generate-novnc-cert.sh "${CERT_DIR}" + fi + + NOVNC_PORT="${NOVNC_HTTPS_PORT:-6443}" + SSL_ARGS="--cert=${CERT_FILE} --ssl-only" + SCHEME="https" +else + log "[*] HTTP mode (set NOVNC_ENABLE_HTTPS=true for HTTPS)" + SSL_ARGS="" + SCHEME="http" +fi + +log "[*] Starting noVNC websockify on port ${NOVNC_PORT} (${SCHEME}) ..." log "[*] web root : ${NOVNC_WEB}" log "[*] target : 127.0.0.1:${VNC_PORT}" +# note: SSL_ARGS may include --ssl-only; omit to allow fallbacks when certs are untrusted +tmp_args="${SSL_ARGS}" +# remove ssl-only if present to avoid 'non-SSL connection received but disallowed' errors +tmp_args=$(echo "${tmp_args}" | sed 's/--ssl-only//g') websockify \ --web "${NOVNC_WEB}" \ --heartbeat 30 \ + ${tmp_args} \ --log-file "${LOG_DIR}/novnc.log" \ "${NOVNC_PORT}" \ "127.0.0.1:${VNC_PORT}" & @@ -496,6 +588,9 @@ log "[diag] websockify process: $(pgrep -a websockify 2>/dev/null || echo NOT RU log "[diag] listening ports: $(ss -tlnp 2>/dev/null | grep -E '5900|6080|8080' || echo none)" # ---- 6. Chrome ------------------------------------------------------------- +# remove stale profile locks which can prevent Chrome from launching +rm -f /opt/chrome-profile/Singleton* /opt/chrome-profile/Default/Singleton* + log "[*] Starting Chrome ..." /opt/start-chrome.sh >> "${LOG_DIR}/chrome.log" 2>&1 & CHROME_PID=$! @@ -508,10 +603,13 @@ wait_port 4040 "log viewer" 20 log "" log "[+] All services started." -log "[+] Browser (noVNC) : http://localhost:${NOVNC_PORT}" +log "[+] Browser (noVNC) : ${SCHEME}://localhost:${NOVNC_PORT}" log "[+] Log viewer : http://localhost:4040" log "[+] TLS proxy : localhost:${PROXY_PORT}" log "[+] Host logs dir : ${LOG_DIR} (mounted to ./logs/ on host)" +if [ "${NOVNC_ENABLE_HTTPS}" = "true" ]; then + log "[+] Certificate : ${CERT_FILE}" +fi log "" cleanup() { @@ -529,7 +627,7 @@ RUN chmod +x /entrypoint.sh # --------------------------------------------------------------------------- # 9. Ports # --------------------------------------------------------------------------- -EXPOSE 6080 5900 8080 4040 9222 +EXPOSE 6080 6443 80 443 5900 8080 4040 9222 WORKDIR /opt/tlsdebug ENTRYPOINT ["/entrypoint.sh"] diff --git a/labs/remotebrowser/README.md b/labs/remotebrowser/README.md index 6ad042d..3582fd2 100644 --- a/labs/remotebrowser/README.md +++ b/labs/remotebrowser/README.md @@ -1 +1,65 @@ -### Allow Testing of Browser In Browser +# TLSDebug Remote Browser Lab + +Browser-in-browser setup for testing HTTPS interception with TLSDebug. All-in-one Docker container with Chrome, noVNC, and the TLS intercepting proxy pre-configured. + +## Quick Start + +```bash +docker-compose up +``` + +Open your browser: +- **http://localhost:6080** - Chrome browser interface (proxy already configured) +- **http://localhost:4040** - Real-time traffic monitor + +Browse any HTTPS site in the remote Chrome - all traffic will be intercepted and logged. + +## What's Included + +- **Headless Chrome** - Full browser with TLS proxy pre-configured +- **noVNC** - Web-based VNC viewer (no VNC client needed) +- **TLSDebug Proxy** - Intercepts and logs all HTTPS traffic +- **Debug Monitor** - Port 4040 shows live traffic, tokens, and sessions +- **Auto CA Trust** - Proxy certificate automatically trusted in Chrome + +## Ports + +| Port | Service | Description | +|------|---------|-------------| +| 6080 | noVNC | Browser interface - start here | +| 4040 | Monitor | Real-time traffic viewer | +| 8888 | Proxy | TLS proxy (mapped from internal 8080) | +| 5900 | VNC | Raw VNC access (optional) | +| 9222 | DevTools | Chrome debugging protocol (optional) | + +## Debug Monitor (Port 4040) + +The monitor shows: +- **All HTTP requests/responses** with full headers +- **Session cookies** - Marked with `"session": true` +- **JWT tokens** - Automatically decoded with claims visible +- **OAuth tokens** - Access and refresh tokens +- **POST data** - Form parameters and JSON payloads + +## Captured Data + +All logs and tokens are saved to `./logs/` on your host: +- `proxy.log` - Full request/response logs +- `EditThisCookie_Sessions.json` - All captured cookies and sessions +- `captured_tokens.json` - JWT and OAuth tokens +- `proxy-ca.crt` - CA certificate (if you need to trust it elsewhere) + +## Configuration + +Edit `docker-compose.yml` to customize: +- `START_URL` - Set the browser's starting page +- Port mappings - Change if ports conflict +- `./logs` volume - Where captured data is saved + +## Use Cases + +- Debug OAuth/SAML flows +- Inspect JWT token contents +- Extract session cookies for testing +- Analyze API request/response patterns +- Troubleshoot TLS/HTTPS issues diff --git a/labs/remotebrowser_TLS/Dockerfile b/labs/remotebrowser_TLS/Dockerfile new file mode 100644 index 0000000..158cbce --- /dev/null +++ b/labs/remotebrowser_TLS/Dockerfile @@ -0,0 +1,633 @@ +# ============================================================================= +# TLSDebug + Headless Chrome + noVNC — single self-contained Dockerfile +# ============================================================================= +FROM debian:bookworm-slim + +ENV DEBIAN_FRONTEND=noninteractive \ + DISPLAY=:99 \ + VNC_PORT=5900 \ + NOVNC_PORT=6080 \ + NOVNC_HTTPS_PORT=6443 \ + PROXY_PORT=8080 \ + CHROME_REMOTE_DEBUGGING_PORT=9222 \ + PROXY_HOST=127.0.0.1 \ + NOVNC_ENABLE_HTTPS=false + +# --------------------------------------------------------------------------- +# 1. Base packages +# --------------------------------------------------------------------------- +RUN apt-get update && apt-get install -y --no-install-recommends \ + golang-go \ + git \ + xvfb \ + x11vnc \ + openbox \ + xterm \ + python3 \ + python3-pip \ + python3-websockify \ + novnc \ + wget \ + gnupg \ + ca-certificates \ + fonts-liberation \ + libasound2 \ + libatk-bridge2.0-0 \ + libatk1.0-0 \ + libcups2 \ + libdbus-1-3 \ + libgdk-pixbuf2.0-0 \ + libgtk-3-0 \ + libnspr4 \ + libnss3 \ + libnss3-tools \ + libx11-xcb1 \ + libxcomposite1 \ + libxdamage1 \ + libxfixes3 \ + libxkbcommon0 \ + libxrandr2 \ + libxshmfence1 \ + xdg-utils \ + openssl \ + curl \ + netcat-openbsd \ + procps \ + psmisc \ + x11-utils \ + unclutter \ + && rm -rf /var/lib/apt/lists/* + +# --------------------------------------------------------------------------- +# --------------------------------------------------------------------------- +# --------------------------------------------------------------------------- +# --------------------------------------------------------------------------- +# 2. Install browser +# amd64 -> Google Chrome stable (official Google .deb) +# arm64 -> Chromium from Debian bookworm (native deb, no snap) +# +# Base image is debian:bookworm-slim which has real chromium packages +# on all architectures — no snap wrappers. +# --------------------------------------------------------------------------- +RUN ARCH=$(dpkg --print-architecture) && \ + if [ "${ARCH}" = "amd64" ]; then \ + echo "[browser] amd64: installing Google Chrome ..." && \ + wget -q -O /tmp/chrome.deb \ + "https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb" && \ + apt-get update && \ + apt-get install -y --no-install-recommends /tmp/chrome.deb && \ + rm /tmp/chrome.deb && \ + rm -rf /var/lib/apt/lists/* && \ + ln -sf /usr/bin/google-chrome /usr/local/bin/browser && \ + echo "[browser] installed: $(google-chrome --version)"; \ + elif [ "${ARCH}" = "arm64" ]; then \ + echo "[browser] arm64: installing Chromium from Debian bookworm ..." && \ + apt-get update && \ + apt-get install -y --no-install-recommends chromium && \ + rm -rf /var/lib/apt/lists/* && \ + ln -sf /usr/bin/chromium /usr/local/bin/browser && \ + echo "[browser] installed: $(chromium --version)"; \ + else \ + echo "[browser] ERROR: unsupported arch ${ARCH}" && exit 1; \ + fi + +# --------------------------------------------------------------------------- +# 3. Build TLSDebug from source +# AllTrafficModule is already active by default and logs every request, +# response, headers and body — no source patching needed. +# We set log flags via the LOG_FLAGS env var at runtime instead. +# --------------------------------------------------------------------------- +WORKDIR /build +RUN git clone --depth 1 https://github.com/secdev02/TLSDebug.git . \ + && CGO_ENABLED=0 go build -ldflags "-s -w" -o /usr/local/bin/tlsproxy tlsproxy.go \ + && chmod +x /usr/local/bin/tlsproxy \ + && rm -rf /build + +# 4. Locate noVNC web root and write kiosk index.html +# Kiosk mode: auto-connects, scales to fill browser window, hides toolbar, +# no password prompt, no connection dialog — just the desktop. +# --------------------------------------------------------------------------- +RUN find /usr -name "vnc.html" 2>/dev/null | head -1 | xargs -I{} dirname {} > /opt/novnc_path \ + && NOVNC_WEB=$(cat /opt/novnc_path) \ + && echo "noVNC web root: ${NOVNC_WEB}" \ + && ls -la "${NOVNC_WEB}" + +RUN NOVNC_WEB=$(cat /opt/novnc_path) && cat > "${NOVNC_WEB}/index.html" << 'HTML' + + +
+ +
+ + +
+