HOVEL // chapter 09
Chapter 09 Part 02 / Runtime Platform

Squatter Payload Provider

Squatter is Hovel's first core payload provider. It is a lab and demonstration payload system for authorized environments. The provider is a normal Hovel payload_provider module that speaks JSON-RPC over stdio; the Windows payload binary is opaque payload code produced by that provider. The current provider metadata reports Windows x86 with a Windows 7 compatibility floor. The Windows C payload keeps XP SP3 as a low-level lab support floor in its headers, but Hovel should not claim XP compatibility until provider metadata and tests do.

Hovel must not learn Squatter's implant wire protocol, command grammar, PE internals, or Windows API strategy. Hovel learns step contracts, payload identity, compatibility, lifecycle requirements, artifacts, transport endpoints, payload instances, cleanup handles, and session refs from the provider over RPC. Hovel controls planning, confirmation, artifact recording, capability validation, and session brokering; the provider owns Squatter's transport and wire semantics.

Settled Decisions

  1. Squatter is a payload capability, not a native Hovel execution path.
  2. The Squatter provider is a first-class payload_provider module with explicit chain steps.
  3. The provider reports step contracts, configuration schema, prepare materialization, cleanup contracts, and output capabilities through JSON-RPC.
  4. Hovel discovers payload formats, transports, compatibility, and session strategy from live provider contracts, not static payload YAML.
  5. Final payload bytes are generated during execution after confirmation, then recorded as artifacts with hashes.
  6. Hovel materializes provider bytes to controlled workspace artifact paths and passes typed payload artifacts to installer steps.
  7. Payload generation and materialization are per target and per transport.
  8. A provider may return multiple artifacts for one target, but one artifact must be marked primary.
  9. Installed Squatter agents are tracked as durable Hovel installed payload records, separate from generated artifacts and live sessions.
  10. Squatter returns provider-owned reconnect and cleanup descriptors; Hovel validates and persists the installed payload record in SQLite.
  11. Exploit modules do not receive Squatter protocol details. They produce execution capabilities that Squatter install steps consume.
  12. Provider compatibility is checked from contracts and metadata where the current planning path exposes them; actual bytes are generated after confirmation.
  13. The provider is tagged dangerous because Squatter supports file transfer, process execution, DLL loading, task listing, and sessions.
  14. The provider offers an opt-in PKI-backed squatter+tls Mesh stream. Hovel stamps the identity into the generated PE, Mesh carries ciphertext unchanged, and statically linked wolfSSL terminates TLS 1.3 inside the Windows payload.

Repository Shape

modules/
  squatter/
    README.md
    BUILD.bazel
    provider/
      main.go
      lp.go
      mesh.go
      stamp.go
      generate_main.go
    client/
      cmd/
        squatterctl/
        smbadminctl/
      shell/
      smbpipe/
      wire/
      xfer/
    tests/
    windows/
      BUILD.bazel
      pe_inspect_test.py
      source_contract_test.py
      src/
        squatter.c
        base/
        iocpserver/
        modules/
        nocrt/
        runtime/
        security/
        sqlog/
        wire/

The provider is written in Go using Hovel's Go SDK. The Windows payload is C code built with a pinned MinGW cross toolchain. The current payload includes the mux runtime, TCP bind, reverse TCP, SMB named-pipe support, interactive shell transport, one-shot cmd.exe execution, typed Windows collection commands, process control, file transfer, payload status, and auditable self-cleanup. PE and source-contract tests protect the no-import-table payload shape and the expected source layout.

Module Contract

A payload provider participates in the common module lifecycle and exposes chain-step contracts through the generic step RPC methods: step.describe, step.prepare, step.execute, and step.cleanup. The provider dispatches internally by step_id. Hovel never calls Squatter-specific private methods for orchestration.

squatter.generate
squatter.install_smb
squatter.connect_smb
squatter.connect_tcp_bind
squatter.listen_tcp_callback
squatter.connect_tcp_callback

Only connector steps produce SessionRef. Listener steps keep provider-owned live state for the chain run. Generate and install steps may produce PayloadArtifact, PayloadInstance, TransportEndpoint, CleanupHandle, and explicit installed-payload descriptors, but they do not open operator sessions.

Provider Configuration

Common options should use payload.* keys. Squatter-specific options may use squatter.* keys when they are not portable across payload providers.

payload.provider = squatter
payload.format = pe-exe
payload.transport = smb-named-pipe | tcp-bind | tcp-callback
payload.lhost = 10.10.14.3
payload.lport = 4444
payload.bind_port = 9100
payload.pipe = random-neutral
squatter.agent.max_chunk_size = 65536

payload.transport is required. The first Squatter provider supports smb-named-pipe, tcp-bind, and tcp-callback. SMB named-pipe mode requires an SMB CredentialCapability and can be installed by the provider. TCP bind mode requires an explicit installed-payload record or capability for a target endpoint. TCP callback mode requires a listener step plus an explicit installed-payload record or capability. If the agent has not connected back, the installed payload remains installed with provider-owned callback status in its reconnect metadata until a refresh or connection attempt proves it unreachable.

Payload Metadata

Payload metadata is returned by the provider over RPC. Hovel uses it for planning, review, compatibility checks, artifact recording, and session orchestration.

{
  "id": "squatter/windows/x86/windows-7/smb-named-pipe/pe-exe",
  "name": "squatter",
  "version": "0.1.0",
  "kind": "pe",
  "platform": "windows",
  "os": "windows",
  "arch": "x86",
  "minOS": "windows-7",
  "testedOS": ["windows-7"],
  "formats": ["pe-exe", "pe"],
  "tags": ["pe", "windows", "agent", "squatter"],
  "capabilities": [
    "host.info",
    "file.get",
    "file.put",
    "file.stat",
    "file.hash",
    "registry.query",
    "eventlog.query",
    "drive.list",
    "share.list",
    "acl.stat",
    "process.exec",
    "process.exec.as_user",
    "process.run",
    "process.list",
    "process.tasklist",
    "process.kill",
    "payload.status",
    "payload.cleanup"
  ],
  "transport": {
    "kind": "smb-named-pipe",
    "encrypted": false
  },
  "session": {
    "kind": "agent",
    "acquisition": "active-connect",
    "requiresPreThrowListener": false,
    "requiresPostThrowConnect": true,
    "owner": "payload_provider"
  }
}

DLL output is no longer advertised by the current provider command surface. DLL/rundll support would add export and loader semantics and should follow only after the PE EXE orchestration and typed command surface stay stable.

Transports

SMB named-pipe, TCP bind, and TCP callback comms are all required Squatter functionality. They differ in chain requirements and reconnect strategy, but the Squatter provider owns the actual LP-to-payload protocol in all modes.

SMB named-pipe mode uses credential-backed active connection:

SMB Named-Pipe Flow
  1. squatter.generate
  2. squatter.install_smb
  3. squatter.connect_smb
  4. provider-owned Hovel session

TCP bind mode uses target-side listening:

TCP Bind Flow
  1. squatter.generate
  2. explicit or external TCP-bind install record
  3. squatter.connect_tcp_bind
  4. provider-owned Hovel session

TCP callback mode uses a confirmed listener step:

TCP Callback Flow
  1. squatter.listen_tcp_callback
  2. squatter.generate
  3. explicit or external TCP-callback install record
  4. payload callback
  5. squatter.connect_tcp_callback
  6. provider-owned Hovel session

SMB mode uses a Windows named pipe exposed through SMB. The provider connects to \\target\pipe\name using an explicit CredentialCapability and exposes the resulting Squatter agent session through Hovel's session abstraction. The exploit module does not own or proxy that session. The operator-side connector uses github.com/jfjallid/go-smb to authenticate to IPC$, open the configured pipe, and run the same Squatter mux protocol used by TCP transports over the resulting pipe handle. The required configuration keys are target.host or smb.host, payload.pipe, smb.username, smb.password, optional smb.domain, and optional smb.port.

The first SMB connector is SMB2/3 only because it is backed by github.com/jfjallid/go-smb. Windows XP targets that only expose SMBv1 should use tcp-bind or tcp-callback for the representative MS17-010 chain unless a separate SMBv1 pipe connector is added.

TCP bind generation patches the payload to listen on payload.bind_port on the target. The provider connects to target.host:payload.bind_port after installation and emits an active SessionRef when the TCP connection opens. TCP callback generation patches the payload with payload.lhost and payload.lport, starts a provider listener, and adopts the accepted connection as the Squatter session. TCP install is intentionally record-driven until the provider has a real remote-execution installer for those transports.

Mesh and PKI-Backed TLS

Squatter also implements Hovel's destination-scoped Mesh contract. The provider reports one root node plus one stable child node for every surveyed or dialed host/port, a read-only destination survey task, and two stream protocols: squatter for a raw TCP-bind flow and squatter+tls for a TLS 1.3 flow. Both become ordinary Hovel SessionRef values, so the daemon's Mesh bridge can expose them without learning the Squatter frame grammar.

Every endpoint node has a direct provider link and optional route. Routed stream creation validates the route shape and terminal node, then rejects any conflicting node id, destination host, or destination port. This keeps multiple live Squatter endpoints independently addressable and prevents a caller from using valid topology metadata to redirect a stream elsewhere.

Payload TLS is backed by the provider's strict payload-tls-server standard stamp slot. That slot accepts only a hovel.pki.bundle/v1 projection with private-key bytes, the tls-server purpose and profile, the payload consumer type, and the portable-x509 compatibility target. Hovel resolves the selected assignment into one private bundle containing the leaf, private key, chain, trust anchors, validity, fingerprints, and key-establishment policy. The provider rejects unknown fields and verifies certificate/key consistency, ordered chain termination, EKU, validity, freshness, bundled CRLs, and exact named-group policy before writing a versioned SQPKI001 manifest into a configured TCP-bind PE. The current payload requires the complete ordered portable classical set: x25519, secp256r1, secp384r1, and secp521r1. Subsets, reordered preferences, and other policies are rejected rather than silently diverging from the compiled wolfSSL profile. SHA-256 bindings cover both the canonical bundle and the complete runtime manifest. Callback and named-pipe artifacts are rejected by this stamp slot until their TLS roles have an end-to-end implementation.

Squatter Mesh TLS Boundary
  1. TLS Mesh client
  2. Hovel Session carrying TLS ciphertext
  3. provider and Mesh byte pass-through
  4. payload wolfSSL terminates TLS 1.3
  5. Squatter frame mux

The certificate and PKCS#8 key are payload-owned: they are stamped into the PE rather than retained as provider runtime state or installed in a Windows certificate store. The no-CRT payload validates all lengths, counts, and digests before initializing statically linked wolfSSL. An unstamped payload retains plaintext TCP behavior, while a present but malformed or modified stamp fails startup closed and never falls back to plaintext. Named pipes remain protected at their existing SMB transport boundary. TCP accepts create a session immediately, while its TLS handshake runs on the session reader thread with bounded socket I/O, so a silent peer cannot monopolize the listener.

The pinned wolfSSL 5.9.2 profile uses the Windows NT 4.0 API surface and does not depend on Schannel, Secur32, Crypt32, CNG, or the Windows certificate store. This prevents TLS from raising Squatter's legacy API floor, although the complete provider continues to report Windows 7 until older releases are exercised in the automated matrix. Static wolfSSL distribution is subject to wolfSSL's GPLv3 or commercial licensing terms.

Prepared Target Footprint

Prepare is pure/local. It generates planned target-side identifiers, but it does not build the final executable, contact the target, stage files, or start services. The target review flow lets the operator edit prepared values before confirmation and makes them immutable afterward; the current runtime can call step.prepare, but generic pre-confirm editing and persistence are still pending.

credential username: random neutral string
credential password: random high-entropy string
service name:       random neutral string
service display:    random neutral or configured string
staged filename:    random neutral .exe by default
pipe name:          random neutral string
collision strategy: fail after confirmation

Target-side names must not include Hovel, Squatter, run ids, or other tool-identifying strings. Evidence stores the complete mapping plainly, including plaintext created credentials, so the operator can remediate even if automated cleanup fails.

Session Ownership

Squatter sessions are provider-managed and Hovel-brokered. A live install is a durable installed payload record; a session is an attachable view over that target-side instance. Hovel sees a SessionRef with kind, transport, state, installed payload id, and capabilities. The provider owns framing, task IDs, file chunking, command/result mapping, retries, and transport details.

session kind        agent
session owner       payload_provider
transport           squatter/smb-named-pipe, squatter/tcp-bind, or squatter/tcp-callback
capabilities        host.info, file.get, file.put, file.stat, registry.query,
                    eventlog.query, process.exec, process.list, payload.cleanup

The provider may present a shell-like operator experience through Hovel's session API, but the LP-to-payload protocol should be typed and robust. Hovel should not treat a Squatter session as a raw cmd.exe terminal. Operators can discover typed session actions with session commands or session capabilities, then call them with session call without touching the interactive byte stream.

Operators may detach from a live Squatter session and reconnect later when the transport supports it. SMB named-pipe and TCP bind reconnect with an active-connect strategy. TCP callback sessions attach to an accepted listener connection; if no callback is present, the installed payload remains installed or unreachable according to the provider's explicit observation.

Installed Payload Tracking

Squatter install steps return an explicit installed-payload descriptor when the target-side agent is staged and started. Hovel persists that descriptor as workspace inventory with a short handle such as p1. The record stores normalized fields for listing and filtering, plus Squatter-owned reconnect and cleanup JSON with schema names and versions.

{
  "provider": "squatter",
  "payloadId": "squatter/windows/x86/xp-sp3/tcp-bind/pe-exe",
  "payloadVersion": "0.1.0",
  "target": "10.0.0.42",
  "targetId": "t1",
  "instanceKey": "stamp-or-agent-id-when-known",
  "transport": "tcp-bind",
  "endpoint": "10.0.0.42:9101",
  "state": "installed",
  "supportsReconnect": true,
  "supportsMultipleSessions": false,
  "reconnect": {
    "providerId": "squatter",
    "schema": "squatter.reconnect.tcp_bind",
    "schemaVersion": "1",
    "descriptor": { "host": "10.0.0.42", "port": 9101 }
  }
}

payloads connect p1 passes the stored reconnect descriptor back to Squatter through the provider connect_session operation. On success, Hovel creates a normal session visible in session list and links it to the installed payload. On failure, Hovel records the exact provider error and marks the installed payload unreachable; it does not delete the record.

payloads commands p1 and its payloads capabilities p1 alias ask the provider for the installed payload's typed action catalog. payloads call p1 <command> invokes one provider-owned action without opening an interactive session. The convenience commands payloads getfile, payloads putfile, and payloads cmd are compatibility shims over the same provider call path. Command results can produce artifacts; Hovel materializes those artifacts into the workspace and prints the artifact id and path instead of binary bytes.

payloads register-squatter <target> --host <host> --port <port> is a development and lab helper for manually started TCP-bind Squatter instances. It creates a normal installed-payload record with a provider-owned reconnect descriptor, so the same payloads connect, payloads commands, and payloads call paths can operate it.

Squatter may support more than one reconnect endpoint for a deployed agent later. The v1 descriptor may contain one endpoint, but the storage and provider JSON should allow endpoint lists without changing the core installed-payload table.

Throw Planning

The target throw plan hash covers operator intent, the live step contract snapshot, and deterministic configuration: selected provider steps, selected transport, provider config, prepared target-side names, expected format, expected platform, compatibility facts, and whether listener or connector steps are required. The current source records reviewed intent, target IDs, module or step references, and configuration; full contract snapshots and prepared-value hashing remain pending.

The plan hash should not include final generated payload bytes, runtime session IDs, or observed connection handles. Those values are produced after confirmation and recorded as events and artifacts. Post-confirmation contract drift blocking is part of the target safety model and is not implemented yet.

Chain validation must enforce declared requirements. Validation should fail when a Squatter install step lacks a compatible payload artifact, when SMB mode lacks an SMB credential capability, or when a connector step lacks an installed payload instance and endpoint in an allowed state.

Throw Execution

Squatter steps produce capabilities, artifacts, evidence, and state transitions. Generation failures, listener bind failures, install failures, and connection failures are reported at the responsible step with exact evidence.

Throw Execution Flow
  1. for each target, query live step contracts
  2. prepare local names and transport config
  3. confirm snapshot
  4. generate final per-target payload artifacts
  5. materialize artifacts in workspace
  6. install payload with declared capabilities
  7. connect or listen for session when required
  8. keep provider alive while it owns active sessions
  9. cleanup target and provider-side state from cleanup handles

Hovel records generated payloads in the normal artifact list. Operators should see metadata such as name, kind, provider module, target, throw, SHA-256, size, and controlled workspace path. Hovel should not render binary bytes in ordinary terminal output.

If install succeeds but session connection fails, Squatter returns the installed-payload descriptor plus a warning evidence record. Hovel persists the installed payload and marks it unreachable. The CLI must print the exact target, staged path, service name, pipe or port, plaintext SMB credential when one was created, connection endpoint, exact error, active cleanup handles, and the reconnect and cleanup commands.

Build Contract

Squatter now lives in the modules/ slice, outside the core Hovel framework workspace. Use task modules:ci for the hermetic module gate, task modules:wine-test for the host Wine boundary, task modules:wine-docker-test for both real PE functional runs, task modules:squatter:coverage for the 90% aggregate Go ratchet, and task release:modules-package for installable archives. Keep Squatter labels and host dependencies behind those Task contracts rather than calling Bazel or Wine directly.

The Windows payload uses repo-local Bazel rules over a pinned MinGW archive:

url = "https://github.com/vibepwners/windows-toolchains/releases/download/v0.0.1/mingw-w64-gcc-15.2.0-ucrt-posix-dw2-i686-w64-mingw32-linux-x86_64.tar.xz"
sha256 = "<recorded checksum>"

The helper stays intentionally narrow so Squatter can build PE payloads without turning the repository into a general Windows C/C++ toolchain abstraction.

cc_binary(
    name = "squatter.exe",
    srcs = ["squatter.c"],
    linkopts = [
        "-nostdlib",
        "-Wl,-e,_sq_nocrt_entry",
        "-Wl,--subsystem,console",
    ],
)

windows_multiplatform(
    name = "squatter_all",
    binary = ":squatter.exe",
)

Defaults should encode the intended no-CRT/no-import posture:

-ffreestanding
-fno-stack-protector
-fno-asynchronous-unwind-tables
-nostdlib
-nodefaultlibs
-Wl,--entry,<entry>

PE inspection verifies the produced payload is i386, has the expected subsystem and entry point, embeds Hovel/Squatter markers, and has no unexpected import table or CRT dependency.

Windows Payload Contract

The Windows payload is not cross-platform code. The current payload is i386, uses a no-CRT entry path, resolves Win32 APIs through the PEB, and keeps Windows-specific headers centralized under base/win.h. The provider reports conservative compatibility until broader Windows versions are tested.

Provider-reported Squatter capabilities:

  1. Collect native host facts.
  2. Download, upload, stat, and hash files.
  3. Query registry values, recent event log records, logical drives, local shares, and file ACLs.
  4. Run processes, launch through an interactive user token when available, list processes, and kill processes by PID.
  5. Report payload status and request auditable payload cleanup.

The concrete provider-owned commands currently exposed through Hovel are:

wininfo
process.list
process.run
process.run_as_user
process.kill
payload.status
payload.cleanup
file.stat
registry.query
eventlog.query
drive.list
share.list
acl.stat
getfile
putfile
cmd

Safe collection commands are marked read-only in the provider catalog. Process execution, process termination, file upload, and payload cleanup are marked destructive. The payload also includes an echo module for mux and functional tests, but that test module is not an operator command.

Current Implementation Status

The current source-backed Squatter path includes:

  1. Core capability schema names for RemoteExecutionCapability, CredentialCapability, PayloadArtifact, PayloadInstance, TransportEndpoint, SessionRef, and CleanupHandle.
  2. Generic step JSON-RPC dispatch through step.describe, step.prepare, step.execute, and step.cleanup.
  3. Live contract planning that resolves enabled module steps to capability bindings or typed missing requirements. Full contract snapshot persistence and post-confirmation drift blocking remain future safety work.
  4. SMB install preparation for neutral service names, staged filenames, pipe names, endpoint config, and planned cleanup handles. Credential creation remains a separate module step.
  5. SMB named-pipe connect through explicit credentials, TCP bind connect, and TCP callback listener/connect paths that can emit SessionRef capability records.
  6. Durable installed payload inventory with short handles, provider-owned reconnect descriptors, provider-owned cleanup descriptors, and Hovel-owned persistence.
  7. Provider-owned payload command surfaces for host facts, process list/run/kill, file get/put/stat/hash, registry query, event log query, drive/share listing, ACL stat, payload status, payload cleanup, and cmd.exe execution.
  8. CLI and MCP typed command discovery and invocation through payloads commands/payloads call, session commands/session call, hovel_payload_capabilities/hovel_payload_call, and hovel_session_capabilities/hovel_session_call.
  9. Human-readable command tables with safe/write/destructive effect labels and pretty-printed JSON command output.

The Squatter path still avoids:

  1. MS17-010-specific Squatter protocol handling.
  2. Hidden helper-step insertion by the planner.
  3. Target-visible Hovel or Squatter ownership markers.
  4. Command-only Squatter staging as a fallback for the representative SMB chain.
  5. Silent credential redaction in normal operator CLI output.

The representative MS17-010 chain should require an upload-capable remote execution path for SMB install. If the MS17-010 exploit cannot honestly produce an upload_file sub-capability, Squatter SMB install must not pretend it can use the MS17-010 exploit for staging. A separate command-only installer variant may be designed later if it becomes operationally necessary.