Skip to content

Latest commit

 

History

9 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 

Repository files navigation

static_doc_analyzer

A static document triage tool: it inspects a received file (PDF, Word, Excel, PowerPoint, RTF, archives…) and tells you whether it's reasonable to open it — without ever opening it or executing anything.

Built for machines without antivirus, typically on Linux, where the dubious attachment still shows up anyway.

python3 static_doc_analyzer.py <path_to_document>

Installation

None. The script only uses the Python standard library and calls no external program.

  • Python 3.8 or newer (validated on 3.12)
  • No pip install, no third-party binary, no network access
  • A single file, copyable as-is onto any machine

Usage

# Analyze a document, human-readable report
python3 static_doc_analyzer.py invoice.pdf

# Several files at once, one line per file
python3 static_doc_analyzer.py --quiet ~/Downloads/*

# Machine-readable output, for a script or a SOAR
python3 static_doc_analyzer.py --json document.docx

# Cap analysis time on a large file
python3 static_doc_analyzer.py --timeout 20 large_report.pdf
Option Effect
--json Structured JSON output (single object, or list if multiple files)
--quiet One line per file: verdict, score, name
--timeout N Time budget per file, in seconds (default: 90)
--no-color Disable color (automatic if output isn't a terminal)

Reading the report

The report has five sections:

  1. File identity — size, entropy, actual type inferred from the bytes (not the extension), SHA-256 and MD5 for your pivots and threat-intel lookups.
  2. Verdict and score — see below.
  3. Indicators — everything found, grouped by category, each with: its weight, its precise location in the file (obj 12, word/vbaProject.bin#module1…), the detection context and the excerpt that triggered the rule. Nothing is masked or truncated.
  4. Aggravating correlations — combinations of indicators that, together, characterize an attack chain.
  5. Extracted IOCs — URLs, IP addresses and UNC paths found in the document, defanged (hxxp[://]…) so they can be safely copied into a ticket or a search engine.

Verdicts and thresholds

Score Verdict Return code What it means
≥ 85 MALICIOUS 3 Do not open. Isolate the file and treat as an incident.
45 – 84 SUSPICIOUS 2 Do not open outside a sandbox. Detonation or dynamic analysis required.
18 – 44 REVIEW NEEDED 1 Unusual element. Open only in a controlled environment.
< 18 CLEAN 0 No active code, macro, embedded object or remote link. The document can be opened.
ERROR 4 Empty, unreadable, too large, or analysis impossible.

The return codes let you chain the tool into a script:

if python3 static_doc_analyzer.py --quiet "$f"; then
    xdg-open "$f"
else
    echo "Do not open: $f"
fi

An incomplete analysis is never CLEAN. If a limit is hit (time budget exhausted, truncated archive, file over 64 MB), the verdict is raised to at least "Review needed" and the report states this explicitly.


What the score is based on

The score is a risk accumulator, not a probability. It has no absolute meaning: it's calibrated so the thresholds above land in the right place.

1. Every indicator has a weight

Example indicator Weight
Remote template attachedTemplate (remote template injection) 70
Embedded PE/ELF executable, ms-msdt: handler, Equation Editor object 46 – 50
/Launch action in a PDF, VSS shadow copy deletion, AMSI bypass 45 – 46
Excel 4.0 macro (XLM), regsvr32 squiblydoo 38 – 40
.DownloadString(, powershell -enc, certutil -urlcache 28 – 34
Automatic entry point AutoOpen / Document_Open 26
Presence of a VBA project 18
Unreadable compressed stream, encrypted PDF 6 – 8
External hyperlink 0 (informational)

2. The weight is scaled by discovery context

This is the core mechanism of the tool. The same string doesn't carry the same weight depending on where it's found:

Context Factor
Decompressed VBA macro, PDF JavaScript, decoded stream, decrypted base64 × 1.0
Strings extracted from a binary × 0.6
XML attributes, container structure × 0.45
Visible document text × 0.25

3. Repetitions have diminishing returns

+20% per distinct occurrence, capped at 1.6 times the base weight. Ten WScript.Shell occurrences don't carry ten times the risk of a single one.

4. An indicator only counts once

Its maximum weight across all locations is kept, then distinct indicators are summed.

5. Visible text is capped

6 points maximum per rule, 15 points maximum in total, and it never triggers correlations. Direct consequence: an incident report, an internal procedure or training material that quotes powershell -enc, certutil -urlcache and WScript.Shell stays CLEAN. These are the documents you receive most often; they shouldn't trigger an alert.

6. Correlation bonus

+14 to +28 points when several behaviors combine: automatic execution + download, macro + embedded executable, PDF JavaScript + known exploitation primitive… It's the combination that makes a maldoc, not the isolated element.

7. Total capped at 200

Calibration in one sentence: a single decisive indicator should be enough to condemn (remote template at 70, plus the download/execution correlation at +16, gives 86 → MALICIOUS), while a single mild indicator never goes past "Review needed" (macro present = 18).


What gets analyzed

The tool identifies the format from the header bytes, never from the extension. A .pdf that is actually an executable gets flagged as such.

PDF — full decoding of filter chains (FlateDecode, ASCIIHexDecode, ASCII85Decode, LZWDecode, RunLengthDecode, PNG predictors), unfolding of compressed object streams /ObjStm (where modern PDF JavaScript hides), detection of hex-obfuscated names (/J#61vaScript), /OpenAction, /AA, /Launch, /JavaScript, /EmbeddedFile, /XFA, /RichMedia triggers, and JavaScript APIs associated with known CVEs.

OOXML (docx, xlsx, pptx, docm, xlsm…)actual decompression of VBA macros in MS-OVBA format, which a simple string extraction can't do; analysis of external relationships, distinguishing a remote attachedTemplate or oleObject (attack) from a plain hyperlink (benign); DDE/DDEAUTO fields; Excel 4.0 (XLM) macros and _xlnm.Auto_Open; mismatch between extension and the presence of a macro; reassembly of <w:t> runs to reconstruct payloads deliberately fragmented across them.

OLE / legacy formats (doc, xls, ppt) — VBA modules, Equation Editor object (CVE-2017-11882 vector), Ole10Native packages embedding an executable.

RTF\objdata, \objupdate, structure obfuscation, hex decoding of several successive embedded objects.

Secondary formats frequently abused in phishing — Windows shortcuts (.lnk, target and command line treated as executed code, not as a plain binary); OneNote (.one, a common vector for hiding a .hta/.vbs behind a clickable image); Excel web queries (.iqy) and SYLK files (.slk), two formats that bypass Office's macro warning.

Scripts received directly — a .vbs, .ps1, .js, .sh, .bat… sent as-is (outside a document/archive) is analyzed as executed code, not as text: its visible content IS the payload, with no intermediate rendering layer to discount.

Archives — dangerous extensions (Windows and Linux/macOS: .exe… as much as .sh, .run, .desktop, .AppImage), misleading double extensions, Unicode RTLO character masking the real extension, zip slip, decompression bombs, nested documents and scripts (analyzed recursively up to 3 levels deep).

Cross-cutting — embedded PE/ELF executables, decoded base64 blobs re-analyzed, IOC extraction (URLs, IPs, UNC/SMB paths for NTLM credential leakage).


Security of the tool itself

  • Analyzed content is never executed, nor extracted to disk.
  • All data originating from the file is neutralized before display: a document can't inject ANSI escape sequences into your terminal to falsify the report.
  • Anti-denial-of-service protections: global time budget, size caps, decompression caps, entry-count caps and recursion-depth caps.
  • No network access, so no information leakage about the analyzed document.

Limitations to keep in mind

  • This is static analysis. A document whose malicious behavior relies entirely on a remote resource fetched at open time can slip through.
  • A CLEAN verdict is not a guarantee of safety, it's the absence of a known indicator. The usual caution about the sender and context still applies.
  • Encrypted or password-protected documents are partially opaque: this is flagged in the report.
  • A legitimate document containing a macro will come out as "Review needed". This is intentional: a macro always warrants a look.

Customization

Everything is configured at the top of the file, without touching the logic:

Element Role
STRUCT Weights of structural indicators
R(...) calls Pattern rules: identifier, category, weight, expression, tags
COMBOS Correlations and their bonuses
CTX_FACTOR Per-context weighting factors
TEXT_RULE_CAP / TEXT_TOTAL_CAP Caps applied to visible text
VERDICTS Thresholds and messages
BENIGN_URI_RE Domains to ignore (XML schemas, namespaces)

Two scripts ship alongside the tool to validate your changes:

python3 make_corpus.py      # generates a test corpus in ./corpus
python3 test_docscan.py     # replays the full validation suite

The suite covers edge cases, display safety, return codes, anti-DoS protections, fuzzing on mutated files and verdict non-regression. Run it after every new signature you add: it's what will keep you from reintroducing a false positive.

About

This script analyzes your docx, xlsx and pdf files for threats without ever executing them!

Topics

Resources

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages