Unexpected TCP session tracking

Hi, all.

During the reverse shell attack test, Suricata did not trigger any alerts, and the TCP session was recorded as timed out. And if you need pcap file of this test, please give me an email address.

Environment Information:

  • Suricata version: Suricata 8.0.0-dev
  • The traffic handled by Suricata is very low, approximately 20 Mbps.

flow json log:

{
  "timestamp": "2024-08-09T18:06:54.706322+0800",
  "flow_id": 663964755393370,
  "in_iface": "0000:5e:00.1",
  "event_type": "flow",
  "vlan": [
    626
  ],
  "src_ip": "10.182.14.14",
  "src_port": 55274,
  "dest_ip": "8.149.245.226",
  "dest_port": 80,
  "proto": "TCP",
  "app_proto": "failed",
  "flow": {
    "pkts_toserver": 24,
    "pkts_toclient": 26,
    "bytes_toserver": 8733,
    "bytes_toclient": 1761,
    "start": "2024-08-09T18:05:38.613343+0800",
    "end": "2024-08-09T18:05:47.887039+0800",
    "age": 9,
    "state": "closed",
    "reason": "timeout",
    "alerted": false
  },
  "metadata": {
    "flowbits": [
      "tcp_syn_sent",
      "tcp_syn_ack",
      "tcp_established"
    ]
  },
  "tcp": {
    "tcp_flags": "1b",
    "tcp_flags_ts": "1b",
    "tcp_flags_tc": "1b",
    "syn": true,
    "fin": true,
    "psh": true,
    "ack": true,
    "state": "closed",
    "ts_max_regions": 1,
    "tc_max_regions": 1
  },
  "host": "suricata01"
}

Test Rule:

alert tcp any any -> any any (msg:"input linux shell command"; flow:to_client,established; pcre:"/(id|whoami|ifconfig|ip\sa|ip\saddr|history)\n$/"; flowbits:set, linux_cmd; flowbits:noalert; classtype:misc-activity; sid:1000058; priority:3; rev:1;)
alert tcp any any -> any any (msg:"Detected reverse shell behavior"; flow:to_server,established; flowbits:isset,linux_cmd; lua:./lua/linux_reverse_shell.lua; classtype:command-and-control; sid:1000059; priority:1; rev:1;)

/var/lib/suricata/rules/lua/linux_reverse_shell.lua

local patterns = {
    "uid=%d+%(%w+%)%sgid=%d+%(%w+%)%sgroups=%d+%(%w+%)",
    "lo:%sflags=73<UP,LOOPBACK,RUNNING>%s%smtu%s65536",
    "root:x:0:0:root:%/root:%/bin%/bash",
}
local function match_regex(s)
    for _, pattern in ipairs(patterns) do
        if s:match(pattern) then
            return true
        end
    end
    return false
end

function init (args)
    local needs = {}
    needs["payload"] = tostring(true)
    return needs
end

function match(args)
    local payload = tostring(args["payload"])
    if not payload then
        return 0
    end
    ret = match_regex(payload)
    if ret then
        return 1
    end
    return 0
end

return 0

I can’t promise anything but I’d be happy to try the pcap. I’ll share my email address via DM

Thank for all. You can check your mailbox and the sha256 of pcap file is 1615f950485f89e0fb10448a06b1f45fc38aa8a58af8f272b699c65212f61841

Thanks – I received the pcap with the proper sha256 value.

The lua script is never being invoked since the rules aren’t matching.

1 Like

Oh, That is my fault. I changed regex expression. Now I have new problem.
my rules:

alert tcp any any -> any any (msg:"input linux shell command"; flow:to_client,established; pcre:"/(?:ifconfig|id|whoami|cat\s\/etc\/passwd|cat\s\/etc\/shadow|ip\saddr|ip\sa)\n/"; flowbits:set, linux_cmd; flowbits:noalert; classtype:misc-activity; sid:1000058; priority:3; rev:1;)
alert tcp any any -> any any (msg:"检测到反弹shell行为"; flow:to_server,established; flowbits:isset,linux_cmd; lua:./lua/linux_reverse_shell.lua; classtype:command-and-control; sid:1000059; priority:1; rev:1;)

and the lua script:


local patterns = {
    "uid=%d+%(%w+%)%sgid=%d+%(%w+%)%sgroups=%d+%(%w+%)",
    "lo:%sflags=73<UP,LOOPBACK,RUNNING>%s%smtu%s65536",
    "root:x:0:0:root:%/root:%/bin%/bash",
}
local function match_regex(s)
    for _, pattern in ipairs(patterns) do
        if s:match(pattern) then
            return true
        end
    end
    return false
end

function init (args)
    local needs = {}
    needs["payload"] = tostring(true)
    return needs
end

function match(args)
    local payload = tostring(args["payload"])
    SCLogNotice("payload: " .. payload)
    if not payload then
        return 0
    end
    ret = match_regex(payload)
    if ret then
        return 1
    end
    return 0
end

return 0

You can see, there is linux_cmd in flowbits from evelog, but nothing print by lua script in suricata.log

{
  "timestamp": "2024-08-23T14:51:48.876779+0800",
  "flow_id": 1228601676695012,
  "in_iface": "0000:5e:00.0",
  "event_type": "flow",
  "vlan": [
    626
  ],
  "src_ip": "10.182.14.14",
  "src_port": 53278,
  "dest_ip": "8.130.21.149",
  "dest_port": 58080,
  "proto": "TCP",
  "app_proto": "failed",
  "flow": {
    "pkts_toserver": 13,
    "pkts_toclient": 11,
    "bytes_toserver": 1240,
    "bytes_toclient": 749,
    "start": "2024-08-23T14:50:36.875880+0800",
    "end": "2024-08-23T14:50:43.085855+0800",
    "age": 7,
    "state": "closed",
    "reason": "timeout",
    "alerted": false
  },
  "metadata": {
    "flowbits": [
      "tcp_syn_sent",
      "tcp_syn_ack",
      "tcp_established",
      "linux_cmd"
    ]
  },
  "tcp": {
    "tcp_flags": "1b",
    "tcp_flags_ts": "1b",
    "tcp_flags_tc": "1b",
    "syn": true,
    "fin": true,
    "psh": true,
    "ack": true,
    "state": "closed",
    "ts_max_regions": 1,
    "tc_max_regions": 1
  },
  "host": "qrc6-sec-nta01.gz"
}

Thanks for the update. I’m using the latest rules and lua script. I’ve removed the flowbits: noalert and there are no alerts (nor flowbit in the log).

Are you using the same pcap you had sent to me previously?

Hi, here is my test cmdline and output,

root@zeek-1:/var/log/suricata# sha256sum /home/suricata/pcaps/revshell-3.pcap
1615f950485f89e0fb10448a06b1f45fc38aa8a58af8f272b699c65212f61841  /home/suricata/pcaps/revshell-3.pcap
root@zeek-1:/var/log/suricata# cat /home/suricata/rules/suricata.rules
alert tcp any any -> any any (msg:"input linux shell command"; flow:to_client,established; pcre:"/(?:ifconfig|id|whoami|cat\s\/etc\/passwd|cat\s\/etc\/shadow|ip\saddr|ip\sa)\n/"; flowbits:set, linux_cmd; flowbits:noalert; classtype:misc-activity; sid:1000058; priority:3; rev:1;)
alert tcp any any -> any any (msg:"检测到反弹shell行为"; flow:to_server,established; flowbits:isset,linux_cmd; lua:./lua/linux_reverse_shell.lua; classtype:command-and-control; sid:1000059; priority:1; rev:1;)
root@zeek-1:/var/log/suricata# suricata -k none --runmode single  -r /home/suricata/pcaps/revshell-3.pcap
Notice: suricata: This is Suricata version 8.0.0-dev (bb65c29ab 2024-07-26) running in USER mode [LogVersion:suricata.c:1158]
Notice: threads: Threads created -> W: 1 FM: 1 FR: 1   Engine started. [TmThreadWaitOnThreadRunning:tm-threads.c:1907]
Notice: suricata: Signal Received.  Stopping engine. [SuricataMainLoop:suricata.c:2829]
Notice: pcap: read 1 file, 40 packets, 8903 bytes [ReceivePcapFileThreadExitStats:source-pcap-file.c:388]
root@zeek-1:/var/log/suricata# cat flow.json | jq
{
  "timestamp": "2024-08-09T18:01:03.992743+0800",
  "flow_id": 399175472421635,
  "event_type": "flow",
  "vlan": [
    626
  ],
  "src_ip": "10.182.14.14",
  "src_port": 55108,
  "dest_ip": "8.149.245.226",
  "dest_port": 80,
  "proto": "TCP",
  "app_proto": "failed",
  "flow": {
    "pkts_toserver": 20,
    "pkts_toclient": 20,
    "bytes_toserver": 7534,
    "bytes_toclient": 1369,
    "start": "2024-08-09T18:00:49.486156+0800",
    "end": "2024-08-09T18:01:03.992743+0800",
    "age": 14,
    "state": "closed",
    "reason": "shutdown",
    "alerted": false
  },
  "metadata": {
    "flowbits": [
      "linux_cmd"
    ]
  },
  "tcp": {
    "tcp_flags": "1b",
    "tcp_flags_ts": "1b",
    "tcp_flags_tc": "1b",
    "syn": true,
    "fin": true,
    "psh": true,
    "ack": true,
    "state": "closed",
    "ts_max_regions": 1,
    "tc_max_regions": 1
  },
  "host": "zeek"
}

And output if I remove flowbits: noalert :

root@zeek-1:/var/log/suricata# suricata -k none --runmode single  -r /home/suricata/pcaps/revshell-3.pcap
Notice: suricata: This is Suricata version 8.0.0-dev (bb65c29ab 2024-07-26) running in USER mode [LogVersion:suricata.c:1158]
Notice: threads: Threads created -> W: 1 FM: 1 FR: 1   Engine started. [TmThreadWaitOnThreadRunning:tm-threads.c:1907]
Notice: suricata: Signal Received.  Stopping engine. [SuricataMainLoop:suricata.c:2829]
Notice: pcap: read 1 file, 40 packets, 8903 bytes [ReceivePcapFileThreadExitStats:source-pcap-file.c:388]
root@zeek-1:/var/log/suricata# cat alert.json | jq
{
  "timestamp": "2024-08-09T18:01:03.992290+0800",
  "flow_id": 399177711136546,
  "pcap_cnt": 38,
  "event_type": "alert",
  "src_ip": "8.149.245.226",
  "src_port": 80,
  "dest_ip": "10.182.14.14",
  "dest_port": 55108,
  "proto": "TCP",
  "toa_src_ip": "0.0.0.0",
  "toa_src_port": 0,
  "pkt_src": "wire/pcap",
  "metadata": {
    "flowbits": [
      "linux_cmd"
    ]
  },
  "alert": {
    "action": "allowed",
    "gid": 1,
    "signature_id": 1000058,
    "rev": 1,
    "signature": "input linux shell command",
    "category": "Misc activity",
    "severity": 3
  },
  "app_proto": "failed",
  "direction": "to_client",
  "flow": {
    "pkts_toserver": 19,
    "pkts_toclient": 19,
    "bytes_toserver": 7464,
    "bytes_toclient": 1303,
    "start": "2024-08-09T18:00:49.486156+0800",
    "src_ip": "10.182.14.14",
    "dest_ip": "8.149.245.226",
    "src_port": 55108,
    "dest_port": 80
  },
  "payload": "aWZjb25maWcKaWQKaXAgYWRkcgpjYXQgL2V0Yy9wYXNzd2QKZXhpdAo=",
  "payload_printable": "ifconfig\nid\nip addr\ncat /etc/passwd\nexit\n",
  "stream": 1,
  "host": "zeek"
}

Can you post or DM the suricata.yaml configuration file?

I’m glad you are handling this matter.
suricata.yaml :

%YAML 1.1
---
suricata-version: "8.0"
vars:
  address-groups:
    HOME_NET: "[192.168.0.0/16,10.0.0.0/8,172.16.0.0/12]"
    EXTERNAL_NET: "!$HOME_NET"
    HTTP_SERVERS: "$HOME_NET"
    SMTP_SERVERS: "$HOME_NET"
    SQL_SERVERS: "$HOME_NET"
    DNS_SERVERS: "$HOME_NET"
    TELNET_SERVERS: "$HOME_NET"
    AIM_SERVERS: "$EXTERNAL_NET"
    DC_SERVERS: "$HOME_NET"
    DNP3_SERVER: "$HOME_NET"
    DNP3_CLIENT: "$HOME_NET"
    MODBUS_CLIENT: "$HOME_NET"
    MODBUS_SERVER: "$HOME_NET"
    ENIP_CLIENT: "$HOME_NET"
    ENIP_SERVER: "$HOME_NET"
  port-groups:
    HTTP_PORTS: 80
    SHELLCODE_PORTS: "!80"
    ORACLE_PORTS: 1521
    SSH_PORTS: 22
    DNP3_PORTS: 20000
    MODBUS_PORTS: 502
    FILE_DATA_PORTS: "[$HTTP_PORTS,110,143]"
    FTP_PORTS: 21
    GENEVE_PORTS: 6081
    VXLAN_PORTS: 4789
    TEREDO_PORTS: 3544
default-log-dir: /var/log/suricata/
stats:
  enabled: yes
  interval: 8
plugins:
outputs:
  - fast:
      enabled: no
      filename: fast.log
      append: yes
  - eve-log:
      enabled: yes
      filetype: regular
      filename: alert.json
      types:
        - alert:
            payload: yes
            payload-buffer-size: 4kb
            payload-printable: yes
            http-header: yes
            http-body: no
            http-body-printable: yes
            tagged-packets: yes
            metadata:
              app-layer: true
              flow: true
              rule:
                metadata: true
                raw: false
      xff:
        enabled: yes
        mode: extra-data
        deployment: reverse
        header: X-Forwarded-For,X-Real-IP
  - eve-log:
      enabled: yes
      filetype: regular
      filename: flow.json
      types:
        - flow
  - eve-log:
      enabled: yes
      filetype: regular
      filename: tls.json
      types:
        - tls:
            enabled: yes
            extended: yes
  - eve-log:
      enabled: yes
      filetype: regular
      filename: eve.json
      pcap-file: false
      community-id: false
      community-id-seed: 0
      types:
        - frame:
            enabled: no
        - anomaly:
            enabled: no
            types:
        - http:
            enabled: no
            extended: no
        - dns:
            enabled: no
        - tls:
            enabled: no
            extended: no
        - files:
            enabled: no
        - smtp:
            enabled: no
        - dnp3:
            enabled: no
        - ftp:
            enabled: no
        - rdp:
            enabled: no
        - nfs:
            enabled: no
        - smb:
            enabled: no
        - tftp:
            enabled: no
        - ike:
            enabled: no
        - dcerpc:
            enabled: no
        - krb5:
            enabled: no
        - bittorrent-dht:
            enabled: no
        - snmp:
            enabled: no
        - rfb:
            enabled: no
        - sip:
            enabled: no
        - quic:
            enabled: no
        - dhcp:
            enabled: no
            extended: no
        - ssh:
            enabled: no
        - mqtt:
            enabled: no
        - http2:
            enabled: no
        - pgsql:
            enabled: no
        - stats:
            totals: yes
            threads: no
            deltas: no
  - http-log:
      enabled: no
      filename: http.log
      append: yes
  - tls-log:
      enabled: no
      filename: tls.log
      append: yes
  - tls-store:
      enabled: no
  - pcap-log:
      enabled: no
      filename: log.pcap
      limit: 1000mb
      max-files: 2000
      compression: none
      mode: normal
      use-stream-depth: no
      honor-pass-rules: no
  - alert-debug:
      enabled: yes
      filename: alert-debug.log
      append: yes
  - stats:
      enabled: yes
      filename: stats.log
      append: yes
      totals: yes
      threads: no
  - syslog:
      enabled: no
      facility: local5
  - file-store:
      version: 2
      enabled: no
      xff:
        enabled: no
        mode: extra-data
        deployment: reverse
        header: X-Forwarded-For
  - tcp-data:
      enabled: no
      type: file
      filename: tcp-data.log
  - http-body-data:
      enabled: no
      type: file
      filename: http-data.log
  - lua:
      enabled: no
      scripts:
logging:
  default-log-level: notice
  default-output-filter:
  outputs:
  - console:
      enabled: yes
  - file:
      enabled: yes
      level: info
      filename: suricata.log
  - syslog:
      enabled: no
      facility: local5
      format: "[%i] <%d> -- "
af-packet:
  - interface: bond1
    threads: 30
    cluster-id: 99
    cluster-type: cluster_flow
    defrag: yes
    use-mmap: yes
    tpacket-v3: yes
    ring-size: 8192
  - interface: default
af-xdp:
  - interface: default
dpdk:
  eal-params:
    proc-type: primary
  interfaces:
    - interface: 0000:3b:00.0
      threads: auto
      promisc: true
      multicast: true
      checksum-checks: true
      checksum-checks-offload: true
      mtu: 1500
      mempool-size: 65535
      mempool-cache-size: 257
      rx-descriptors: 1024
      tx-descriptors: 1024
      copy-mode: none
      copy-iface: none
    - interface: default
      threads: auto
      promisc: true
      multicast: true
      checksum-checks: true
      checksum-checks-offload: true
      mtu: 1500
      rss-hash-functions: auto
      mempool-size: 65535
      mempool-cache-size: 257
      rx-descriptors: 1024
      tx-descriptors: 1024
      copy-mode: none
      copy-iface: none
pcap:
  - interface: eth0
  - interface: default
pcap-file:
  checksum-checks: auto
app-layer:
  protocols:
    telnet:
      enabled: yes
    rfb:
      enabled: yes
      detection-ports:
        dp: 5900, 5901, 5902, 5903, 5904, 5905, 5906, 5907, 5908, 5909
    mqtt:
      enabled: yes
    krb5:
      enabled: yes
    bittorrent-dht:
      enabled: yes
    snmp:
      enabled: yes
    ike:
      enabled: yes
    tls:
      enabled: yes
      detection-ports:
        dp: 443
      ja3-fingerprints: yes
    pgsql:
      enabled: no
      stream-depth: 0
    dcerpc:
      enabled: yes
    ftp:
      enabled: yes
    rdp:
    ssh:
      enabled: yes
    http2:
      enabled: yes
    smtp:
      enabled: yes
      raw-extraction: no
      mime:
        decode-mime: yes
        decode-base64: yes
        decode-quoted-printable: yes
        header-value-depth: 2000
        extract-urls: yes
        body-md5: no
      inspected-tracker:
        content-limit: 100000
        content-inspect-min-size: 32768
        content-inspect-window: 4096
    imap:
      enabled: detection-only
    smb:
      enabled: yes
      detection-ports:
        dp: 139, 445
    nfs:
      enabled: yes
    tftp:
      enabled: yes
    dns:
      tcp:
        enabled: yes
        detection-ports:
          dp: 53
      udp:
        enabled: yes
        detection-ports:
          dp: 53
    http:
      enabled: yes
      libhtp:
         default-config:
           personality: IDS
           request-body-limit: 100kb
           response-body-limit: 100kb
           request-body-minimal-inspect-size: 32kb
           request-body-inspect-window: 4kb
           response-body-minimal-inspect-size: 40kb
           response-body-inspect-window: 16kb
           response-body-decompress-layer-limit: 2
           http-body-inline: auto
           swf-decompression:
             enabled: no
             type: both
             compress-depth: 100kb
             decompress-depth: 100kb
           double-decode-path: no
           double-decode-query: no
         server-config:
    modbus:
      enabled: no
      detection-ports:
        dp: 502
      stream-depth: 0
    dnp3:
      enabled: no
      detection-ports:
        dp: 20000
    enip:
      enabled: no
      detection-ports:
        dp: 44818
        sp: 44818
    ntp:
      enabled: yes
    quic:
      enabled: yes
    dhcp:
      enabled: yes
    sip:
asn1-max-frames: 256
datasets:
  defaults:
  rules:
security:
  limit-noproc: true
  landlock:
    enabled: no
    directories:
      read:
        - /usr/
        - /etc/
        - /etc/suricata/
  lua:
    allow-rules: true
    max-bytes: 50000000
    max-instructions: 6000000
    allow-restricted-functions: true
sensor-name: mysuricata
pid-file: /var/lib/suricata/suricata.pid
coredump:
  max-dump: unlimited
host-mode: sniffer-only
max-pending-packets: 65534
runmode: workers
autofp-scheduler: hash
unix-command:
  enabled: auto
legacy:
  uricontent: enabled
exception-policy: auto
engine-analysis:
  rules-fast-pattern: yes
  rules: yes
pcre:
  match-limit: 3500
  match-limit-recursion: 1500
host-os-policy:
  windows: [0.0.0.0/0]
  bsd: []
  bsd-right: []
  old-linux: []
  linux: []
  old-solaris: []
  solaris: []
  hpux10: []
  hpux11: []
  irix: []
  macos: []
  vista: []
  windows2k3: []
defrag:
  memcap: 10gb
  hash-size: 65536
  trackers: 65535
  max-frags: 65535
  prealloc: yes
  timeout: 60
flow:
  memcap: 10gb
  hash-size: 65536
  prealloc: 10000
  emergency-recovery: 30
vlan:
  use-for-tracking: false
livedev:
  use-for-tracking: false
flow-timeouts:
  default:
    new: 30
    established: 300
    closed: 0
    bypassed: 100
    emergency-new: 10
    emergency-established: 100
    emergency-closed: 0
    emergency-bypassed: 50
  tcp:
    new: 60
    established: 600
    closed: 600
    bypassed: 100
    emergency-new: 5
    emergency-established: 100
    emergency-closed: 10
    emergency-bypassed: 50
  udp:
    new: 30
    established: 300
    bypassed: 100
    emergency-new: 10
    emergency-established: 100
    emergency-bypassed: 50
  icmp:
    new: 30
    established: 300
    bypassed: 100
    emergency-new: 10
    emergency-established: 100
    emergency-bypassed: 50
stream:
  memcap: 10gb
  checksum-validation: yes
  inline: auto
  reassembly:
    memcap: 10gb
    depth: 1mb
    toserver-chunk-size: 2560
    toclient-chunk-size: 2560
    randomize-chunk-size: yes
host:
  hash-size: 4096
  prealloc: 1000
  memcap: 32mb
decoder:
  teredo:
    enabled: true
    ports: $TEREDO_PORTS
  vxlan:
    enabled: true
    ports: $VXLAN_PORTS
  geneve:
    enabled: true
    ports: $GENEVE_PORTS
detect:
  profile: medium
  custom-values:
    toclient-groups: 3
    toserver-groups: 25
  sgh-mpm-context: auto
  inspection-recursion-limit: 3000
  prefilter:
    default: mpm
  grouping:
  profiling:
    grouping:
      dump-to-disk: false
      include-rules: false
      include-mpm-stats: false
mpm-algo: auto
spm-algo: auto
threading:
  set-cpu-affinity: no
  cpu-affinity:
    - management-cpu-set:
        cpu: [ 1 ]
    - receive-cpu-set:
        cpu: [ 1 ]
    - worker-cpu-set:
        cpu: [ 0 ]
        mode: "balanced"
  detect-thread-ratio: 1.0
luajit:
  states: 128
profiling:
  rules:
    enabled: yes
    filename: rule_perf.log
    append: yes
    limit: 10
    json: yes
  keywords:
    enabled: yes
    filename: keyword_perf.log
    append: yes
  prefilter:
    enabled: yes
    filename: prefilter_perf.log
    append: yes
  rulegroups:
    enabled: yes
    filename: rule_group_perf.log
    append: yes
  packets:
    enabled: yes
    filename: packet_stats.log
    append: yes
    csv:
      enabled: no
      filename: packet_stats.csv
  locks:
    enabled: no
    filename: lock_stats.log
    append: yes
  pcap-log:
    enabled: no
    filename: pcaplog_stats.log
    append: yes
nfq:
nflog:
  - group: 2
    buffer-size: 18432
  - group: default
    qthreshold: 1
    qtimeout: 100
    max-size: 20000
capture:
netmap:
 - interface: eth2
 - interface: default
pfring:
  - interface: eth0
    threads: auto
    cluster-id: 99
    cluster-type: cluster_flow
  - interface: default
ipfw:
napatech:
    streams: ["0-3"]
    enable-stream-stats: no
    auto-config: yes
    hardware-bypass: yes
    inline: no
    ports: [0-1,2-3]
    hashmode: hash5tuplesorted
default-rule-path: /home/suricata/rules
rule-files:
  - suricata.rules
classification-file: /etc/suricata/classification.config
reference-config-file: /etc/suricata/reference.config

Thanks for sending your configuration file.

When I use

  • Your lua scripts and lua rules
  • Your pcap
  • Your suricata configuration
    There are 4 alerts
  • 1 with sid 1000059
  • 3 with sid 1000058

I’m using suricata master [3486477 - Suricata-Main] 2024-08-30 08:16:44 Notice: suricata: This is Suricata version 8.0.0-dev (304271e63a 2024-08-12) running in USER mode

I used this command line:

src/suricata -k none  -c /tmp/unexpected.yaml  -S ~/rules/lua.rules -l /tmp/ll -r /tmp/revshell-3.pcap  --set suricata.lua.allow-rules=true

Is it worth adding an SV test for these lua rules? Would need a public pcap of course.

Hi, Jeff. Thanks for your rely.
I try to run suricata like your cmdline, but I got an error msg:

root@host-1:/var/log/suricata# suricata -k none -c /etc/suricata/suricata.yaml -S /home/suricata/rules/suricata.rules -l /var/log/suricata/ -r /home/suricata/pcaps/revshell-3.pcap --set suricata.lua.allow-rules=true
Notice: suricata: This is Suricata version 8.0.0-dev (304271e63 2024-08-12) running in USER mode [LogVersion:suricata.c:1153]
Error: runmodes: The custom type "workers" doesn't exist for this runmode type "PCAP_FILE".  Please use --list-runmodes to see available custom types for this runmode [RunModeDispatch:runmodes.c:410]

why your cmdline work without --runmode?
and I switch to commit 304271e63a . It still only generate alert “sid:1000058”, not any “sid:1000059”. my cmdline:

suricata -k none -c /etc/suricata/suricata.yaml -S /home/suricata/rules/suricata.rules --runmode single -r /home/suricata/pcaps/revshell-3.pcap --set suricata.lua.allow-rules=true

I added --runmode=single to the command line and continue to get 4 alerts (same as before).

Can you post the output of suricata --build-info?

Of course. here is output :

root@host-1:/var/log/suricata# suricata --build-info
This is Suricata version 8.0.0-dev (304271e63 2024-08-12)
Features: PCAP_SET_BUFF AF_PACKET HAVE_PACKET_FANOUT LIBCAP_NG LIBNET1.1 HAVE_HTP_URI_NORMALIZE_HOOK PCRE_JIT HAVE_NSS HTTP2_DECOMPRESSION HAVE_LUA HAVE_JA3 HAVE_JA4 HAVE_LIBJANSSON TLS TLS_C11 MAGIC RUST POPCNT64
SIMD support: SSE_4_2 SSE_4_1 SSE_3 SSE_2
Atomic intrinsics: 1 2 4 8 16 byte(s)
64-bits, Little-endian architecture
GCC version 11.4.0, C version 201112
compiled with _FORTIFY_SOURCE=2
L1 cache line size (CLS)=64
thread local storage method: _Thread_local
compiled with LibHTP v0.5.48, linked against LibHTP v0.5.48

Suricata Configuration:
  AF_PACKET support:                       yes
  AF_XDP support:                          no
  DPDK support:                            no
  eBPF support:                            no
  XDP support:                             no
  PF_RING support:                         no
  NFQueue support:                         no
  NFLOG support:                           no
  IPFW support:                            no
  Netmap support:                          no
  DAG enabled:                             no
  Napatech enabled:                        no
  WinDivert enabled:                       no

  Unix socket enabled:                     yes
  Detection enabled:                       yes

  Libmagic support:                        yes
  libjansson support:                      yes
  hiredis support:                         no
  hiredis async with libevent:             no
  PCRE jit:                                yes
  GeoIP2 support:                          no
  JA3 support:                             yes
  JA4 support:                             yes
  Non-bundled htp:                         no
  Hyperscan support:                       no
  Libnet support:                          yes
  liblz4 support:                          yes
  Landlock support:                        yes
  Systemd support:                         yes

  Rust support:                            yes
  Rust strict mode:                        no
  Rust compiler path:                      /usr/bin/rustc
  Rust compiler version:                   rustc 1.75.0 (82e1608df 2023-12-21) (built from a source tarball)
  Cargo path:                              /usr/bin/cargo
  Cargo version:                           cargo 1.75.0

  Python support:                          yes
  Python path:                             /usr/bin/python3
  Install suricatactl:                     yes
  Install suricatasc:                      yes
  Install suricata-update:                 no, not bundled

  Profiling enabled:                       no
  Profiling locks enabled:                 no
  Profiling rules enabled:                 no

  Plugin support (experimental):           yes
  DPDK Bond PMD:                           no

Development settings:
  Coccinelle / spatch:                     no
  Unit tests enabled:                      no
  Debug output enabled:                    no
  Debug validation enabled:                no
  Fuzz targets enabled:                    no

Generic build parameters:
  Installation prefix:                     /usr
  Configuration directory:                 /etc/suricata/
  Log directory:                           /var/log/suricata/

  --prefix                                 /usr
  --sysconfdir                             /etc
  --localstatedir                          /var
  --datarootdir                            /usr/share

  Host:                                    x86_64-pc-linux-gnu
  Compiler:                                gcc (exec name) / g++ (real)
  GCC Protect enabled:                     no
  GCC march native enabled:                yes
  GCC Profile enabled:                     no
  Position Independent Executable enabled: no
  CFLAGS                                   -g -O2 -fPIC -std=c11 -march=native -I${srcdir}/../rust/gen -I${srcdir}/../rust/dist -I../rust/gen
  PCAP_CFLAGS                               -I/usr/include
  SECCFLAGS
root@host-1:/var/log/suricata#

As far as I know, the pattern in the lua script is not the reverse shell. The reverse shell is not looking like this. There are many script language to port a reverse shell, such as python, php, bash, javascript, perl and etc. If the pattern in the lua script is a bash script, it is not the reverse shell.

Yes, you are right, but the point of the topic is that rule 1000058 has already set the linux_cmd flowbit for this TCP stream, yet rule 1000059 still fails to trigger.

alert tcp any any -> any any (msg:"input linux shell command"; flow:to_client,established; pcre:"/(?:ifconfig|id|whoami|cat\s\/etc\/passwd|cat\s\/etc\/shadow|ip\saddr|ip\sa)\n/"; flowbits:set, linux_cmd; flowbits:noalert; classtype:misc-activity; sid:1000058; priority:3; rev:1;)
alert tcp any any -> any any (msg:"检测到反弹shell行为"; flow:to_server,established; flowbits:isset,linux_cmd; lua:./lua/linux_reverse_shell.lua; classtype:command-and-control; sid:1000059; priority:1; rev:1;)

As far as I know, you will never trigger the rule id 1000058 via the TCP flow as they are the *nix commands only. The question is how can you inspect the flow for these pattern?

I suggest you to use Wireshark to inspect the traffic flow before writing the rules.

Hi, Jeff. Thanks for all your help.
I’ve found a solution, and it works. Now my rule file and script is :

alert tcp-pkt any any -> any any (msg:"input linux shell command"; flow:to_client,established; pcre:"/(?:ifconfig|id|whoami|cat\s\/etc\/passwd|cat\s\/etc\/shadow|ip\saddr|ip\sa)\n/"; flowbits:set, linux_cmd; flowbits:noalert; classtype:misc-activity; sid:1000058; priority:3; rev:1;)
alert tcp any any -> any any (msg:"检测到反弹shell行为"; flow:to_server,established; lua:./lua/linux_reverse_shell.lua; flowbits:isset,linux_cmd; classtype:command-and-control; sid:1000059; priority:1; rev:1;)
local patterns = {
    "uid=%d+%(%w+%)%sgid=%d+%(%w+%)%sgroups=%d+%(%w+%)",
    "lo:%sflags=73<UP,LOOPBACK,RUNNING>%s%smtu%s65536",
    "root:x:0:0:root:%/root:%/bin%/bash",
}
local function match_regex(s)
    for _, pattern in ipairs(patterns) do
        if s:match(pattern) then
            return true
        end
    end
    return false
end

function init (args)
    local needs = {}
    needs["payload"] = tostring(true)
    return needs
end

function match(args)
    local payload = tostring(args["payload"])
    SCLogNotice("payload: " .. payload)
    if not payload then
        return 0
    end
    ret = match_regex(payload)
    if ret then
        return 1
    end
    return 0
end

return 0

As you can see, I replaced tcp with tcp-pkt. I suspect that the payload buffer in the TCP stream can only be checked a limited number of times, whereas tcp-pkt does not have this limitation. Is that correct?

tcp-pkt is for matching content in individual TCP packets. tcp-stream matches on the TCP stream only. Depending on how packets are fragmented, the first rule may not work items in the pcre as split across fragments.

1 Like