.. role:: example-rule-emphasis SSH Keywords ============ Suricata has several rule keywords to match on different elements of SSH connections. .. _ssh-hooks: Hooks ----- The SSH parser exposes a per-direction state machine. A rule that hooks one of the states below is evaluated once the transaction's progress in that direction has reached the state; for firewall rules the verdict applies while the transaction stays at the hooked state and stops applying once it advances past it (see :ref:`app-layer-state`). Request (``to_server``) side: +---------------------------+-------------------------------------------------+ | State | Meaning | +===========================+=================================================+ | ``request_banner`` | Version exchange: the banner (version) phase; | | | the first packets of the direction. A banner | | | line of 256 bytes or more without an | | | end-of-line keeps the direction here: the line | | | is consumed greedily until the end-of-line, and | | | the condition is observable through the | | | ``ssh.long_banner`` event (the banner data is | | | published when the long line first parses). | +---------------------------+-------------------------------------------------+ | ``request_kex`` | A valid banner line was parsed; key exchange. | | | ``ssh.proto``, ``ssh.software`` and the hassh | | | buffers are registered at this state. A long | | | banner can publish them while the line is | | | still open; if the line never completes, the | | | values are not inspected at a clean flow end | | | (the direction stays below the registered | | | state); a flow that ends disrupted (depth | | | truncation or async reassembly) reads as past | | | this state, and the values are inspected there | +---------------------------+-------------------------------------------------+ | ``request_session`` | A ``new keys`` record was seen in this | | | direction: the session is established for that | | | direction; the two directions are tracked | | | independently (a parse failure in either ends | | | the flow's app-layer parsing, see Completion | | | and failure). ``new keys`` is a transition | | | rather than a phase with its own data: match | | | the record itself with a frame rule (message | | | code 21, see the example below). | +---------------------------+-------------------------------------------------+ Response (``to_client``) side: +---------------------------+-------------------------------------------------+ | State | Meaning | +===========================+=================================================+ | ``response_banner`` | Server banner (version) phase: the same | | | behavior as ``request_banner`` above, in the | | | server-to-client direction. | +---------------------------+-------------------------------------------------+ | ``response_kex`` | Server key exchange: same as | | | ``request_kex`` — the direction's | | | ``ssh.proto`` / ``ssh.software`` and hassh | | | buffers are registered at this state. | +---------------------------+-------------------------------------------------+ | ``response_session`` | A ``new keys`` record was seen in the | | | server-to-client direction: same as | | | ``request_session``. | +---------------------------+-------------------------------------------------+ Overly long banner lines ~~~~~~~~~~~~~~~~~~~~~~~~ A banner line of 256 bytes or more without an end-of-line keeps the direction in the banner state. The line is consumed greedily until the end-of-line arrives, and the direction then advances to ``kex``. The condition is observable through the ``ssh.long_banner`` app-layer event (the banner data is published when the long line first parses). If the line does not parse as a banner when it completes, the direction fails and is frozen in ``banner``. Completion and failure ~~~~~~~~~~~~~~~~~~~~~~ ``request_done`` / ``response_done`` is the registered completion state. No direction ever reports it: a successful direction tops out at ``session``, and a direction that hits an unrecoverable error — an invalid banner or an invalid record (header or payload) — is frozen in the state the error occurred in. It is the last progress the direction reports, and the reason is available through the ``ssh.invalid_banner`` / ``ssh.invalid_record`` app-layer events and the per-direction ``error`` / ``state`` fields of the eve ``ssh`` object (``invalid_banner`` / ``invalid_record`` and the state the failure occurred in; a failed flow is logged even when no banner was parsed, so the error field may be its only content). A parse failure puts the flow into an error state: nothing after the failing packet is parsed or delivered, mid-flow or at the flow-end flush, and the frozen state is what the progress accessor - and thus the flow-end eve object - reports. The ``ssh.invalid_banner`` / ``ssh.invalid_record`` events are evaluated as soon as the tx is next inspected - the failing packet itself unless the error policy skips it - at the latest the flow-end flush. A keyword registered at the failed state gets no eof run (eof requires progress above the registered state), so its flow-end evaluation stays non-terminal; states before it were already terminalized when the direction left them. The ``ssh.long_banner`` and ``ssh.long_kex_record`` events are non-fatal: the direction continues (a long banner keeps it in ``banner`` until the line completes; an oversized key-exchange record keeps it in ``kex`` until the record has been buffered in full). Consequences worth knowing: * ``ssh:request_complete`` (and the firewall policy key ``request-complete``) is bound to the completion state, which no direction reports: the hook matches nothing for well-formed flows. A disrupted flow (depth truncation or async reassembly) matches, because on a disrupted flow the engine reports the completion state as the transaction progress instead of querying the parser. Before this change it matched a flow whose session was established. Review existing ``accept:* ssh:request_complete`` rules and ``request-complete`` firewall policies: a session-admit rule like that no longer fires. Failures are reported through the ``ssh.invalid_banner`` / ``ssh.invalid_record`` events instead. * Rules cannot reference ``done`` (``request_done`` / ``response_done`` do not load): it is out of both name tables. The generic hook lists for the completion state are registered under the built-in completion names (``ssh:request_complete:generic`` / ``ssh:response_complete:generic``); the state-name-based lists are not registered for the completion id (the id-to-name lookup returns null). They run only at a disrupted flow end, where the engine reports the completion state as the transaction progress instead of querying the parser - so Lua hooks on ssh completion no longer fire for well-formed flows; before this change the accessor reported the terminal state once both directions had sent NewKeys. * ``app-layer-state:>request_session`` matches only directions that were *disrupted past session (e.g. by depth truncation or async reassembly)* — a successful request direction stops at ``request_session``, and a failed one is frozen at the state it failed in (use the ``ssh.invalid_*`` events for that). For firewall rules and ``app-layer-state`` users, :doc:`../firewall/ssh-state-migration` maps the old hook names to the new phases and describes the behaviour changes. Frames ------ The SSH parser supports the following frames: * ssh.record_hdr * ssh.record_data * ssh.record_pdu These are header + data = pdu for SSH records, after the banner and before encryption. The SSH record header is 6 bytes long : 4 bytes length, 1 byte passing, 1 byte message code. Example: .. container:: example-rule alert ssh any any -> any any (msg:"hdr frame new keys"; :example-rule-emphasis:`frame:ssh.record.hdr; content: "|15|"; endswith;` bsize: 6; sid:2;) This rule matches like Wireshark ``ssh.message_code == 0x15``. ssh.proto --------- Match on the version of the SSH protocol used. ``ssh.proto`` is a sticky buffer, and can be used as a fast pattern. ``ssh.proto`` replaces the previous buffer name: ``ssh_proto``. You may continue to use the previous name, but it's recommended that existing rules be converted to use the new name. Format:: ssh.proto; Example: .. container:: example-rule alert ssh any any -> any any (msg:"match SSH protocol version"; :example-rule-emphasis:`ssh.proto;` content:"2.0"; sid:1000010;) The example above matches on SSH connections with SSH version 2.0. ssh.software ------------ Match on the software string from the SSH banner. ``ssh.software`` is a sticky buffer, and can be used as fast pattern. Format:: ssh.software; Example: .. container:: example-rule alert ssh any any -> any any (msg:"match SSH software string"; :example-rule-emphasis:`ssh.software;` content:"openssh"; nocase; sid:1000020;) The example above matches on SSH connections where the software string contains "openssh". ssh.hassh --------- Match on hassh (md5 of hassh algorithms of client). .. container:: example-rule alert ssh any any -> any any (msg:"match hassh"; \ ssh.hassh; content:"ec7378c1a92f5a8dde7e8b7a1ddf33d1";\ sid:1000010;) ``ssh.hassh`` is a 'sticky buffer'. ``ssh.hassh`` can be used as ``fast_pattern``. ssh.hassh.string ---------------- Match on Hassh string (hassh algorithms of client). .. container:: example-rule alert ssh any any -> any any (msg:"match hassh-string"; \ ssh.hassh.string; content:"none,zlib@openssh.com,zlib"; \ sid:1000030;) ``ssh.hassh.string`` is a 'sticky buffer'. ``ssh.hassh.string`` can be used as ``fast_pattern``. ssh.hassh.server ---------------- Match on hassh (md5 of hassh algorithms of server). .. container:: example-rule alert ssh any any -> any any (msg:"match SSH hash-server"; \ ssh.hassh.server; content:"b12d2871a1189eff20364cf5333619ee"; \ sid:1000020;) ``ssh.hassh.server`` is a 'sticky buffer'. ``ssh.hassh.server`` can be used as ``fast_pattern``. ssh.hassh.server.string ----------------------- Match on hassh string (hassh algorithms of server). .. container:: example-rule alert ssh any any -> any any (msg:"match SSH hash-server-string"; \ ssh.hassh.server.string; content:"umac-64-etm@openssh.com,umac-128-etm@openssh.com"; \ sid:1000040;) ``ssh.hassh.server.string`` is a 'sticky buffer'. ``ssh.hassh.server.string`` can be used as ``fast_pattern``.