TCPK -- Thick Client Pentest Kit
Evidence over assertion. A scanner that proves its findings.
TCPK is a portable, dependency-light PowerShell engine for auditing Windows desktop applications -- MSIX, MSI, ClickOnce, and portable apps; managed .NET, native PE, and Electron alike. It is built around one opinion: a string match is a lead, not a finding. Every dangerous-API hit is lifted to IL with Mono.Cecil and run through a bounded source-to-sink taint pass before it is allowed to call itself "Confirmed", every score is the real FIRST.org CVSS v4.0 number derived from an attack-archetype vector, and every finding carries CWE, MITRE ATT&CK, and OWASP TASVS provenance.
00TL;DR
Point TCPK at an installed app or an .msix. It collects findings from ~130 checks across a dozen buckets, kills the false positives deterministically by reading the IL, scores what survives with a faithful CVSS v4.0 implementation, correlates co-occurring weaknesses into exploit chains, and writes an analyst-grade HTML + Excel report plus machine-readable JSON and a CycloneDX SBOM.
Import-Module .\TCPK\TCPK.psd1
# full static + posture audit, reports to .\out\App
Invoke-TcpkAudit -Target 'C:\Program Files\WindowsApps\Contoso.App_1.0.0_x64__abc' `
-OutDir .\out\App -Acknowledge
# add local-only AI triage (Ollama); never leaves the box
Invoke-TcpkAudit -Target 'C:\Apps\Contoso' -Acknowledge -EnableLlm
# CI gate: non-zero exit if anything HIGH or worse survives verification
Invoke-TcpkAudit -Target 'C:\Apps\Contoso' -Acknowledge -FailOn HIGH
01Why thick clients are their own threat model
Web apps keep their logic on the server. Thick clients ship it to the attacker. The moment an MSIX lands on disk, the adversary owns a copy of your binaries, your config, your embedded URLs, your trust decisions, and -- far too often -- your secrets. The interesting questions are not "is there an injection point on a form" but:
- What did you bake into the artifact? Private keys, cloud account keys, connection strings, JWTs, hardcoded credentials -- readable by anyone who downloads the installer.
- What trust does it grant the OS and other processes? Custom URI schemes (
app://), file-type associations, COM servers, named pipes, RPC interfaces, services, scheduled tasks -- each one an attacker-reachable entry point. - Does it validate what it is told? A URI handler that hands its argument to
Process.Start; aBinaryFormatter.Deserializeover a stream from disk; a certificate callback thatreturn trues. - Is the binary even defended? ASLR/DEP/CFG/GS, Authenticode, strong naming -- or stripped and replaceable in a user-writable directory.
TCPK is organized around those questions, not around a generic OWASP web checklist bolted onto a desktop app.
02Design principles
1. Evidence over assertion
A regex that finds BinaryFormatter proves the type is referenced, not that Deserialize() is invoked, reachable, and fed by external input. TCPK refuses to print a confident verdict it cannot back with IL. The confidence model is first-class output.
2. Data-driven detection
Secret patterns, dangerous call-sites, and deserialization sinks live in Data/secrets.json as regex rules with severity + CWE. Adding a detection is a JSON edit, not a code change. Rules are compiled once and prefiltered with a cheap literal.
3. Local-first, air-gap friendly
Zero third-party PowerShell modules, no Excel install (the workbook is written byte-by-byte), no internet required. The optional AI layer defaults to local Ollama and refuses to send decompiled IL to a cloud provider unless you explicitly open the gate.
4. Honest scoring
The displayed CVSS v4.0 number is computed by a faithful port of the FIRST.org reference algorithm from an attack-archetype vector, so a LOCAL-only issue is never mislabelled with a network attack vector, and the severity badge always agrees with the computed band.
03Architecture
TCPK is a single PowerShell module plus three front-ends (CLI, WinForms GUI, MCP server) over a shared engine. The module auto-loads its class, private helpers, and public cmdlets; the function name equals the file basename; public cmdlets are auto-exported.
TCPK/
TCPK.psd1 # manifest: ModuleVersion, ReleaseNotes, exports
TCPK.psm1 # dot-sources Classes/ -> Private/ -> Public/
Classes/
TcpkFinding.ps1 # [TcpkFinding] -- the universal output object
Private/ # _-prefixed helpers (not exported)
_Finding.ps1 # New-TcpkFinding factory + CVSS archetype map
_Cvss40.ps1 # FIRST.org CVSS v4.0 base-score engine
_Decompile.ps1 # Mono.Cecil IL bridge (callsites, taint, TLS verdicts)
_Attack.ps1 # MITRE ATT&CK technique map
_Tasvs.ps1 # OWASP TASVS + Desktop App Top 10 map
_VerifyHint.ps1 # per-rule manual re-validation playbook
_PeReader.ps1 # raw PE parser (DllCharacteristics, load-config, ...)
_Llm.ps1 # multi-provider LLM client (local-first, cloud-gated)
_Strings.ps1, _UriSink.ps1, _Msix.ps1, _Xlsx.ps1, ...
Public/ # one cmdlet per file, grouped by bucket
Discovery/ Manifest/ OsIntegration/ Credentials/ Runtime/
Network/ WebView2/ Logging/ Memory/ AntiDebug/ Verify/
Recon/ Report/ Exploit/ Llm/
Invoke-TcpkAudit.ps1 # the top-level orchestrator
Data/
secrets.json # rules[], callsite_patterns[], deser_tokens[]
cvss/cvss40-lookup.json# 270-entry macrovector -> base-score table
cve/catalog.json # curated component CVE catalog
checklist/thick-client-checklist.json # 55-case methodology
Start-TCPKGui.ps1 # WinForms console
Start-TcpkMcpServer.ps1 # Model Context Protocol server for AI agents
The finding object
Every public cmdlet returns [TcpkFinding] instances. Report generators are the only code that knows about formatting; everything else passes typed objects down the pipeline. This is the contract:
class TcpkFinding {
[string] $Module # bucket: static | manifest | os | creds | runtime |
# network | webview2 | logging | memory |
# antidebug | exploit | chain | meta
[string] $RuleId # stable dotted id, e.g. 'callsites.command-execution'
[string] $Severity # INFO | LOW | MEDIUM | HIGH | CRITICAL
[string] $Confidence # Confirmed | Inferred | Unverified | Skipped
# (refined at verify time to 'Confirmed (IL)',
# 'Likely-FP (IL)', 'Confirmed (LLM)', ...)
[string] $Title
[string] $Description
[string] $File # primary path / key the finding applies to
[string] $Evidence # observed value (secrets redacted before storing)
[string[]] $Affected # aggregation: every occurrence of the SAME rule
[string[]] $Cwe # e.g. 'CWE-798'
[string] $Impact # else derived from Severity at report time
[string] $Cvss # explicit v4.0 vector (e.g. a real NVD vector) or
# assigned by attack archetype at report time
[string] $Fix
[string] $Timestamp # ISO-8601 UTC
}
A representative finding, post-verification, serialized to findings.json:
{
"Module": "static",
"RuleId": "callsites.command-execution",
"Severity": "MEDIUM",
"Confidence": "Confirmed (IL)",
"Title": "Process / shell execution call site in Updater.dll",
"File": "C:\\Apps\\Contoso\\Updater.dll",
"Evidence": "Process.Start, ProcessStartInfo",
"Cwe": ["CWE-78"],
"Impact": "Meaningful weakness that aids an attack chain ...",
"Cvss": "",
"Fix": "Allow-list the argument before it reaches the process-launch sink.",
"Description": "... [TCPK IL: reachable call where external input reaches the
sink (1 call site) -- the method reads an external source (file / registry /
network / IPC / HTTP request) or a caller parameter flows into the argument.
Treat as a real injectable path and review.]"
}
Detection buckets
Checks are grouped A-L. The CLI runs them in order; the GUI runs the same set and streams each result live.
| Bucket | Module | What it interrogates | Examples |
|---|---|---|---|
| A · Static binary | static | PE imports/exports, strings, secrets, endpoints, deserialization, dangerous call-sites, TLS bypass, XXE, WCF, reflection, P/Invoke, packers, Electron, unsafe CRT, CSV injection, single-file extraction | secrets.*, callsites.*, deser.*, tls-bypass.*, csv.formula-injection-risk |
| B · MSIX manifest | manifest | AppxManifest capabilities, protocol/file-type extensions, appExecutionAlias shadowing, COM, full-trust | msix.protocol-handler, msix.alias-shadowing |
| C · OS integration | os | Registry footprint, autostart, services, scheduled tasks, UAC, COM servers, DLL search order, file associations | protocol-handler, uac.*, service.* |
| D · Credential storage | creds | PEM/PFX key material, DPAPI blobs, Credential Manager, browser/Electron token stores (App-Bound vs DPAPI) | keymaterial.*, browser.cookie-key-dpapi |
| E · Runtime (live) | runtime | Process mitigations, loaded-module signatures, listening ports, handles, window/GUI inspection, token, child processes, in-memory secrets (requires -ProcessName) | process.*, ports.*, memsecret.* |
| F · Network | network | Backend endpoints, cleartext schemes, TLS posture, gRPC/SignalR channels, self-hosted listeners | scheme.cleartext-http, rpc.grpc-insecure-credentials |
| G · WebView2 | webview2 | Navigation targets, virtual-host mapping, host-object bridge, web-message handlers, script injection | webview2.add-host-object |
| H · Logging/telemetry | logging | PII in logs, telemetry sinks, ETW providers | log.*, pii.* |
| I · Memory hygiene | memory | WER/pagefile exposure, secret-in-memory residency, dump policy | mem.*, wer.* |
| J · Anti-tamper | antidebug | Anti-debug, timing checks, self-integrity, packing/obfuscation | antidebug.*, packer.* |
| K · Exploit (gated) | exploit | PoC template generation for confirmed findings -- behind Enable-TcpkExploit -Acknowledge | K01..K06 |
| L · Verify / Recon | chain | IL proof (Confirm-*), target profile, attack surface, exploit-chain correlation, SBOM | chain.uri-handler-sink-rce |
04The audit pipeline
Invoke-TcpkAudit is a deterministic, ordered pipeline. The ordering matters: deduplication and false-positive killers run before scoring; IL verification runs before the LLM so the model and the reports inherit the proven verdicts; aggregation runs after the LLM so per-file judgments are preserved; the per-occurrence snapshot is taken before aggregation so recon and attack-surface views see every endpoint individually.
Any check that throws becomes a single meta.cmdlet-failed INFO finding rather than aborting the run -- a partial audit is always better than no audit.
05The confidence ladder
Severity answers "how bad if real". Confidence answers "how sure it is real". TCPK treats them as orthogonal and surfaces both. A finding climbs (or falls) the ladder as evidence accrues:
| Confidence | Meaning | Produced by |
|---|---|---|
Inferred | A substring / regex / type reference matched. The name is present; reachability and data-flow are unproven. | Static scanners (bucket A-J) |
Confirmed | A structural fact verified without a decompiler (a PFX loaded with an empty password; a registry value exists; a port is listening). | Direct probes |
Confirmed (IL) | Mono.Cecil proved the API is invoked, the enclosing method is reachable, and external input (a source API or a caller parameter) reaches the sink. | Confirm-TcpkCallsiteUsage |
Likely-FP (IL) | The IL says the API is never actually invoked (string-only match), or is only ever called with constant arguments. Severity is dropped to INFO / one notch. | Confirm-TcpkCallsiteUsage |
Confirmed (LLM) / Likely-FP (LLM) / Uncertain (LLM) | Advisory second opinion from a model that read the decompiled method. Adjusts confidence, never severity. | Invoke-TcpkLlmCodeJudgment |
This is the whole point of the tool. Most scanners stop at
Inferredand call it CRITICAL. TCPK makes the cost of a false positive explicit and then spends deterministic IL analysis to drive it down.
06The IL verification engine
The bridge in _Decompile.ps1 loads Mono.Cecil (which ships with ILSpy) and reads method bodies directly. The core primitive, Get-TcpkCallsiteUsage, answers three questions a substring scan cannot:
- Is the API actually invoked? A
call/callvirt/newobjto the sink, or did the rule match a mere string/type reference? - Is the enclosing method reachable? public / virtual / entry-point / event-handler-shaped / has any in-assembly caller.
- Does external input reach the dangerous argument? A bounded backward scan of the IL preceding the call, classifying the argument as
constant(ldstr/ldc),dynamic(ldarg/ldloc/ldfld), ortainted.
Interprocedural source-to-sink taint
"Tainted" is the signal that turns a maybe into a finding -- and it follows external input across method boundaries, not just within the sink's own method. A call site is tainted when:
tainted = methodHasSource the sink's method itself reads a source
OR sourceOrTaintedReturningCall inline: Process.Start(ReadConfig())
OR taintedLocal via local: var x = ReadConfig(); Process.Start(x)
OR taintedField cross-method carrier:
Configure() { _cmd = ReadConfig(); }
Run() { Process.Start(_cmd); }
OR (callerParameter AND reachable) a reachable method's own parameter feeds it
methodHasSource = the method calls one of:
System.IO.File::Read*/Open*, StreamReader::Read*, ReadAllText/Bytes,
Console::Read*, Environment::GetEnvironmentVariable/GetCommandLineArgs,
Registry::GetValue/OpenSubKey, WebClient/HttpClient downloads,
ReadAsStringAsync, NamedPipe/PipeStream::Read, Socket::Receive,
SqlDataReader, HttpRequest::get_Form/get_QueryString/get_InputStream, ...
A "tainted-returning method" (cached, 3-hop fixpoint over the call graph) is a
value-returning method that transitively reads a source. A "tainted field" is one
assigned such a value anywhere in the assembly. Both are PRECISE -- direct
assignment from a known source only -- so recall rises without new false positives.
The classifier is deliberately biased safe: it only calls an argument constant when no dynamic load appears in the window, so a real bug is never demoted on a miss. The decision matrix for an injection-class sink:
| IL observation | Verdict | Effect |
|---|---|---|
| API never invoked (call/newobj absent) | Likely-FP (IL) | severity -> INFO |
| Called only with constant args | Likely-FP (IL) | severity -1 notch |
| Reachable + external input reaches sink | Confirmed (IL) | "real injectable path" |
| Reachable + dynamic, no proven source | unchanged | review note, no over-claim |
Managed and native sinks
The engine matches both managed BCL sinks by declaring type and P/Invoke sinks by method name (so a native WinExec/CreateProcess/ShellExecute is treated like Process.Start). A declared-but-never-called P/Invoke capability (a keyboard hook, screen grab, or token impersonation that was copied in wholesale but never invoked) is correctly demoted to INFO. Deserialization is confirmed when an unsafe formatter's Deserialize() / ReadObject() is actually invoked -- and strengthened when the stream is tainted.
# standalone IL confirmation over a single assembly
Confirm-TcpkDeserialization -Dll .\Updater.dll
# Dll Formatter In Op Target
# Updater.dll BinaryFormatter Updater.Cache::Load call Deserialize
# the audit pipeline runs this automatically; you can also pipe findings:
$findings | Confirm-TcpkCallsiteUsage
-----BEGIN RSA PRIVATE KEY----- in a UI placeholder, or registers a normal app:// URI handler, will scream CRITICAL all day. The IL/structural layer is what separates "the string is here" from "the bug is here".07The CVSS v4.0 engine
A CVSS v4.0 base score is not a closed-form formula -- it is a 270-entry macrovector lookup plus a severity-distance interpolation. _Cvss40.ps1 is a faithful port of the FIRST.org reference calculator (cvss_score.js / cvss_lookup.js / max_severity.js / max_composed.js), so the number is derived from the vector, never guessed. The lookup table ships verbatim.
# the score is computed, and validated against the FIRST.org reference:
Get-TcpkCvss40Score -Vector 'CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N'
# Score Rating MacroVector
# 9.3 Critical 000200
Findings are not scored by a hand-typed number; they are scored by an attack archetype -- a representative 11-metric vector chosen for the bug family. This is what stops a local-only weakness from being mislabelled with a network attack vector:
| Archetype | Vector (abridged) | Score | For |
|---|---|---|---|
net-rce | AV:N VC:H VI:H VA:H | 9.3 | unsafe deserialization, poisoned update |
live-credential | AV:N VC:H VI:H | 9.3 | cloud/API/SCM/SSH credential, network read+write |
net-mitm | AV:N VC:H VI:H VA:N | 8.7 | TLS cert/hostname bypass |
embedded-key | AV:L VC:H VI:H | 8.5 | private key / unprotected keystore in the artifact |
local-privesc | AV:L PR:L VC:H VI:H VA:H | 7.x | writable ACL / hijack / service path |
shipped-secret | AV:L VC:H VI:N | 6.9 | confidentiality-only secret on disk |
weak-crypto | AV:N AC:H AT:P VC:L VI:L | low | weak hash / mode / RNG |
For genuinely mixed families (raw call-sites, registry), TCPK refuses to fabricate a vector and prints "assign exact CVSS v4.0 vector per finding" instead -- and for credential findings it picks the archetype by severity tier, so the badge and the computed band always agree.
08Exploit-chain correlation
Most checks score a single condition in isolation. Real compromise is usually a chain. Get-TcpkExploitChains raises a finding above its parts when the preconditions co-occur -- and, crucially, it only chains links that survived verification (it skips anything already demoted to Likely-FP, and a chain is only as real as its weakest link).
chain.unsigned-update-writable (CRITICAL)
= updater applies payloads with NO signature check
+ a standard user can write to a directory the app loads from
chain.web-host-bridge-rce (CRITICAL)
= WebView2 loads loosely-scoped / external content
+ a .NET<->JS host bridge is exposed to it
chain.uri-handler-sink-rce (CRITICAL)
= the OS hands attacker-controlled URIs/files to the app
+ activation input reaches a REAL RCE sink
(command-execution / deserialize / path-build -- not a weak call-site)
The narrative, contributing conditions, and a per-link verify playbook are emitted with the finding so the analyst can confirm each link and then prove the path end-to-end.
09Detection catalog (selected)
Detections are data-driven. A dangerous call-site rule is a JSON object; Test-TcpkCallsites matches its patterns, and Confirm-TcpkCallsiteUsage later verifies any hit:
{
"id": "command-execution",
"title": "Process / shell execution call site",
"severity": "MEDIUM",
"cwe": ["CWE-78"],
"patterns": ["Process.Start","ProcessStartInfo","ShellExecute",
"CreateProcess","WinExec","cmd.exe","UseShellExecute"]
}
A secret rule requires a real body, not just a header marker (so UI placeholders do not false-positive):
{
"id": "pem-private-key",
"title": "PEM-encoded private key",
"severity": "HIGH",
"pattern": "-----BEGIN (?:RSA |EC |DSA |OPENSSH |PGP |ENCRYPTED )?PRIVATE KEY-----[A-Za-z0-9+/=\\s\\\\]{100,6000}-----END (?:RSA |EC |DSA |OPENSSH |PGP |ENCRYPTED )?PRIVATE KEY-----",
"cwe": ["CWE-321","CWE-798"]
}
| Class | Representative rules | CWE |
|---|---|---|
| Hardcoded secrets | AWS/Azure/GCP keys, connection strings, PEM/SSH/PuTTY keys, GitHub/Slack/Stripe tokens, JWTs | CWE-798, CWE-321 |
| Injection sinks | callsites.command-execution, sql-command-construction, ldap-query, ssrf-request-build, path-traversal-build, nosql-command-construction | CWE-78/89/90/918/22 |
| Unsafe deserialization | deser.binaryformatter, netdatacontractserializer, soapformatter, typenamehandling | CWE-502 |
| Transport security | tls-bypass.cert-callback-accepts-all, scheme.cleartext-http, rpc.grpc-insecure-credentials | CWE-295, CWE-319 |
| Crypto misuse | weak-symmetric-crypto (DES/3DES/RC2/RC4/ECB), weak-hash-md5-sha1, weak-rng, base64-as-encryption | CWE-327/328/338 |
| Parser / archive | xxe.*, zipslip.*, csv.formula-injection-risk | CWE-611/22/1236 |
| Native memory | native.unsafe-crt (strcpy/sprintf/gets + SDL-banned lstrcpyW/StrCpyW/_snprintf family) | CWE-120/134/787 |
| Capability abuse | keylogging / screen capture / clipboard / token impersonation / COM elevation / UAC-bypass registry | CWE-250/269 |
TCPK transparently unpacks what it audits: it extracts .NET single-file (PublishSingleFile) bundles and re-scans the assemblies inside, cracks Electron ASAR archives and shipped Java jar/war/ear, parses MSIX manifests, and fingerprints Tauri and Flutter desktop apps.
10Binary hardening + code-signing matrices
Posture is reported as a matrix, not as noise. Get-TcpkPeHardening parses the PE directly (no dumpbin) and reports per-DLL exploit mitigations; missing mitigations are posture, not per-DLL findings, because a missing canary is defense-in-depth, not an exploitable bug on its own.
Get-TcpkPeHardening -Path .\out\extracted | Format-Table DLL,ASLR,DEP,CFG,GS,Status
# DLL ASLR DEP CFG GS Status
# app.dll Yes Yes Yes Yes HARDENED
# legacy.dll Yes Yes No No PARTIAL
# vendor.dll No Yes No No WEAK
Columns: ASLR, DEP, CFG, HighEntropyVA, SafeSEH, GS (the /GS stack cookie, read from the load-config SecurityCookie), ForceIntegrity. The signing matrix (Get-TcpkSigningMatrix) is information-only and timestamp-aware:
| Status | Meaning |
|---|---|
SIGNED / CATALOG | valid Authenticode (embedded / catalog) |
EXPIRED-TS | cert expired but counter-signed by a timestamp authority -- still valid (amber) |
EXPIRED | expired, no timestamp (red) |
UNSIGNED / TAMPERED / UNTRUSTED | no signature / hash mismatch / chain does not build |
Each row carries the signer CN, digest algorithm, valid-from, and expiry. Both matrices render in the GUI, the HTML report, and dedicated Excel sheets, with JSON sidecars (hardening.json, signing.json).
11SBOM + CVE
TCPK emits a CycloneDX SBOM (sbom.cdx.json): every shipped component with name, version, publisher, SHA-256, and a correct purl (pkg:nuget/... derived from deps.json for managed components, native for the rest) -- consumable by Dependency-Track, Grype, or OSV. Matched CVEs from the curated catalog are embedded as a CycloneDX vulnerabilities[] array linked to the affected component bom-ref, and surfaced in the report. An optional -OnlineCve switch additionally queries the OSV API (off by default; sends only the package name + version, fails closed with no network) and merges those CVEs into the same vulnerabilities[].
Get-TcpkCveMatches -Path .\out\extracted | Format-Table Status,Severity,Cve,Package,ShippedVersion,FixedVersion
# Status Severity Cve Package Shipped Fixed
# Vulnerable HIGH CVE-2024-21907 Newtonsoft.Json 12.0.0 13.0.1
Managed (NuGet) matches are version-compared and reliable; native/embedded matches are reported as Present / PossiblyEmbedded so you verify the embedded build before treating them as confirmed.
12The AI verification layer (optional, local-first)
The LLM stage is an advisory second reader, not the source of truth -- the deterministic IL pass already ran. It annotates code-construct findings with a (LLM) verdict and reasoning; it never changes severity. It is model-agnostic:
| Provider | Dialect | Notes |
|---|---|---|
ollama (default) | local | fully offline; nothing leaves the box |
claude | anthropic | cloud (gated) |
openai / gemini / grok / deepseek | openai chat-completions | cloud (gated); free-text model field |
custom | openai-compatible | any self-hosted endpoint |
-AllowCloudLlm (CLI) or confirm the warning dialog (GUI) to send anything off-box. The local Ollama path needs no gate.# offline triage with a local model:
Invoke-TcpkAudit -Target 'C:\App' -Acknowledge -EnableLlm
# explicitly permit a cloud model (IL leaves the machine):
Invoke-TcpkAudit -Target 'C:\App' -Acknowledge -EnableLlm -AllowCloudLlm
13ATT&CK, OWASP TASVS, and the 55-case checklist
Every finding is mapped, at report time, to a MITRE ATT&CK technique and to OWASP TASVS v1.8 + the Desktop App Security Top 10. Mapping is a pure prefix lookup over the RuleId, so it stays in sync with the [TcpkFinding] schema. Findings are also auto-correlated to a 55-case thick-client methodology (TC01-TC55); the auto-status is TCPK's read, and the tester sets the final PASS/FAIL -- a NO-FINDINGS status is explicitly not a pass.
Get-TcpkAttackText 'callsites.command-execution' # T1059 Command and Scripting Interpreter
Get-TcpkTasvsText 'secrets.aws-access-key-id' # TASVS-STORAGE ...; DA3 Sensitive Data Exposure
14Reporting outputs
One audit, many consumers. The output directory is self-contained:
| File | Audience | Contents |
|---|---|---|
index.html | analyst | severity-grouped findings, live filter/search, rule summary, recon, CVE, hardening + signing matrices, SBOM; computed CVSS + ATT&CK + TASVS + verify playbook per finding |
intel.html | analyst / triage | self-contained offline "program intelligence" dashboard: severity + confidence with the evidence ladder explained, a classified recon endpoint map, and filterable per-finding cards -- one file, no server, no CDN |
report.xlsx | reviewer / triage | Summary, Findings, Checklist, DLL Hardening, DLL Signing, CVEs, Recon, SBOM sheets (pure-PowerShell, no Excel needed) |
findings.json | tooling / CI | the full typed finding pipeline |
sbom.cdx.json | supply-chain | CycloneDX components + vulnerabilities[] |
hardening.json / signing.json | posture | per-DLL mitigation + code-signing matrices |
profile.json / attack-surface.json | recon | app identity + every endpoint, port, handler, COM server, pipe, file association |
exploits.json | red team | correlated chains + exploit plan |
run.jsonl / run.log | audit trail | structured + human run log |
15Usage
CLI
Import-Module .\TCPK\TCPK.psd1
# full audit (MSIX, install dir, or single binary)
Invoke-TcpkAudit -Target 'C:\Path\App.msix' -OutDir .\out\App -Acknowledge
# include live-process checks (ports, handles, in-memory secrets)
Invoke-TcpkAudit -Target 'C:\Apps\Contoso' -ProcessName Contoso -Acknowledge
# run a single check in isolation
Test-TcpkSecrets -Path 'C:\Apps\Contoso'
Get-TcpkSigningMatrix -Path 'C:\Apps\Contoso'
$findings | Confirm-TcpkCallsiteUsage # IL-verify a finding set
Get-TcpkCvssVector $finding # the computed v4.0 vector
GUI
# recommended launcher (handles STA + module-path resolution)
.\TCPK.bat
# or directly
powershell -STA -ExecutionPolicy Bypass -File .\Start-TCPKGui.ps1
Pick a target, click Run Audit, watch the progress bar and findings stream in. Tabs expose Recon/Target, Exploit/CVE, SBOM, DLL Mitigation Matrix, DLL Signing, and the live log. The AI provider, model (free-text), and key are configured inline.
Web control panel
# loopback-only (127.0.0.1), token-gated, discovery-only -- prints a URL+token, opens your browser
.\TCPK-WebUI.bat
The same audit, driven from the browser: live progress, pause/resume/cancel, and result tabs (Findings, Recon, SBOM, DLL Mitigation, DLL Signing, Logs, Reports) with report downloads. Every /api/* call requires a per-session X-TCPK-Token header, the Host header is checked, and the gated exploit bucket is never reachable -- so a web page you happen to visit cannot drive it (no localhost CSRF / DNS-rebind).
MCP server (for AI agents)
# expose TCPK's cmdlets as Model Context Protocol tools
powershell -File .\Start-TcpkMcpServer.ps1
16Install
- Windows PowerShell 5.1+ (the GUI is WinForms; the engine also runs under PowerShell 7).
- No third-party PowerShell modules. Reporting needs nothing extra.
- Optional: ILSpy (for the bundled Mono.Cecil that powers IL verification) and Ollama (for fully-local AI triage).
# clone / unzip, then:
Import-Module .\TCPK\TCPK.psd1
Get-Command -Module TCPK | Measure-Object # 178 cmdlets
Invoke-TcpkAudit -Target '<app>' -OutDir .\out -Acknowledge
17Authorization & ethics
-Acknowledge (CLI) before it runs. The exploit / PoC bucket is gated separately behind Enable-TcpkExploit -Acknowledge and emits templates, not weapons. Audit outputs can contain real secrets extracted from a target and are excluded from version control by default -- never commit out/. Use it on your own software, a contracted engagement, written bug-bounty scope, or a lab.