cswork / libraryOpen practice workspace

Articles

Snort Interview Questions: Rule Matching, Stream Context, and False Positives

Practice Snort interview questions with flow state, sticky buffers, normalized HTTP matching, false positives, and controlled traffic validation.

A Snort rule fires on a harmless export request, but misses a similar request split across packets. An interview answer that starts with “change the threshold” has skipped the important questions: what did the sensor observe, which buffers did the rule inspect, and what does a match actually establish?

These Snort interview questions use one original lab rule to separate three decisions: whether traffic satisfies the detection predicate, whether the engine has the required stream context, and whether an alert represents a policy violation. Keep those decisions separate when explaining both missed detections and false positives.

Evidence boundary: Official facts below refer to the current Snort 3 Rule Writing Guide, checked October 6, 2026. This is Snort 3 syntax, not an interchangeable Snort 2 rule recipe. The requests, labels and calculations are original practice exercises. No candidate reports or employer-specific interview-frequency claims are used. The local checks validate the exercises; they do not constitute Snort execution or measured detection performance.

For a related boundary drill, Design a packet reassembler API helps explain what incomplete input can and cannot establish.

What must match before this rule generates an alert?

Official fact: A conventional rule header identifies an action and traffic constraints; its detection options must also be satisfied. Snort 3 additionally supports service rule headers, such as alert http, for identified service traffic without specifying addresses and ports in the header.

Do not translate that service header into “inspect anything using port 80.” Service identification and the configured inspection path matter. A port-based TCP header and an HTTP service header express different constraints.

Consider this original, source-reviewed Snort 3 lab rule, using an exercise SID that must be checked for uniqueness in a real local ruleset:

alert http (
    msg:"LAB export request marker";
    flow:to_server,established;
    http_method;
    content:"POST";
    bufferlen:4;
    http_uri:path;
    content:"/lab/export";
    bufferlen:11;
    http_client_body;
    content:"lab_action=export";
    sid:1000001;
    rev:1;
)

Its purpose is intentionally narrow: find an established client request with the selected method, normalized path and body marker. It does not establish that an export is unauthorized.

The rule has three buffer-specific tests. POST occupies four bytes, so the method content test plus whole-buffer length constrains the selected method to that string. /lab/export occupies eleven bytes, so the corresponding path tests exclude a longer path containing the same substring. The body condition remains a substring search.

Official fact: bufferlen tests the current buffer length. Without a preceding sticky-buffer selection, its default context differs from the HTTP method or URI buffer intended here. Read each buffer selector before interpreting the following content and length options.

A useful answer states the conjunction explicitly: identified HTTP, request direction, established session, exact selected method, exact selected normalized path, and body substring. If one condition fails, this rule's detection predicate fails. Do not assume ordered first-match firewall behavior across Snort rules.

Why does changing the sticky buffer change the answer?

Official fact: Snort 3 HTTP sticky buffers select the inspection context for subsequent payload options. The selector precedes its associated content checks. Snort 2 examples can use different placement conventions, so establish the engine version before copying a rule.

Suppose the bytes lab_action=export appear in the URL query, but the request body is empty. A search of an undifferentiated request string might succeed. This rule should not satisfy its body condition: http_client_body selects the request-body inspection buffer.

A marker in the query cannot satisfy a content condition scoped to the request body.

Here is an original fixture table. Assume HTTP identification and the required stream state are available unless a row changes them. “Match” means the stated predicate, not a validated engine replay.

Changed fixturePredicate resultExplanation
POST, /lab/export, body marker presentMatchAll stated conditions hold
GET with the same path and bodyNo matchThe method condition fails
POST to /lab/export/archiveNo matchThe selected path exceeds eleven bytes
Marker only in the URI queryNo matchThe body buffer has no marker
Server response containing the markerNo matchRequest direction is required
POST body contains not_lab_action=exportMatchSubstring matching does not parse a form-field name

The final row exposes a genuine limitation of the exercise rule. Content matching proves only the selected byte pattern. It does not prove an exact form parameter or its business meaning. Before proposing a more structured condition, establish the application's encoding and the inspector's supported buffer semantics.

Official fact: http_uri selects normalized URI information, while http_raw_uri retains its raw counterpart. Selectors can isolate components such as path and query. Normalization behavior depends partly on configuration; do not claim every possible encoding is transformed identically.

For an interview follow-up, compare the actual raw and normalized buffers for one authorized fixture. Our local checks take normalized paths as explicit inputs; they do not implement or simulate Snort's URI normalization.

How can packet boundaries cause misleading reasoning?

A TCP packet is not necessarily a complete application message. Imagine a request body delivered as two successive pieces:

piece A: lab_action=ex
piece B: port

Neither piece alone contains the full marker. The assembled application body does. This is an original boundary exercise, not a supplied attack trace.

Official fact: The flow option can constrain direction, established state and reassembled versus non-reassembled traffic. to_server,established specifies request direction and connection state; it is not equivalent to only_stream and does not by itself demonstrate that every relevant byte was reconstructed.

Inference for troubleshooting: A missed body condition may arise before content evaluation. Inspect capture coverage, stream handling, service identification and whether the intended HTTP body reaches the selected buffer. A sensor cannot infer application bytes it never observed, and encrypted application traffic does not become plaintext merely because a rule mentions HTTP.

Our Python fixture concatenates already ordered body pieces solely to test the substring boundary. It does not handle TCP sequence numbers, retransmissions, missing segments, inspection limits or encrypted transport. Explain that distinction if asked what the test proves.

A packet capture containing the marker is therefore only one piece of evidence. Ask whether the correct direction reached Snort, whether the session was recognized as required, and whether the inspector exposed that marker in the relevant buffer. Keep the failing pcap and configuration together so a reviewer can reconstruct the missing-buffer decision.

What does a false positive mean in this lab?

Assume the application permits exports by authorized users and forbids exports by other users. Both groups can send the same method, path and marker. Authorization is not represented in our detection predicate.

Inference: An alert can be correct as a byte-level match and still be a false positive against the operational policy. Conversely, no alert is not proof that an unauthorized export did not happen. Ground truth needs application or investigation evidence independent of the rule.

Use this original labeled cohort of 1,000 requests. These numbers are invented to teach denominators, not reported product accuracy:

Application-policy labelAlertNo alertTotal
Unauthorized export20 true positives5 false negatives25
Legitimate request30 false positives945 true negatives975
Total509501,000

Precision is 20 / 50 = 40%: of the alerts, how many identify the labeled violation? Recall is 20 / 25 = 80%: of the labeled violations, how many triggered? The false-positive rate is 30 / 975 ≈ 3.08%: of legitimate requests, how many triggered?

State each denominator before naming the metric.

Thirty false positives out of fifty alerts is not a 60% false-positive rate. It is the false discovery fraction among alerts. Choosing the wrong denominator changes the operational interpretation even when every count is correct.

Before tuning, inspect the thirty legitimate matches. Are they authorized exports, substring collisions, duplicated events or fixtures whose labels are wrong? Inspect the five misses separately. A visibility failure and an overly narrow path predicate need different repairs.

Will detection_filter solve the false positives?

Official fact: detection_filter adds a rate condition tracked by source or destination and is evaluated after the other rule options. Its count describes a rate that must be exceeded before events are generated under that condition.

For example, a reviewed lab variation might add:

detection_filter:track by_src,count 5,seconds 60;

That changes which matching activity generates events. It does not parse the application's authorization state or fix the body substring collision.

Choose a rate threshold only after checking which labeled violations it would suppress.

If legitimate exports are frequent and unauthorized exports are rare, rate filtering may suppress valuable rare detections while leaving noisy frequent behavior. Shared source addresses can also aggregate activity that the application attributes to different users. State the tracking key and the expected operational consequence before choosing a number.

For a defensible comparison, replay the same labeled cohort against the original and proposed configurations, retain event identifiers, and recalculate both misses and false alerts. Fewer alerts can remove true positives as well as false positives; compare both cells against the same independent labels. Add negative controls for the specific legitimate pattern that motivated the change.

How would you verify the rule and diagnose no alerts?

Start with an authorized offline lab, a pinned Snort 3 build, the actual Lua configuration and a known ruleset. The official rules usage guide documents loading a rule with -R, reading a pcap with -r, and choosing alert output.

A proposed replay command is:

snort -c /path/to/snort.lua -R local.rules \
  -r authorized-lab.pcap -A alert_fast

This command has not been run for this article. Source-reviewed option placement is not evidence that a rule compiles under your installed build or that its traffic produces exactly one event.

Check configuration and rule loading first, then packet statistics, identified service, required flow state, exposed buffers and alert logging. The reading traffic guide distinguishes offline inputs and supported acquisition settings. Keep input, inspection and output evidence separate.

Replay POST versus GET, the eleven-byte path versus its longer suffix, and body-marker versus query-only fixtures.

Include the positive request, each one-condition negative, the longer path, the marker in the wrong buffer, the substring collision, and the split-body fixture. A known positive distinguishes a detection failure from an empty or misconfigured input. A known negative distinguishes your intended predicate from a broader accidental match.

For this article, twelve local checks verify byte lengths, the explicit fixture conjunction, the split-string illustration and the cohort arithmetic. They do not load Snort, validate Lua, replay a pcap or establish capture completeness. In an interview, explain what you would execute next and which uncertainty that execution resolves.

Sources and Further Reading