Write recovered secrets and carved artifacts owner-only (0600/0700) - #30
Open
SackOfHacks wants to merge 1 commit into
Open
Write recovered secrets and carved artifacts owner-only (0600/0700)#30SackOfHacks wants to merge 1 commit into
SackOfHacks wants to merge 1 commit into
Conversation
pcapper deliberately does not redact what it recovers (reporting._redact_secret is a documented no-op), so reports, exports, the run log, carved artifacts, extracted files and decrypted streams routinely contain cleartext credentials, LDAP binds, SNMP community strings, session tokens and malware samples. None of those write paths restricted permissions, so under a default umask they landed at 0644 and output directories at 0755. On a shared analysis host — a jump box, a lab VM, a multi-analyst forensics workstation — every other local account could read recovered passwords out of another analyst's case directory. On an IR engagement those files are also evidence, and world-readable evidence is harder to defend on chain-of-custody grounds. Adds utils.restrict_permissions() (0600 files, 0700 dirs) and utils.restrict_dir_permissions() (mkdir + chmod), applied at every write path: - utils.safe_write_text — reports and JSON export - exporting.export_csv — CSV export and the _hosts CSV - exporting.export_sqlite — tightened immediately after sqlite3.connect(), so the database is never briefly readable while rows are being inserted - cli._log_event — the --log-file run log, which records event fields - cli — case directories and export output directories - carving — carved artifacts and their output directory - files — extracted file artifacts and their output directory - decryption — decrypted TLS/SSH streams and their output directory Both helpers are best effort by design: POSIX mode bits are largely a no-op on Windows, and failing to tighten permissions never aborts an analysis run. restrict_dir_permissions only tightens a directory it actually creates, so pointing an output flag at an existing shared directory does not silently re-permission it; mkdir errors propagate exactly as a bare mkdir would. Verified under WSL with umask 022, which would otherwise yield 0644/0755: freshly created case/carve/export/files directories come out 0700 and every report, export, log, carved blob and extracted artifact 0600; a second run into the same directories is idempotent; a pre-existing 0755 directory keeps its mode while the files written into it are still 0600. Fixes #29 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XxomH3dRdLtPFFABGp1AEW
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #29.
The problem
pcapper deliberately does not redact what it recovers (
reporting._redact_secretis a documented no-op — the right call for a credential-recovery tool). The consequence is that reports, exports, the run log, carved artifacts, extracted files and decrypted streams routinely contain cleartext credentials, LDAP binds, SNMP community strings, session tokens and malware samples.None of those write paths restricted permissions, so under a default umask they landed at
0644and output directories at0755. On a shared analysis host — a jump box, a lab VM, a multi-analyst forensics workstation — every other local account could read recovered passwords out of another analyst's case directory. On an IR engagement those files are also evidence, and world-readable evidence is harder to defend on chain-of-custody grounds.The fix
Two best-effort helpers in
utils.py, applied at every write path:utils.safe_write_text0600exporting.export_csv_hostsCSV0600exporting.export_sqlite0600, set immediately aftersqlite3.connect()cli._log_event--log-filerun log0600cli0700carving0600/0700files0600/0700decryption0600/0700Two deliberate design choices, both raised in the issue:
chmodis wrapped and swallowed.restrict_dir_permissionsonly tightens a directory it actually creates. Pointing--case-dir/--carve-out/--log-fileat a directory the analyst already has does not silently re-permission it; the files written into it are still0600.mkdirerrors propagate exactly as a baremkdirwould — only thechmodis best effort.The SQLite export is tightened right after
connect()and before any rows are inserted, so the database is never briefly readable while being populated. Directories are tightened atmkdirtime, not at end of run.One ordering bug surfaced while testing and is fixed here:
_log_eventfires at run start and creates the log file's parent, so with--log-file <case>/run.logthe case directory was being created at0755before_analyze_pathsgot to it. Thatmkdirnow goes through the same helper.Verification
Run under WSL with
umask 022, which would otherwise yield0644/0755:The
files.pyextraction path was verified separately on Windows (wheredpktis installed) against a synthetic HTTP GIF transfer —files/0700,secret.gif0600.decryption.pywas not exercised end-to-end (needstsharkplus a keylog file); its edits are the same shape as the verified paths and the module compiles and imports cleanly.python -m compileall pcapperis clean and--self-checkpasses apart from pre-existing optional-dependency warnings in the WSL env (dpkt,geoip2,pydicom, libpcap).The alternative floated in the issue — a
--permissive-outputescape hatch — is not included here; restrictive-by-default with no flag seemed like the better starting point, and it is easy to add later if a shared-team workflow needs it.🤖 Generated with Claude Code
https://claude.ai/code/session_01XxomH3dRdLtPFFABGp1AEW