Infostealer Analysis
Definition
Section titled “Definition”Infostealer analysis covers the malware family whose entire payload is data collection: browser-saved passwords, session cookies, autofill data, payment card forms, cryptocurrency wallet files, and assorted credentials (FTP clients, SSH keys, messaging session tokens). Stealers are overwhelmingly distributed as Malware-as-a-Service — families like RedLine, Lumma, Vidar, and StealC are operated by developers who sell builds and panel infrastructure to affiliates, who distribute the loader through phishing, cracked software, malvertising, and YouTube tutorial scams [verify current family naming per period]. Analysis of a stealer has two faces: binary analysis (what does it collect, how does it decrypt browser stores, where does it upload) and log analysis (the standardized data bundle each victim’s infection produces, sold or traded on criminal markets). Both faces matter: the binary tells you what telemetry to alert on, the logs tell you exactly which credentials are now burned and whose.
Why It Matters
Section titled “Why It Matters”Stealers are the volume product of the criminal economy — infections in the millions per year — and their output (credential logs) is the raw material for a vast secondary market: initial access brokers buy logs, filter for corporate VPN/SSO cookies, and sell footholds to ransomware crews. This makes every stealer infection a potential ransomware precursor, which is why IR teams treat stealer alerts on executive or admin endpoints as severity-one. For defenders, stealers are also a browser-security story: modern Chromium’s password store is DPAPI-protected per user, and cookie stores are increasingly encrypted with keys guarded by OS-level mechanisms (Chrome App-Bound Encryption on Windows [verify current state]) — the arms race between browser vendors and stealers defines what data is practically at risk. Analyzing stealers is also the cleanest entry into malware economics: build prices, log prices, and panel mechanics are publicly studied, and understanding the economics predicts attacker behavior (why they target specific browsers, why logs are structured so rigidly, why new families appear monthly). Finally, the corporate-response side — log-driven credential resets — is a concrete, learnable workflow that directly reduces breach probability, making this module’s skills immediately operational.
Detailed Explanation
Section titled “Detailed Explanation”A stealer’s execution is a fixed pipeline, executed fast (usually under two minutes end-to-end):
INFOSTEALER PIPELINE (Chromium-focused) [1] collect browser profiles: %LOCALAPPDATA%\...\User Data\* [2] key <profile>\Local State -> os_crypt.encrypted_key (base64, DPAPI-protected user key) [3] extract <profile>\Default\Login Data (SQLite: passwords) <profile>\Default\Cookies (SQLite: session cookies) <profile>\Default\Web Data (autofill/cards) wallets: %APPDATA%\Exodus, Electrum dirs, MetaMask ext [4] decrypt DPAPI via CryptUnprotectData -> AES-GCM key cookie v10/v20 values decrypt with that key [5] stage log tree: \passwords.txt \cookies\ \wallets\ \cards\ + \information.log (machine info, geofence result) [6] pack zip with per-build password [7] upload POST to panel (multipart), then optional self-deleteThe DPAPI layer is the crux of Chromium theft: the Local State file holds an encrypted_key blob that CryptUnprotectData decrypts only for the logged-in user — which is why stealers run in the user session (not as service) and why malware analysis sandboxes produce empty logs when the sandbox user differs from the infection context. Firefox keeps its own scheme (logins.json + key4.db, 3DES/AES with a null-salt key derivable offline [verify current Firefox version]) and is targeted separately. Anti-analysis is light but present: geofencing by country in information.log results (stealers refuse to run on CIS/localized sandboxes), VM checks, and mutex-based duplicate-run prevention. The log format is family-standardized — a fixed directory tree inside a password-protected archive — because the panels that consume them are family-standardized too. Log analysis therefore parses structure, not bytes: count credentials, extract domain sets, spot corporate SSO domains (okta, workday, VPN portals), flag session cookies for high-value services (still-valid sessions are more dangerous than passwords, which are often stale). The upload POST itself is a detection gift: multipart POSTs to odd domains from a user process right after browser-file access is a nearly unique behavioral fingerprint.
Common Techniques
Section titled “Common Techniques”| Technique | Target / artifact | Notes |
|---|---|---|
| DPAPI key theft | Local State → CryptUnprotectData | Requires user-session context; sandbox pitfall |
| Login Data SQLite dump | Default\Login Data → logins table | Passwords AES-GCM with DPAPI’d key |
| Cookie store theft | Default\Cookies (v10/v20 blobs) | Live sessions stolen — MFA bypass vector |
| App-Bound Encryption bypass | IElevator COM service abuse [verify] | Chromium hardening; stealers adapted 2024+ |
| Wallet file sweep | Exodus, Electrum, MetaMask extension dirs | Highest per-victim value |
| Clipboard hijack (clipbanker) | overwrite clipboard on BTC/ETH address regex | Slow-burn theft, often bundled |
| Discord/Telegram token theft | %APPDATA%\discord\Local Storage\leveldb | Session hijack for social engineering |
| FTP/SSH client configs | FileZilla recentservers.xml, PuTTY sessions | Legacy but persistent |
| Geofence refusal | CIS/sandbox locale check pre-run | Explains empty sandbox logs |
| Panel upload POST | multipart to gate domain | Network-side detection anchor |
| Log sale / broker filtering | logs[domain] marketplace listings | Corporate hits = broker inventory |
Full Working Code
Section titled “Full Working Code”log_triage.py — triages a (simulated or shared-via-IR) stealer log directory in the standard layout: counts credentials, extracts and ranks contacted domains, flags corporate/SSO domains from a suffix list, enumerates wallets and cookies by service, and emits a prioritized remediation list. Operates on log artifacts — it recovers nothing and steals nothing; it is the IR-side tool.
#!/usr/bin/env python3"""log_triage.py — stealer-log triage for IR (artifact-side tool).Usage: python log_triage.py <log_dir> [--corp corp_domains.txt]Expected standard log layout (any subset): <dir>/passwords.txt (url<TAB>user<TAB>pass per line) <dir>/cookies/*.json or *.txt (domain per line or JSON) <dir>/wallets/ <dir>/cards/ <dir>/information.logstdlib only."""import argparse, json, os, re, sysfrom collections import Counterfrom urllib.parse import urlparse
CORP_DEFAULT = ["okta.com", "workday.com", "vpn", "sso", "citrix", "outlook.", "microsoftonline", "login.microsoft", ".corp", ".local", "gitlab", "jira", "confluence"]
def read_lines(path): try: with open(path, encoding="utf-8", errors="replace") as fh: return fh.read().splitlines() except OSError: return []
def domain_of(url_or_domain): s = url_or_domain.strip() if "://" not in s: s = "https://" + s try: host = urlparse(s).netloc or s[8:] except ValueError: return None host = host.split("@")[-1].split(":")[0].lower() return host or None
def parse_passwords(path): """passwords.txt lines: url<TAB>login<TAB>pass (family variants).""" entries = [] for line in read_lines(path): if not line.strip() or line.count("\t") < 2: continue parts = line.split("\t") url, user = parts[0], parts[1] entries.append((domain_of(url), user, parts[2])) return entries
def parse_cookies(cookie_dir): """Either JSON cookie exports or one-domain-per-line dumps.""" domains = [] if not os.path.isdir(cookie_dir): return domains for name in sorted(os.listdir(cookie_dir)): p = os.path.join(cookie_dir, name) if name.endswith(".json"): try: data = json.load(open(p, encoding="utf-8", errors="replace")) items = data if isinstance(data, list) \ else data.get("cookies", []) for c in items: d = (c.get("domain") or "").lstrip(".").lower() if d: domains.append(d) except (json.JSONDecodeError, OSError): continue else: for line in read_lines(p): d = domain_of(line) if "://" in line else \ line.strip().lstrip(".").lower() if d and "." in d: domains.append(d) return domains
def flag_corp(domain, corp_rules): dl = (domain or "").lower() return any(rule in dl for rule in corp_rules)
def main(): ap = argparse.ArgumentParser() ap.add_argument("log_dir") ap.add_argument("--corp", help="extra corp/SSO substrings, comma sep") ap.add_argument("--top", type=int, default=15) args = ap.parse_args() if not os.path.isdir(args.log_dir): sys.exit(f"not a directory: {args.log_dir}") corp_rules = list(CORP_DEFAULT) if args.corp: corp_rules += [x.strip().lower() for x in args.corp.split(",") if x.strip()] print(f"== log triage: {args.log_dir} ==") # passwords pw_path = os.path.join(args.log_dir, "passwords.txt") creds = parse_passwords(pw_path) if os.path.exists(pw_path) else [] cred_domains = Counter(d for d, _, _ in creds if d) corp_pw = sorted({d for d, _, _ in creds if flag_corp(d, corp_rules)}) print(f"\n[credentials] {len(creds)} entries, " f"{len(cred_domains)} distinct domains") for d, n in cred_domains.most_common(args.top): mark = " <-- CORP" if flag_corp(d, corp_rules) else "" print(f" {n:>4} {d}{mark}") # cookies cookies = parse_cookies(os.path.join(args.log_dir, "cookies")) cookie_domains = Counter(cookies) corp_ck = sorted({d for d in cookie_domains if flag_corp(d, corp_rules)}) print(f"\n[cookies] {len(cookies)} cookies, " f"{len(cookie_domains)} distinct domains") # high-value session cookies: SSO/idp/corp domains sess = [d for d in cookie_domains if flag_corp(d, corp_rules) or re.search(r"(sso|idp|auth|login)\.", d)] for d in sorted(sess, key=lambda x: -cookie_domains[x])[:args.top]: print(f" {cookie_domains[d]:>4} {d} <-- SESSION RISK") # wallets and cards for sub in ("wallets", "cards"): p = os.path.join(args.log_dir, sub) if os.path.isdir(p): n = sum(len(fs) for _, _, fs in os.walk(p)) print(f"\n[{sub}] {n} artifacts: " f"{sorted(os.listdir(p))[:8]}") # machine info info = os.path.join(args.log_dir, "information.log") if os.path.exists(info): lines = read_lines(info) print(f"\n[information.log] {len(lines)} lines; " f"headline: {lines[0][:90] if lines else '-'}") # remediation priority print("\n== REMEDIATION PRIORITY ==") if corp_pw: print(f" 1. password resets: {', '.join(corp_pw[:10])}") if corp_ck or sess: print(f" 2. session invalidation + step-up audit: " f"{', '.join(sorted(set(corp_ck + sess))[:10])}") if not (corp_pw or sess): print(" 1. (no corporate indicators in this log)")
if __name__ == "__main__": main()Code Walkthrough
Section titled “Code Walkthrough”- Lines 19-27 handle the password file format: the de-facto standard is tab-separated
url → login → passwordper line, with family variations mostly in header lines (skipped by the tab-count filter).domain_of()(lines 20-27) normalizes bare domains, URLs with ports, anduser@hostforms into a comparable host string. - Lines 30-51 cover the cookie side in both surviving formats: JSON exports (family-dependent schema, handled with defensive
.getchains) and plain domain-per-line dumps. The.lstrip(".")matters — Chromium cookie domains are stored with a leading dot for domain-wide scope. - Lines 54-56 implement corporate flagging as substring rules rather than exact matches:
vpnandssocatch internal hostnames you’ll never fully enumerate; the--corpflag appends org-specific substrings (your actual domain, your IdP tenant). - Lines 74-91 produce the two counts that matter operationally: credential volume (how burned is this user) and session-risk cookies — SSO/IdP/auth domains, where a stolen cookie is a live MFA-bypassing session, not a stale password. These drive remediation order: sessions first, passwords second.
- Lines 93-107 enumerate wallets/cards artifacts and the machine-info log (geofence result, install path), then emit the prioritized remediation block — the tool’s actual deliverable.
How It Reaches The Target
Section titled “How It Reaches The Target”Distribution is industrialized: the loader rides cracked software installers, malvertising drops, YouTube “free tool” descriptions, and phishing — often as a stub that fetches the stealer payload at run time. On execution the pipeline above completes in under two minutes: browser stores opened, DPAPI key unwrapped, SQLite files copied, wallet directories swept, log tree assembled, ZIP packed, POSTed to the panel — then the loader frequently deletes itself. The victim notices nothing. The logs flow to the affiliate’s panel, get priced by content quality (wallet files and corporate sessions raise value dramatically), and enter the resale chain: logs are packaged and sold on criminal markets, brokers filter for corporate access, and within days a two-minute infection can become an intrusion with an operator inside the VPN. Defenders encounter stealer artifacts at three ranges: telemetry during the two-minute run (browser file access by an unknown process, the upload POST), the log itself when shared by intel partners or law enforcement takedowns, and the aftermath — credential-stuffing waves against the leaked accounts. The analysis work in this module serves the second and third range: turning a log bundle into a prioritized reset list, and turning binary analysis into the telemetry rules that catch the next infection in the first range.
Exploitation Chain
Section titled “Exploitation Chain”- Distribution — affiliate pushes Lumma loader via fake “Chrome update” malvertising (T1185 style [verify mapping])
- Execution — user runs installer in session; stealer payload fetched in-memory
- Collection — DPAPI-unwraps Chromium key; dumps Login Data, Cookies, Web Data; sweeps Exodus wallet dir
- Staging — standardized log tree assembled; zip packed with build password
- Exfiltration — multipart POST to panel gate; loader self-deletes
- Broker phase — log sold; broker filters cookies, finds employee SSO session for target org
- Foothold — broker sells access; operator replays cookie, bypasses MFA, reaches internal apps
- Impact — internal recon begins; ransomware chain seeded weeks later (M8)
Building Your Own Tool
Section titled “Building Your Own Tool”sesscheck.py — a session-cookie risk scorer for IR teams handling stealer logs: given the cookie list from a log, scores each stolen session by service sensitivity (SSO/email/VPN/dev platforms), cookie count and recency fields, and outputs a session-invalidation worksheet ordered by blast radius. Where log_triage.py answers “what did we lose”, sesscheck.py answers “what is still open right now”.
Design decisions
Section titled “Design decisions”- Score sessions, not passwords. Passwords are stale within rotation cycles; cookies are live until invalidated. The tool’s entire focus is the live-session window — the hours-to-days where replay causes real damage.
- Sensitivity tiers are data, not code. Tier lists (SSO > email > cloud console > dev > social) live in an editable table at the top so responders tailor to their estate without touching logic.
--expire-modefor the worksheet. Output is ordered for action: the responder works top-down through invalidation with no re-sorting, because during an incident every minute of tooling friction costs scope.- No network calls, ever. The tool never validates cookies against live services — that is an active, risky action outside IR tooling scope; it scores from artifact metadata only.
Full source
Section titled “Full source”#!/usr/bin/env python3"""sesscheck.py — stolen session-cookie risk scorer (IR worksheet).Usage: python sesscheck.py <cookies_dir> [--out worksheet.md]stdlib only."""import argparse, json, os, re, sysfrom datetime import datetime, timezone
# tier: (regex on domain, weight 0-10, action note)TIERS = [ (r"(sso|okta|onelogin|ping|auth0|azuread|microsoftonline)", 10, "SSO — invalidate tenant-wide, force re-auth all"), (r"(outlook\.|office365|mail\.|gmail\.|mimecast)", 9, "Email — invalidate, audit rules/forwarding"), (r"(aws|console\.|portal\.azure|gcp|cloud\.digitalocean)", 9, "Cloud console — rotate keys, audit API usage"), (r"(github|gitlab|bitbucket|jenkins|jira|confluence)", 8, "Dev platform — revoke tokens, audit pushes"), (r"(vpn|citrix|vmwarehorizon|remote\.|rdweb)", 8, "VPN — kill sessions, check geo anomalies"), (r"(facebook|instagram|linkedin|twitter|x\.com)", 5, "Social — reset, check auth apps"), (r"(bank|paypal|stripe|coinbase|binance)", 7, "Financial — contact provider, audit transactions"),]DEFAULT_WEIGHT, DEFAULT_NOTE = 3, "reset + audit"
def load_cookies(cookie_dir): """Yield dicts {domain, name, expiry} from JSON exports.""" for name in sorted(os.listdir(cookie_dir)): if not name.endswith(".json"): continue p = os.path.join(cookie_dir, name) try: data = json.load(open(p, encoding="utf-8", errors="replace")) except (json.JSONDecodeError, OSError): continue items = data if isinstance(data, list) \ else data.get("cookies", []) for c in items: yield {"domain": (c.get("domain") or "").lstrip(".").lower(), "name": c.get("name") or "?", "expiry": c.get("expires") or c.get("expirationDate") or 0}
def classify(domain): for rx, w, note in TIERS: if re.search(rx, domain): return w, note return DEFAULT_WEIGHT, DEFAULT_NOTE
def live(expiry): """Chromium epochs are seconds; >1e12 means ms. 0 = session.""" if not expiry: return True # session cookie: lives while browser does try: exp = float(expiry) if exp > 1e12: exp /= 1000.0 return exp > datetime.now(timezone.utc).timestamp() except (TypeError, ValueError): return True
def main(): ap = argparse.ArgumentParser() ap.add_argument("cookies_dir") ap.add_argument("--out") args = ap.parse_args() if not os.path.isdir(args.cookies_dir): sys.exit(f"not a directory: {args.cookies_dir}") rows = [] for c in load_cookies(args.cookies_dir): w, note = classify(c["domain"]) if not live(c["expiry"]): w = 0 rows.append((w, c["domain"], c["name"], note)) rows.sort(reverse=True) live_rows = [r for r in rows if r[0] > 0] print(f"[+] {len(rows)} cookies, {len(live_rows)} live-scored") groups = {} for w, dom, name, note in live_rows: groups.setdefault((w, dom, note), []).append(name) print(f"\n{'w':>2} {'domain':<40} {'n':>4} action") print("-" * 90) for (w, dom, note), names in sorted(groups.items(), key=lambda kv: -kv[0][0]): print(f"{w:>2} {dom:<40} {len(names):>4} {note}") if args.out: with open(args.out, "w") as fh: fh.write("# Session invalidation worksheet\n\n") fh.write("Work top-down. Each line = live stolen " "sessions for a domain.\n\n") for (w, dom, note), names in sorted( groups.items(), key=lambda kv: -kv[0][0]): fh.write(f"## [{w}] {dom} ({len(names)} cookies)\n") fh.write(f"- action: {note}\n") fh.write(f"- sample: {', '.join(names[:10])}\n\n") print(f"[+] wrote {args.out}")
if __name__ == "__main__": main()Walkthrough
Section titled “Walkthrough”TIERS(lines 13-29) is the sensitivity model as data: SSO providers at weight 10 (a live Okta session is the whole company), email at 9 (email is the recovery path for everything else), cloud consoles and dev platforms at 8-9, social lower. Editing tiers requires no logic changes — responders align the table with their estate in minutes.live()(lines 48-62) decodes Chromium’s epoch-seconds (with the ms-variant guard) and treats session cookies (no expiry) as live. An expired cookie scores weight 0 and drops off the worksheet — expired session noise is the main volume complaint in raw logs.- The grouping loop (lines 84-89) collapses per-cookie noise into per-domain rows with counts — the responder invalidates a domain’s sessions, not individual cookies.
- The worksheet output (lines 91-101) preserves the priority order and adds sample cookie names for verification — the artifact a responder actually works from during the incident.
Compilation and test procedure
Section titled “Compilation and test procedure”build a synthetic cookie dir in Chromium JSON export shape
Section titled “build a synthetic cookie dir in Chromium JSON export shape”$ python3 -c ” import json, os, time os.makedirs(‘/tmp/log/cookies’, exist_ok=True) now = time.time() cookies = [ {‘domain’:’.sso.corp.example’,‘name’:‘LT’,‘expires’:now+86400}, {‘domain’:’.outlook.office365.com’,‘name’:‘SPA-CID’,‘expires’:now+3600}, {‘domain’:’.github.com’,‘name’:‘logged_in’,‘expires’:now+7200}, {‘domain’:’.news.example’,‘name’:‘track’,‘expires’:now-999}, ] json.dump(cookies, open(‘/tmp/log/cookies/c.json’,‘w’)) ” $ python3 sesscheck.py /tmp/log/cookies —out /tmp/worksheet.md [+] 4 cookies, 3 live-scored 10 .sso.corp.example 1 SSO — invalidate tenant-wide… 9 .outlook.office365.com 1 Email — invalidate… 8 .github.com 1 Dev platform — revoke tokens…
expired entries must be excluded from the worksheet
Section titled “expired entries must be excluded from the worksheet”$ grep -c “news.example” /tmp/worksheet.md || echo “correctly absent”
Detection considerations
Section titled “Detection considerations”Detection splits binary-side and effect-side. Binary-side anchors: a user-session process reading Login Data/Cookies SQLite files then writing a ZIP within minutes (Sysmon EID 11 sequence); CryptUnprotectData calls from non-browser processes (Sysmon EID 10 or ETW [verify availability]); the multipart POST to a fresh domain (EID 3 + proxy). Effect-side anchors: the impossible-travel and impossible-device signals your IdP raises when stolen cookies replay — which is why IdP logs belong in the same hunt as stealer telemetry. Your own tooling leaves no endpoint trace (file reads only, no network). For the estate, the highest-value fix is architectural: phishing-resistant MFA (FIDO2) collapses the value of stolen passwords and makes stolen sessions the only replay path, which shrinks the problem to something IdP anomaly detection can carry. Wazuh mapping of browser-access sequences depends on your Sysmon ruleset [verify]; Suricata sees the upload POST only over plaintext — expect to rely on proxy/EDR correlation for TLS panels, with local sid:1000001+ rules for known gate domains as a stopgap.
Improvements to add
Section titled “Improvements to add”- Parse
information.logmachine fields and join them into the worksheet (hostname → asset owner mapping in IR). - Add Discord/Telegram token detection to the artifact sweep and treat tokens as session-equivalents.
- Import password entries to cross-flag domains that have BOTH live sessions and saved passwords (double exposure).
- Emit a one-page exec summary (counts + worst tier) for incident comms.
- Batch mode: score a directory of many logs (campaign scale) and rank users by exposure, feeding the M15 campaign view.
Detection — Defender View
Section titled “Detection — Defender View”| Signal | Source | ID | Notes |
|---|---|---|---|
| Browser credential store access by non-browser | Sysmon | EID 11 sequence + EID 1 parent chain | Highest-fidelity pre-exfil anchor |
CryptUnprotectData from unknown process | Sysmon / ETW | EID 10 [verify coverage] | Needs module/stack enrichment |
| ZIP staged in %TEMP% after browser access | Sysmon | EID 11 | Pack step of pipeline |
| Multipart POST to fresh domain | Proxy / Sysmon | EID 3 + proxy logs | Panel upload fingerprint |
| Stealer loader process lineage | Sysmon | EID 1 (installer → reg → powershell) | Distribution-tied |
| Cookie replay: impossible device/geo | IdP logs | vendor-specific (Entra/Okta) | Effect-side, post-theft |
| Defender real-time events on loader | Defender | 1116/1117 | Where enabled, catches known builds |
| Known gate domain hits | Suricata | custom/local sid:1000001+ | Rotation-resistant only via feed |
| Wazuh correlation of the above | HIDS/SIEM | [verify rule IDs per deployment] | Join EID 11 + EID 3 per host |
Evasion Techniques
Section titled “Evasion Techniques”Stealer evasion targets the DPAPI/browser layer and the telemetry layer in turn. In-memory payload staging keeps the stealer off disk between fetch and run, denying file-based detections; counters anchor on behavior (file access, API calls), not hash. App-Bound Encryption bypasses abuse the IElevator COM service to request decryption as the browser would [verify mechanism details per Chromium version] — the arms race here is live: each Chromium hardening cycle is followed by new bypasses, so detection cannot depend on the bypass failing, only on the access pattern (which is stable). Geofencing — refusing to run on CIS-locale or sandbox systems — defeats naive detonation; labs answer with locale-matched sandboxes and manual detonation contexts. Panel rotation — gate domains rotate faster than feeds — makes network IOC blocking a treadmill; the durable detections are process-context and access-sequence based. Self-deletion after upload erases the binary but not the Sysmon record of what it did. The meta-lesson mirrors M5: stealers cannot stop performing their function (reading specific well-known paths in the user profile), so the function itself is the detection.
⚠️ What caught me: I once cleared a stealer alert as low-severity because the infected user “had no admin rights” — then the broker-sold cookie session turned up in a ransomware IR two months later, and the entry point was that exact endpoint. A stealer’s severity is not what it executes; it’s what it carries away. I now rate every credential-theft alert by what the stolen data opens, starting with the user’s IdP session, and I treat every stealer hit on a human with an SSO seat as an incident, not an infection.
Lab Practice
Section titled “Lab Practice”- Build a synthetic stealer log (the standard tree from this module’s diagram) and run both tools against it:
log_triage.pyfor inventory andsesscheck.pyfor the invalidation worksheet; verify the corporate flagging with a fake SSO domain. - On a lab VM, inspect a Chromium profile’s
Local Stateand confirm theencrypted_keystructure; then (on your own test profile only) walk the DPAPI unwrap with Python’swin32cryptto see exactly what the stealers automate [verify module name/API]. - Capture a benign browser session’s cookie expiry formats and confirm
sesscheck.py’s epoch handling (seconds vs milliseconds vs session cookies) on real data before trusting it on incident data. - Write the Sysmon sequence rule (EID 11 on
Login Datawithin 5 min of EID 1 process creation from an installer lineage) in your lab pipeline and validate it against a benign browser update — tune until the browser’s own operations don’t fire it. - Replay exercise: hand the worksheet from step 1 to a colleague as a 30-minute timed drill and measure time-to-invalidations-complete; that number is your incident muscle memory.
Interview Questions
Section titled “Interview Questions”- Technical: Explain the Chromium credential protection chain —
Local State, DPAPI, AES-GCM — and precisely what a stealer must do to decrypt a password row. - Technical: Why do stealers refuse to run in analyst sandboxes, and what are the two most common refusal mechanisms?
- Scenario: A log from a partner contains your CFO’s SSO session cookie. Walk through the first hour of response — order of actions and why.
- Conceptual: Why are stolen session cookies operationally worse than stolen passwords, and how does phishing-resistant MFA change that equation?
- Conceptual: How does the stealer → broker → ransomware chain change the severity model for a “simple” stealer alert?
- Deep-dive: Design detection for stealer upload traffic when panels rotate domains weekly and use TLS. Which signals survive rotation?
- Practical: You must validate 400 stealer logs from a takedown share for corporate exposure by end of day. What is your pipeline, and what does the output look like?
Related Modules
Section titled “Related Modules”M5 (String and Artifact Extraction) · M8 (Ransomware Analysis) · M15 (Threat Intelligence and Campaign Clustering) · E11 (Defender Detection Logic) · BT7 (Threat Hunting)