Repository navigation
v2.0.1
What's Changed
Security Hardening — Full Niche Operator Quality Pass (FIX-01 – FIX-09)
-
FIX-01 (CWE-400 / Silent Data Corruption): Fixed closure rebinding bug in
download()stream collector (pwnshell.py:788–822).bytearray += bytesinside the inner closure was rebinding the local parameter name rather than mutating the shared buffer — outerbufstayed empty across all chunks. Replaced with in-placebuf.extend(chunk)and added diagnostic separation between empty-stream and collection-failure error paths. -
FIX-02 (CWE-78 / CWE-88): Resolved PowerShell single-quote injection in
upload()user-supplieddstpath (pwnshell.py:631–674). Replaced all single-quoted PS strings with double-quoted equivalents escaped via_pde()and enforced-LiteralPathacross every file manipulation cmdlet (Add-Content,Move-Item,Remove-Item). -
FIX-03 (CWE-73 / CWE-22): Fixed client-side trust of server-returned
Resolve-Pathoutput for local filename derivation (pwnshell.py:709–745). Default local destination is now derived strictly fromargs[0](user-supplied RPATH viaPureWindowsPath(args[0]).name), not from the server-resolved path. Added CWD-escape check: resolveddstis validated againstPath.cwd()and rejected on traversal. -
FIX-04 (CWE-693): Replaced static 6-byte AMSI patch signature
b8 57 00 07 80 c3(ret 0x80070057) with dynamicbuild_amsi_patch()polymorphic NOP generator (commands.py:38–54,evasion.py:54–75). Stub prefix is selected at runtime fromxchg ax,ax/nop/ multi-byte NOP variants viasecrets.choice(). Eliminated hardcodedStart-Sleep -Seconds 1OPSEC anti-pattern, replaced withVirtualProtect RWX → Marshal::Copy → RXtransition without deterministic sleep. -
FIX-05 (CWE-330): Replaced non-CSPRNG Mersenne Twister
random.randint/randbyteswithsecretsmodule across all security-sensitive call sites (commands.py:56–95,spnego.py:140–155,transports.py:295–310). XOR keys now usesecrets.randbelow(254) + 1(range [1,254], never zero-key), CredSSP nonces usesecrets.token_bytes(32), and PS namespace/classname identifiers usesecrets.token_hex(). -
FIX-06 (CWE-611): Integrated
defusedxml.ElementTreeas a safe XML parsing layer incore/psrp.py:5–16. Implementation monkey-patchesET.fromstringandET.parsewith defusedxml equivalents on import, with graceful fallback to stdlib ET ifdefusedxmlis unavailable.defusedxml>=0.7.1added torequirements.txt. -
FIX-07 (CWE-22 / CWE-78): Reimplemented missing
_validate_remote_path()regression (pwnshell.py:727–748). Strict regex whitelist_SAFE_WINPATH(^[A-Za-z]:\\...) and_SAFE_UNCPATH(^\\\\...) blocking directory traversal sequences (..), shell metacharacters ($,`,;,&,`), and control characters. -
FIX-08 (CWE-400): Enforced
MAX_SYNC_BYTES = 1 * 1024 * 1024output ceiling inrun_sync()(pwnshell.py:298–315). Overflow triggers output truncation with a warning and active runspace interrupt viasignal— prevents OOM from anomalous or adversarially large remote command output. -
FIX-09 (CWE-295 / CWE-300): Implemented cryptographic
pubKeyAuthecho-back verification in CredSSP handshake (transports.py:318–350). Server-returnedpubKeyAuthblob is unwrapped and compared againstSHA256(b"CredSSP Server-To-Client Binding Hash\x00" + nonce + pubkey)— MitM relay without the private key is now detectable. Added CredSSP v5 protocol fallback for legacy domain configurations.
Operational Improvements (OPT-01 – OPT-08)
-
OPT-01: Eliminated
CtrlCHandlerre-entry race condition inrun_with_interrupt()(pwnshell.py:317–335). Handler now covers the entire generator stream duration rather than re-entering per-chunk, closing the window where a Ctrl+C between iterations would fall through to the default SIGINT handler. -
OPT-02: Upload progress display overhauled (
pwnshell.py:648–665). Progress is now calculated in-flight before chunk submission and rendered as a real-time percentage (Upload X/Y (Z%)), replacing the post-chunk counter that lagged one iteration behind actual transfer state. -
OPT-03:
help()refactored from a hardcoded static string into a structuredCOMMAND_REGISTRYdict (pwnshell.py:65–135). All commands with syntax and descriptions are sourced from a single registry — new commands added to the plugin system appear in help output automatically without manual string editing. -
OPT-04: Transcript log writes now pass through
strip_ansi()(pwnshell.py:270–295). Raw VT100/ANSI escape codes no longer pollute.logfiles — output is clean ASCII/UTF-8 readable in any editor or SIEM pipeline. -
OPT-05: Replaced colloquial comment explaining SPNEGO/Kerberos AP_REQ extraction as "hijack" with proper technical rationale citing RFC 4121 NegTokenInit unwrapping and MS-WSMV §3.2.5.1 GSS-API framing requirements (
transports.py:257–270). -
OPT-06: Hardcoded magic constant
0xe0088237in NTLM Type 1 flags defined as named constantNTLM_FLAGS_STANDARDwith comprehensive bitwise breakdown documentation (spnego.py:38–53). -
OPT-07: Sensitive download buffer zeroized on MD5 integrity failure (
pwnshell.py:810–822).buf[:] = b'\x00' * len(buf)executes immediately on checksum mismatch before error is surfaced — prevents credential material from lingering in process memory after a corrupt transfer. -
OPT-08: Introduced
HISTORY_EXCLUDE_PATTERNregex filter onprompt_toolkitFileHistory(pwnshell.py:55–65). Commands matchingpassword|secret|hash|token|pfx|ccache(case-insensitive) are not written to.pwnrm_history, preventing credential strings from persisting to disk across sessions.
Niche Source Audit Fixes (NICHE-01 – NICHE-02)
-
NICHE-01: Replaced bare
except:clause inutfstr()(utils.py:34–40) withexcept Exception:— preservesKeyboardInterruptandSystemExitpropagation during XML decode errors, fixing silently swallowed Ctrl+C in long enumeration runs. -
NICHE-02: Sanitized single-quote injection in SMB target array construction in
get_shares_ps()(shares.py:18–28). Target strings are now stripped and double-quote escaped viat.strip().replace('"', '"')before splicing into the@('TARGETS')` PowerShell array literal.
Architectural Additions (Tier 1–3)
-
Ephemeral Session Encryption: In-memory
Fernetkey (_SESSION_KEY = Fernet(Fernet.generate_key())) generated at process start and used to encrypt all serialized session metadata written to~/.pwnrm/sessions/. Key exists only in process memory — session files are unreadable after PwnRM exits, eliminating post-engagement forensic credential recovery from session artifacts. -
Dual ETW Telemetry Evasion:
!evasionnow patches bothEtwEventWriteandEtwEventWriteFullinntdll.dll. Windows 10 21H2+, Windows 11, and Server 2022/2025 route a significant subset of kernel ETW events throughEtwEventWriteFull— patching onlyEtwEventWriteleft a detectable telemetry path open on all post-21H2 targets. -
Structured Loot
MANIFEST.json(core/loot.py):LootManagernow maintains an operationalMANIFEST.jsonunder~/.pwnrm/loot/with per-artifact entries includingfilename,type,category,source_command, ISO 8601timestamp,sha256,target, andpath. Bothstore_credential()andstore_artifact()update the manifest atomically viaO_CREAT | O_WRONLY | O_TRUNCwith0o600permissions. -
Multi-Session Output Labeling:
fan_out_execoutput streams in!session exec-allare now prefixed with[S:{sid} | {host}]tags per line, disambiguating interleaved output across concurrent sessions. -
Plugin Integrity Verification:
ModuleManagernow callsverify_plugin_integrity()before dynamic import — validates that each*.pymodule file is owned by the current user (st_uid == os.getuid()) and is not world-writable (st_mode & stat.S_IWOTH == 0). World-writable plugin files are rejected with a warning. -
BloodHound CE v6 Meta Headers:
!bloodhoundin-memory LDAP enumeration output now includes correct BloodHound CE v6 metadata headers{"meta": {"type": "...", "count": N, "version": 6, "methods": 0}}— previously missing headers caused silent rejection or misparse on BloodHound CE ingestion. -
Playbook
on_failCleanup Hooks:PlaybookRunnerYAML DSL now supportscleanup_on_failkeys per step. On mid-playbook failure, registered cleanup commands are executed in reverse order against the active session to remove staged artifacts before the operator loses access. -
RBCD Inspection in
!kerberos: AddedmsDS-AllowedToActOnBehalfOfOtherIdentityattribute enumeration. Surfaces write-capable principals and flags RBCD abuse candidates alongside existing S4U2Self / constrained delegation checks. -
Enterprise CA Health Check in
!adcs: Added CA certificate expiration detection and CRL/AIA distribution point reachability probing. Offline CDP and AIA misconfiguration are flagged as strong indicators of unmaintained PKI — correlated with elevated ESC4/ESC7 probability in the output. -
Type Annotations (PEP 484): Comprehensive type annotations added across
core/api.py,core/credentials.py,core/psrp.py,core/runspace.py,core/session_mgr.py,core/transports.py, andcore/utils.py. All public method signatures now carryOptional,Union,Tuple,List,Callable, andGeneratorhints compatible with Python 3.10+ strict mode. -
Test Suite Expansion: 22 unit and security regression tests passing at 100% success rate (
pytest/unittest), up from 21 in v2.0.0.
Full Changelog: v2.0.0...v2.0.1