]> git.ipfire.org Git - thirdparty/suricata.git/commitdiff
websocket: configurable logging of payload in alerts 10873/head
authorPhilippe Antoine <pantoine@oisf.net>
Sat, 6 Jan 2024 22:09:05 +0000 (23:09 +0100)
committerVictor Julien <victor@inliniac.net>
Wed, 17 Apr 2024 05:17:02 +0000 (07:17 +0200)
etc/schema.json
rust/src/websocket/logger.rs
src/output-json-alert.c
suricata.yaml.in

index 451b1a6f6350676f2a6c96821f017a08841e4141..24e9da1d1a02a5be566b083ae83a0a2a30cedccf 100644 (file)
                 },
                 "opcode": {
                     "type": "string"
+                },
+                "payload_base64": {
+                    "type": "string"
+                },
+                "payload_printable": {
+                    "type": "string"
                 }
             },
             "additionalProperties": false
index ab8b5196261663e07b7c120fe8b5319b775d3e03..189d3ab7d9a6024c4a5768f58ed8154619efb2d3 100644 (file)
@@ -21,7 +21,9 @@ use crate::detect::EnumString;
 use crate::jsonbuilder::{JsonBuilder, JsonError};
 use std;
 
-fn log_websocket(tx: &WebSocketTransaction, js: &mut JsonBuilder) -> Result<(), JsonError> {
+fn log_websocket(
+    tx: &WebSocketTransaction, js: &mut JsonBuilder, pp: bool, pb64: bool,
+) -> Result<(), JsonError> {
     js.open_object("websocket")?;
     js.set_bool("fin", tx.pdu.fin)?;
     if let Some(xorkey) = tx.pdu.mask {
@@ -32,6 +34,15 @@ fn log_websocket(tx: &WebSocketTransaction, js: &mut JsonBuilder) -> Result<(),
     } else {
         js.set_string("opcode", &format!("unknown-{}", tx.pdu.opcode))?;
     }
+    if pp {
+        js.set_string(
+            "payload_printable",
+            &String::from_utf8_lossy(&tx.pdu.payload),
+        )?;
+    }
+    if pb64 {
+        js.set_base64("payload_base64", &tx.pdu.payload)?;
+    }
     js.close()?;
     Ok(())
 }
@@ -41,5 +52,12 @@ pub unsafe extern "C" fn rs_websocket_logger_log(
     tx: *mut std::os::raw::c_void, js: &mut JsonBuilder,
 ) -> bool {
     let tx = cast_pointer!(tx, WebSocketTransaction);
-    log_websocket(tx, js).is_ok()
+    log_websocket(tx, js, false, false).is_ok()
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn SCWebSocketLogDetails(
+    tx: &WebSocketTransaction, js: &mut JsonBuilder, pp: bool, pb64: bool,
+) -> bool {
+    log_websocket(tx, js, pp, pb64).is_ok()
 }
index 8465e2a566c92b0005ad656a1f6b48be9a87465a..eb88af294a89732113895806c8ceb73c1905610e 100644 (file)
 #define LOG_JSON_RULE_METADATA     BIT_U16(8)
 #define LOG_JSON_RULE              BIT_U16(9)
 #define LOG_JSON_VERDICT           BIT_U16(10)
+#define LOG_JSON_WEBSOCKET_PAYLOAD        BIT_U16(11)
+#define LOG_JSON_WEBSOCKET_PAYLOAD_BASE64 BIT_U16(12)
 
 #define METADATA_DEFAULTS ( LOG_JSON_FLOW |                        \
             LOG_JSON_APP_LAYER  |                                  \
             LOG_JSON_RULE_METADATA)
 
-#define JSON_BODY_LOGGING  (LOG_JSON_HTTP_BODY | LOG_JSON_HTTP_BODY_BASE64)
+#define JSON_BODY_LOGGING                                                                          \
+    (LOG_JSON_HTTP_BODY | LOG_JSON_HTTP_BODY_BASE64 | LOG_JSON_WEBSOCKET_PAYLOAD |                 \
+            LOG_JSON_WEBSOCKET_PAYLOAD_BASE64)
 
 #define JSON_STREAM_BUFFER_SIZE 4096
 
@@ -293,6 +297,20 @@ static void AlertAddAppLayer(const Packet *p, JsonBuilder *jb,
             void *tx = AppLayerParserGetTx(p->flow->proto, proto, state, tx_id);
             if (tx) {
                 jb_get_mark(jb, &mark);
+                switch (proto) {
+                    // first check some protocols need special options for alerts logging
+                    case ALPROTO_WEBSOCKET:
+                        if (option_flags &
+                                (LOG_JSON_WEBSOCKET_PAYLOAD | LOG_JSON_WEBSOCKET_PAYLOAD_BASE64)) {
+                            bool pp = (option_flags & LOG_JSON_WEBSOCKET_PAYLOAD) != 0;
+                            bool pb64 = (option_flags & LOG_JSON_WEBSOCKET_PAYLOAD_BASE64) != 0;
+                            if (!SCWebSocketLogDetails(tx, jb, pp, pb64)) {
+                                jb_restore_mark(jb, &mark);
+                            }
+                            // nothing more to log or do
+                            return;
+                        }
+                }
                 if (!al->LogTx(tx, jb)) {
                     jb_restore_mark(jb, &mark);
                 }
@@ -874,6 +892,8 @@ static void JsonAlertLogSetupMetadata(AlertJsonOutputCtx *json_output_ctx,
         SetFlag(conf, "payload-printable", LOG_JSON_PAYLOAD, &flags);
         SetFlag(conf, "http-body-printable", LOG_JSON_HTTP_BODY, &flags);
         SetFlag(conf, "http-body", LOG_JSON_HTTP_BODY_BASE64, &flags);
+        SetFlag(conf, "websocket-payload-printable", LOG_JSON_WEBSOCKET_PAYLOAD, &flags);
+        SetFlag(conf, "websocket-payload", LOG_JSON_WEBSOCKET_PAYLOAD_BASE64, &flags);
         SetFlag(conf, "verdict", LOG_JSON_VERDICT, &flags);
 
         /* Check for obsolete flags and warn that they have no effect. */
index 0c83e29660700790685d6e8f389976eafca02f99..89d1516a46680308d0bf6551318280ac71e7c975 100644 (file)
@@ -168,6 +168,8 @@ outputs:
             # metadata: no             # enable inclusion of app layer metadata with alert. Default yes
             # http-body: yes           # Requires metadata; enable dumping of HTTP body in Base64
             # http-body-printable: yes # Requires metadata; enable dumping of HTTP body in printable format
+            # websocket-payload: yes   # Requires metadata; enable dumping of WebSocket Payload in Base64
+            # websocket-payload-printable: yes # Requires metadata; enable dumping of WebSocket Payload in printable format
 
             # Enable the logging of tagged packets for rules using the
             # "tag" keyword.