reverse-skills-max: initial public release (restored, redacted, hardened)
This commit is contained in:
@@ -0,0 +1,9 @@
|
||||
Apache License 2.0 — see the repository root LICENSE file for the full text.
|
||||
|
||||
Copyright the AnalystAIPack contributors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
@@ -0,0 +1,91 @@
|
||||
---
|
||||
name: analyzing-authenticode-signatures
|
||||
description: 'Analyzes Windows Authenticode signatures on PE files: checking for a signature,
|
||||
reading the signer certificate chain, detecting revoked/expired/stolen certs, and recognizing
|
||||
signature-stripping and catalog-signing abuse. Activates for requests to analyze a code
|
||||
signature, verify Authenticode, or assess signer trust on a PE.'
|
||||
domain: cybersecurity
|
||||
subdomain: malware-analysis
|
||||
tags:
|
||||
- malware-analysis
|
||||
- authenticode
|
||||
- code-signing
|
||||
- pe
|
||||
- certificates
|
||||
version: 1.0.0
|
||||
author: analyst-ai-pack
|
||||
license: Apache-2.0
|
||||
mitre_attack:
|
||||
- T1553.002
|
||||
- T1036.001
|
||||
d3fend:
|
||||
- D3-FCR
|
||||
references:
|
||||
- 'Windows Authenticode PE signature format — https://learn.microsoft.com/windows-hardware/drivers/install/authenticode'
|
||||
- 'Microsoft PE/COFF specification (security directory) — https://learn.microsoft.com/windows/win32/debug/pe-format'
|
||||
---
|
||||
|
||||
# Analyzing Authenticode Signatures
|
||||
|
||||
## When to Use
|
||||
|
||||
- You need to know whether a PE is signed and whether the signer is trustworthy.
|
||||
- You suspect a stolen/abused code-signing certificate or a forged/invalid signature.
|
||||
- You are distinguishing legitimately signed software from signed malware.
|
||||
|
||||
**Do not use** "is signed" as "is safe" — adversaries use stolen certs, abuse catalog signing,
|
||||
and append invalid signatures; a valid signature only attests the signer, not benignity.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Access to the PE security directory (Python `struct`/`pefile`); on Windows, `signtool`/PowerShell
|
||||
`Get-AuthenticodeSignature` for authoritative verification.
|
||||
|
||||
## Safety & Handling
|
||||
|
||||
- Parse the signature statically; never execute the sample.
|
||||
- Keep the sample password-protected at rest and reference it by hash.
|
||||
|
||||
## Workflow
|
||||
|
||||
### Step 1: Check for a signature
|
||||
|
||||
Read the PE security directory (certificate table). Absent entry = unsigned; present = embedded
|
||||
PKCS#7. Note that valid signing can also be via a catalog file (not embedded).
|
||||
|
||||
```bash
|
||||
python scripts/analyst.py check sample.exe
|
||||
```
|
||||
|
||||
### Step 2: Extract signer details
|
||||
|
||||
Parse the PKCS#7 to read the signer certificate: subject, issuer, validity dates, and serial.
|
||||
Cross-reference against known-abused/stolen certs.
|
||||
|
||||
### Step 3: Verify, do not just read
|
||||
|
||||
On Windows, verify the signature cryptographically and check revocation
|
||||
(`Get-AuthenticodeSignature`). A present-but-invalid signature is a strong red flag.
|
||||
|
||||
### Step 4: Assess trust in context
|
||||
|
||||
Weigh signer reputation, certificate age, and whether the signed file's behavior matches the
|
||||
purported publisher. Treat anomalies (recently issued cert, mismatched publisher) as suspicious.
|
||||
|
||||
## Validation
|
||||
|
||||
- Signature presence is determined from the security directory, and verified (not just read) on
|
||||
Windows.
|
||||
- Signer certificate details are extracted and checked against abuse intel.
|
||||
- The trust conclusion accounts for revocation/validity, not mere presence.
|
||||
|
||||
## Pitfalls
|
||||
|
||||
- Equating a present signature with safety; stolen-cert malware is common.
|
||||
- Missing catalog-signed files by only checking embedded signatures.
|
||||
- Reading the signature without cryptographically verifying it.
|
||||
|
||||
## References
|
||||
|
||||
- See [`references/api-reference.md`](references/api-reference.md) for the signature checker.
|
||||
- Authenticode format and the PE/COFF security directory (linked in frontmatter).
|
||||
@@ -0,0 +1,39 @@
|
||||
# API Reference: Analyzing Authenticode Signatures
|
||||
|
||||
## Overview
|
||||
|
||||
`scripts/analyst.py` parses the PE security data directory (index 4) to detect an embedded PKCS#7
|
||||
Authenticode signature (WIN_CERTIFICATE), reporting presence, size, revision, and type. It is a
|
||||
dependency-free presence check; cryptographic verification requires OS tooling. It parses bytes
|
||||
statically and never executes the sample.
|
||||
|
||||
## Dependencies
|
||||
|
||||
| Package | Version | Purpose |
|
||||
|---------|---------|---------|
|
||||
| (stdlib) | — | struct parsing, file IO |
|
||||
|
||||
## External tools
|
||||
|
||||
| Tool | Purpose |
|
||||
|------|---------|
|
||||
| signtool / Get-AuthenticodeSignature | Cryptographically verify the signature and revocation |
|
||||
|
||||
## Core functions
|
||||
|
||||
### `check(path)`
|
||||
Returns whether an embedded signature exists and the WIN_CERTIFICATE header fields.
|
||||
|
||||
## Usage
|
||||
|
||||
```bash
|
||||
python scripts/analyst.py check sample.exe
|
||||
```
|
||||
|
||||
## Sources
|
||||
|
||||
- Windows Authenticode PE signature format —
|
||||
https://learn.microsoft.com/windows-hardware/drivers/install/authenticode
|
||||
- PE/COFF security data directory (index 4, file-offset semantics) —
|
||||
https://learn.microsoft.com/windows/win32/debug/pe-format
|
||||
- Stolen/abused signing certificates (T1553.002), MITRE ATT&CK
|
||||
@@ -0,0 +1,62 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Check a PE for an embedded Authenticode signature and summarize the certificate blob.
|
||||
|
||||
Parses the PE optional-header security data directory (index 4) to detect an embedded PKCS#7
|
||||
signature (WIN_CERTIFICATE), reporting its presence, size, and revision/type. Deep certificate
|
||||
parsing and cryptographic verification require a PKCS#7 library / OS tooling; this gives a fast,
|
||||
dependency-free presence check. It parses bytes statically and never executes the sample.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import struct
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
WIN_CERT_TYPE = {0x0001: "X509", 0x0002: "PKCS_SIGNED_DATA", 0x0003: "RESERVED",
|
||||
0x0004: "TS_STACK_SIGNED"}
|
||||
|
||||
|
||||
def check(path: str) -> dict:
|
||||
data = Path(path).read_bytes()
|
||||
if data[:2] != b"MZ":
|
||||
return {"error": "not a PE"}
|
||||
e_lfanew = struct.unpack_from("<I", data, 0x3C)[0]
|
||||
if data[e_lfanew:e_lfanew + 4] != b"PE\x00\x00":
|
||||
return {"error": "no PE signature"}
|
||||
opt = e_lfanew + 24
|
||||
magic = struct.unpack_from("<H", data, opt)[0]
|
||||
pe32_plus = magic == 0x20B
|
||||
dd_offset = opt + (112 if pe32_plus else 96)
|
||||
sec_dir = dd_offset + 4 * 8 # security directory is index 4
|
||||
rva, size = struct.unpack_from("<II", data, sec_dir)
|
||||
if not rva or not size:
|
||||
return {"signed_embedded": False,
|
||||
"note": "no embedded signature (may still be catalog-signed)"}
|
||||
# For the security directory, 'rva' is a file offset (not an RVA)
|
||||
dwlen, wrev, wtype = struct.unpack_from("<IHH", data, rva)
|
||||
return {
|
||||
"signed_embedded": True,
|
||||
"certificate_table_offset": rva,
|
||||
"certificate_table_size": size,
|
||||
"win_cert_length": dwlen,
|
||||
"revision": hex(wrev),
|
||||
"type": WIN_CERT_TYPE.get(wtype, hex(wtype)),
|
||||
"note": "presence only; verify cryptographically with signtool / "
|
||||
"Get-AuthenticodeSignature",
|
||||
}
|
||||
|
||||
|
||||
def main(argv: list[str] | None = None) -> int:
|
||||
parser = argparse.ArgumentParser(description="Check Authenticode signature presence")
|
||||
sub = parser.add_subparsers(dest="cmd", required=True)
|
||||
p = sub.add_parser("check")
|
||||
p.add_argument("file")
|
||||
args = parser.parse_args(argv)
|
||||
print(json.dumps(check(args.file), indent=2))
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
Reference in New Issue
Block a user