HackBlue

Tools · Wireshark · 09 / 30

The display filter language

Comparison, logic, membership, and slices. The surprises are fields that occur twice, and fields that never share a frame.

13 min read · practice only with authorization

Comparisons and logic

A display filter compares a field to a value: ==, !=, >, <, >=, <=. && and || combine tests, and ! negates one. Strings take contains and matches. contains is a case-sensitive substring. matches is a regular expression, which is the wrong tool until contains fails you.

Membership keeps a filter readable. tcp.port in {80, 443} is the same idea as tcp.port == 80 || tcp.port == 443. Use the form you will still understand in a week. Quote strings. Leave numbers unquoted.

Snippet
tcp.port in {80, 443} && ip.addr == 10.8.0.40

A field can be true twice in one frame

ip.addr occurs twice, once for the source and once for the destination. The expression ip.addr == 10.8.0.40 && ip.addr == 10.8.0.20 matches a frame that has one of those addresses on each side. It is the usual way to say “between these two hosts” without caring about direction. ip.src == 10.8.0.40 && ip.dst == 10.8.0.20 is the directional version.

The same trick fails across packets. http.request.method == "POST" && http.response.code == 200 does not mean “the POST that got a 200.” The request and the response are different frames, so the two fields are not true together. Filter to the request, note tcp.stream, and then read that stream. The stream index is how a multi-frame fact stays one fact.

Worked case: the POST, then its reply

The lab app on 10.8.0.20 received a login. You filter http.request.method == "POST" && ip.addr == 10.8.0.20 and find frame 900 in tcp.stream 14. The response is not in that row. You replace the filter with tcp.stream == 14 and read the next HTTP response in that stream. The status code sits on its own frame, a few packets later.

You also want every lab host that touched either port 80 or port 443. tcp.port in {80, 443} && ip.addr == 10.8.0.0/24 does that in one line. Click a surprising row and check the field, because a match on tcp.port is true for the source port or the destination port. An ephemeral source port of 443 is rare and worth a second look when it happens.

Slices when the dissector has no field

A slice such as tcp.payload[0:4] tests raw bytes. Reach for it when you know the offset and the dissector has not named the field. Prefer a named field when one exists. Named fields survive version changes in your notes better than offsets do.

When a filter returns nothing, the field may be absent rather than different. http.authorization with no comparison matches frames where that field exists. That presence test is how you find “did anyone send the header,” which is a different question from “did this value appear.”

  • == and != compare one field to a value.
  • && and || combine tests. Parentheses group them.
  • ip.addr twice means the two ends of one frame.
  • A request field and a response field need the stream, not one &&.

Diagram

One frame can hold a field twice. Two frames cannot share a single test.

  1. Compare

    Compare a field to a literal. Quote strings and group the logic.

    Membership
    tcp.port in {80, 443} && ip.addr == 10.8.0.40
  2. Two ends

    Repeating ip.addr matches the two addresses of a single frame.

  3. Two frames

    A request and its response live in one tcp.stream, not in one row.

  4. Presence

    A bare field name tests that the field exists.

    Presence
    http.authorization

How to say “these two”

Two addresses, one frame

Filter. ip.addr == 10.8.0.40 && ip.addr == 10.8.0.20

Holds. Each side of the same frame supplies one match.

Use. The conversation, in either direction.

Request and response

Filter. http.request.method == "POST" && http.response.code == 200

Holds. Almost never. Those fields sit on different frames.

Use. Find the POST, then filter tcp.stream for that index.

Check yourself

1. Which filter selects packets between 10.8.0.40 and 10.8.0.20, either way?

2. You found a POST in tcp.stream 14 and need its HTTP status. What do you filter next?

3. Which filter finds frames that carry an HTTP Authorization header, whatever the value?