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