Blog

Blog

Welcome to the Blog.

Terminal Kits for Incident Triage

Small, scriptable primitives under time pressure

2026-02-22

During an incident, tool quality is less about features and more about reliability under pressure. A terminal kit that is small, predictable, and scriptable often beats a heavyweight platform with perfect screenshots but slow interaction. Triage is fundamentally a time-budgeted decision process: gather evidence, reduce uncertainty, choose containment, repeat. Your toolkit should optimize that loop.

Most failed triage sessions share a pattern: analysts spend early minutes assembling ad-hoc commands, searching historical snippets, and normalizing inconsistent logs. By the time they get coherent output, the window for clean containment may be gone. A prepared terminal kit solves this by standardizing primitives before incidents happen.

A strong baseline kit usually has four layers. First, acquisition tools to collect logs, process snapshots, network state, and artifact hashes without mutating evidence more than necessary. Second, normalization tools that convert varied formats into comparable records. Third, query tools for rapid filtering and aggregation. Fourth, packaging tools to export findings with reproducible command history.

The “reproducible command history” part is often neglected. If commands are not captured with context, handoff quality collapses. Teams should treat command logs as first-class incident artifacts: timestamped, host-tagged, and linked to case identifiers. This both improves collaboration and reduces postmortem reconstruction effort.

Command wrappers help enforce consistency. Instead of everyone typing bespoke variants of grep, awk, and jq pipelines, define stable entry scripts with sane defaults: UTC timestamps, strict error handling, deterministic output columns, and explicit field separators. Analysts can still drop to raw commands, but wrappers eliminate repetitive setup mistakes. ... continue

Recon Pipeline with Unix Tools

Composable stages instead of one monolithic scanner

2026-02-22

Recon tooling has exploded, but many workflows are still stronger when built from composable Unix primitives instead of a single monolithic scanner. The reason is control: you can tune each step, inspect intermediate data, and adapt quickly when targets or scope constraints change.

A practical recon pipeline is not about running every tool. It is about building trustworthy data flow:

If one stage is noisy, downstream conclusions become fiction.

My default stack stays intentionally boring:

Boring tools are good because they are scriptable and predictable. ... continue

Giant Log Lenses

Layout stress test for wide CLI output and log lines

2026-02-22

When dashboards hide detail, I still go back to raw logs and text-first tools.
This short note is intentionally built as a rendering stress test: some code lines are much wider than the article window to verify horizontal scrolling behavior. The examples are realistic enough to copy, but the primary goal is visual QA for long literals, long command chains, and dense tabular output.

1
rg --no-heading --line-number --color=never "timeout|connection reset|tls handshake|upstream prematurely closed" ./logs/production/edge/*.log | jq -R 'split(":") | {file:.[0], line:(.[1]|tonumber), message:(.[2:]|join(":"))}' | awk 'BEGIN{FS="|"} {printf "%-42s | L%-6s | %s\n",$1,$2,$3}' | sort -k1,1 -k2,2n

2-liner (wide structured print)

1
2
rows=[{"ts":"2026-02-22T04:31:55Z","service":"api-gateway-eu-central-1-prod-blue","endpoint":"/v1/orders/checkout/recalculate-shipping-and-tax","latency_ms":912,"trace":"9f58b69b2d7d4a21a3f17d5e4f7a0112"}]
print("\n".join(f"{r['ts']} | {r['service']:<36} | {r['latency_ms']:>4}ms | {r['endpoint']} | trace={r['trace']}" for r in rows))

4-liner (wide payload path)

1
2
3
4
const payload = {tenant:"northwind-enterprise-platform",env:"production-eu-central-1",featureFlags:["long-session-replay-streaming","websocket-fallback-polling","incremental-checkpoint-serializer-v2"],meta:{requestId:"4b1d3be8fd7e4ad6a9f8c71e2bbf9a44",userAgent:"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 Chrome/124.0.0.0 Safari/537.36"}};
const digest = btoa(JSON.stringify(payload)).replace(/\+/g,"-").replace(/\//g,"_").replace(/=+$/,"");
const url = `https://collector.example.internal/v2/telemetry/ingest/really/long/path/that/keeps/going?tenant=${payload.tenant}&env=${payload.env}&digest=${digest}`;
fetch(url,{method:"POST",headers:{"content-type":"application/json","x-trace-id":"4b1d3be8fd7e4ad6a9f8c71e2bbf9a44"},body:JSON.stringify(payload)});

Wide table sample

Service Endpoint Example Artifact Notes
api-gateway-eu-central-1-prod-blue /v1/orders/checkout/recalculate-shipping-and-tax trace=9f58b69b2d7d4a21a3f17d5e4f7a0112;span=7e5b57e0f9c04a9d;attempt=03;zone=eu-central-1b Extra-wide row to force horizontal overflow
realtime-session-broker /ws/connect/tenant/northwind-enterprise-platform/client/web-desktop-legacy-fallback wss://rt.example.internal/ws/connect/tenant/northwind-enterprise-platform/client/web-desktop-legacy-fallback?resumeToken=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9... Long URL + token-like payload

If this article behaves correctly, code blocks and tables stay on one logical line and can be scrolled horizontally without breaking the text grid style.

Related reading:

Building Repeatable Triage Kits

Packaging workflow so every analyst starts the same way

2026-02-22

Security triage often fails for a boring reason: every analyst starts from a different local setup. Different aliases, different tool versions, different output assumptions, different artifact paths. The result is inconsistent decisions and hard-to-compare findings.

A repeatable triage kit solves this by packaging workflow, not just binaries.

Think of a triage kit as a portable operating system for first-pass analysis. It should answer, consistently:

Without those answers, triage quality depends on individual heroics.

The kit design should be opinionated and minimal. Start with four modules: ... continue

Threat Modeling in the Small

One feature, one flow, one safer change this week

2026-02-22

When people hear “threat modeling,” they often imagine a conference room, a wall of sticky notes, and an enterprise architecture diagram no single human fully understands. That can be useful, but it can also become theater. Most practical security wins come from smaller, tighter loops: one feature, one API path, one cron job, one queue consumer, one admin screen.

I call this “threat modeling in the small.” The goal is not to produce a perfect model. The goal is to make one change safer this week without slowing delivery into paralysis.

Start with a concrete unit. “User authentication” is too broad. “Password reset token creation and validation” is the right scale. Draw a tiny flow in plain text. List the trust boundaries. Ask where attacker-controlled data enters. Ask where privileged actions happen. Ask where logging exists and where it does not.

At this size, engineers actually participate. They can reason from code they touched yesterday. They can connect risks to implementation choices. They can estimate effort honestly. Security stops being abstract policy and becomes software design.

My default prompt set is short: ... continue

Security Findings as Design Feedback

Reading vulnerabilities as architecture signals

2026-02-22

Security reports are often treated as defect inventories: patch issue, close ticket, move on. That workflow is necessary, but it is incomplete. Many findings are not isolated mistakes; they are design feedback about how a system creates, hides, or amplifies risk. Teams that only chase individual fixes improve slowly. Teams that read findings as architecture signals improve compoundingly.

A useful reframing is to ask, for each vulnerability: what design decision made this class of bug easy to introduce and hard to detect? The answer is frequently broader than the code diff. Weak trust boundaries, inconsistent authorization checks, ambiguous ownership of validation, and hidden data flows are structural causes. Fixing one endpoint without changing those structures guarantees recurrence.

Take broken access control patterns. A typical report may show one API endpoint missing a tenant check. The immediate patch adds the check. The design feedback, however, is that authorization is optional at call sites. The durable response is to move authorization into mandatory middleware or typed service contracts so bypassing it becomes difficult by construction. Good security design reduces optionality.

Input-validation findings show similar dynamics. If every handler parses raw request bodies independently, validation drift is inevitable. One team sanitizes aggressively, another copies old logic, a third misses edge cases under deadline pressure. The root issue is distributed policy. Consolidated schemas, shared parsers, and fail-closed defaults turn ad-hoc validation into predictable infrastructure.

Injection flaws often reveal boundary confusion rather than purely “bad escaping.” When query construction crosses multiple abstraction layers with mixed assumptions, responsibility blurs and dangerous concatenation appears. The design-level fix is not a lint rule alone. It is to constrain query creation to safe primitives and enforce typed interfaces that make unsafe composition visibly abnormal. ... continue

Incident Response with a Notebook

A timestamped timeline when dashboards lag

2026-02-22

Modern incident response tooling is powerful, but under pressure, people still fail in very analog ways: they lose sequence, they forget assumptions, they repeat commands without recording output, and they argue from memory instead of evidence. A simple notebook, used with discipline, prevents all four.

This is not anti-automation advice. It is operator reliability advice. When systems are failing fast and dashboards are lagging, your most valuable artifact is a timeline you can trust.

I keep a strict notebook format for incidents:

That structure sounds verbose until minute twenty, when context fragmentation starts. By minute forty, it is the difference between controlled recovery and expensive chaos.

The “expected result” field is especially important. Teams often run commands reactively, then treat any output as signal. That is backwards. State your hypothesis first, then test it. If expected and actual differ, you learn something real. If you skip expectation, every log line becomes confirmation bias. ... continue

ROP Under Pressure

Payloads that survive leaks, mitigations, and messy binaries

2026-02-22

Return-oriented programming feels elegant in writeups and messy in real targets. In controlled examples, gadgets line up, stack state is stable, and side effects are manageable. In live binaries, you are usually balancing fragile constraints: limited write primitives, partial leaks, constrained input channels, and mitigation combinations that punish assumptions.

Working “under pressure” means building payloads that survive imperfect conditions, not just proving theoretical code execution.

My practical approach starts by classifying constraints before touching gadgets:

Without this map, gadget hunting becomes random motion.

A reliable chain should minimize dependencies. Fancy multi-stage chains look impressive but fail more often when target timing or memory layout shifts. Prefer short chains with explicit stack hygiene and clear post-condition checks. ... continue

Fuzzing to Exploitability

Triage, minimization, and harness quality between crash and finding

2026-02-22

Fuzzing finds crashes quickly. Turning crashes into reliable security findings is slower, less glamorous work. Many teams stall in the gap between “it crashed” and “this is exploitable under defined conditions.” Bridging that gap requires discipline in triage, reduction, root-cause analysis, and harness quality. Without this discipline, fuzzing campaigns generate noise instead of security value.

The first mistake is overvaluing raw crash counts. Hundreds of unique stack traces can still map to a handful of root causes. Counting crashes as progress creates perverse incentives: bigger corpus churn, less deduplication, shallow analysis. Useful metrics are different: number of distinct root causes, percentage with minimized reproducers, time to fix confirmation, and recurrence rate after patches.

Crash triage begins with deterministic reproduction. If you cannot replay reliably, you cannot reason reliably. Save exact binaries, runtime flags, environment variables, and input artifacts. Capture hashes of test executables. Tiny environmental drift can turn a real vulnerability into a ghost. Reproducibility is not bureaucracy; it is scientific control.

Input minimization is the next force multiplier. Large fuzz artifacts obscure causality and slow debugger cycles. Use minimizers aggressively to isolate the smallest trigger that preserves behavior. A minimized artifact clarifies parser states, boundary transitions, and corruption points. It also produces cleaner reports and faster regression tests.

Sanitizers provide critical signal, but they are not the end of analysis. AddressSanitizer might report a heap overflow; you still need to determine reachable control influence, overwrite constraints, and realistic attacker preconditions. UndefinedBehaviorSanitizer may flag dangerous operations that are currently non-exploitable yet indicate brittle code likely to fail differently under compiler or platform changes. Triage should classify both immediate risk and latent risk. ... continue

Exploit Reliability over Cleverness

2026-02-22

Exploit writeups often reward elegance: shortest payload, sharpest primitive chain, most surprising bypass. In real engagements, the winning attribute is usually reliability. A moderately clever exploit that works repeatedly beats a brilliant exploit that succeeds once and fails under slight environmental variation.

Reliability is engineering, not luck.

The first step is to define what reliable means for your context:

If reliability is not measured, it is mostly imagined.

A practical reliability-first workflow: ... continue