app-layer-protocol:failed; doesn't match traffic with ALPROTO_UNKNOWN
I'm working on writing some signatures for some malware that uses a custom protocol. In my signatures, I'd like to be able to write "app-layer-protocol:failed;" in order to filter out traffic that has a known protocol associated with it. However, with that logic incorporated to my signatures, some of the signatures fail to fire. Upon inspection of these rule matches with and without that logic, I found that the rules that consistently fire are being "matched" with a protocol as ALPROTO_FAILED, and the ones that are not firing are not matched at all, and thus are labeled as having no app layer protocol, i.e. ALPROTO_UNKNOWN. There is no support for looking for traffic with app-layer-protocol:unknown; either. From my perspective, any traffic that has an "unknown" protocol, would be considered to have failed application layer protocol inspection. Therefore, I would like app-layer-protocol:failed; to match both traffic that has been classified as ALPROTO_UNKNOWN and ALPROTO_FAILED.
A tedious workaround to this thing would be to have something to the effect of
app-layer-protocol:!http; app-layer-protocol:!tls; app-layer-protocol:!smtp; app-layer-protocol:!ftp; ...
You get the idea. Unfortunately, that is a lot of extra logic to include in a signature, and also means the signature is not as easily maintained -- any additional protocols added to suricata would need to be NOT-ed out later.
I'm not sure if this is considered a feature or a bug... I felt it was a bug, as I expected my traffic to match against the signature whether application layer protocol detection failed or... ended up in an unknown state. Those are effectively the same state in my eyes, I understand that those might have different meanings within the code of suricata. Please let me know if I can help by providing any more information, clarifying anything, etc. I don't know how to label this in terms of effort or difficulty either; I ask that you please excuse my ignorance.
Updated by spencer walden about 3 years ago
- File alert-debug.log alert-debug.log added
- File bash_i_php_webshell.pcap bash_i_php_webshell.pcap added
- File fast.log fast.log added
- File proto.rules proto.rules added
Andreas Herz wrote:
Do you have some pcaps or specific cases that might help to reproduce the issue and work on a better implementation on code side?
I can provide PCAP, and give the specific case I'm talking about, sure!
...However, I'm not sure it will help make implementation better code side. I'm just asking for the keyword "app-layer-protocol" with the argument/value of "failed" to match traffic that has been marked as either ALPROTO_UNKNOWN or ALPROTO_FAILED.
The case we're looking at here:
- I've used a webshell I put on a webserver for testing purposes to spawn a reverse shell ("bash -i") back to me.
- The shell is coming from port ephemeral port 50666 back to me at port 8080 where I'm controlling the shell.
- I issue some standard Linux commands via the reverse shell.
- I'm trying to write rules looking for these Linux commands.
- There is no L7/Application Layer Protocol happening here, just raw TCP.
- In order to cut down the amount of packets that Suricata should inspect, I wanted to use "app-layer-protocol:failed;" as a check to make sure there is no L7 protocol in play, as I'm using the "tcp" protocol keyword in the rule header
- According to alert-debug.log, the protocol is determined to be 0, or ALPROTO_UNKNOWN
Additionally, I've attached the alert-debug.log file and the fast.log file associated with running the PCAP.
As you can see from the fast.log, only the signatures that don't have the check for "app-layer-protocol:failed;" properly fire on the traffic. In the alert-debug.log file, you can see that the signatures that did fire were the ones that don't check for an L7 protocol and that they claim the protocol is 0 -- See the line (FLOW APP_LAYER: DETECTED: FALSE, PROTO 0)
+================ TIME: 03/01/2019-01:13:39.800519 PCAP PKT NUM: 59 PKT SRC: wire/pcap SRC IP: 172.28.128.9 DST IP: 172.28.128.10 PROTO: 6 SRC PORT: 8080 DST PORT: 50666 TCP SEQ: 3373388884 TCP ACK: 1197728138 FLOW: to_server: FALSE, to_client: TRUE FLOW Start TS: 03/01/2019-01:13:36.705786 FLOW PKTS TODST: 5 FLOW PKTS TOSRC: 5 FLOW Total Bytes: 832 FLOW IPONLY SET: TOSERVER: TRUE, TOCLIENT: TRUE FLOW ACTION: DROP: FALSE FLOW NOINSPECTION: PACKET: FALSE, PAYLOAD: FALSE, APP_LAYER: FALSE FLOW APP_LAYER: DETECTED: FALSE, PROTO 0 PACKET LEN: 69 PACKET: 0000 08 00 27 59 7D D1 08 00 27 5C B3 68 08 00 45 00 ..'Y}... '\.h..E. 0010 00 37 C8 09 40 00 40 06 1A 6B AC 1C 80 09 AC 1C .7..@.@. .k...... 0020 80 0A 1F 90 C5 EA C9 11 D4 54 47 63 E1 8A 80 18 ........ .TGc.... 0030 00 E3 E4 B5 00 00 01 01 08 0A 00 CC 1F 58 00 CB ........ .....X.. 0040 F5 9A 6C 73 0A ..ls. ALERT CNT: 1 ALERT MSG : EXAMPLE No App Layer Protocol Check ls ALERT GID : 1 ALERT SID : 6 ALERT REV : 1 ALERT CLASS : Misc activity ALERT PRIO : 3 ALERT FOUND IN : PACKET ALERT IN TX : N/A PAYLOAD LEN: 3 PAYLOAD: 0000 6C 73 0A ls.
According to the Suricata source code (https://github.com/OISF/suricata/blob/c1b30fe9fd4970043a546d05ab57e23c837998e1/src/app-layer-protos.h#L29) Protocol 0 is ALPROTO_UNKNOWN. However, since I've said I'm looking for "app-layer-protocol:failed;", which maps to ALPROTO_FAILED, my signatures don't fire.
What I'm asking for is a code change such that the keyword "app-layer-protocol:failed;" actually maps to see if the app-layer-protocol is either ALPROTO_UNKNOWN || ALPROTO_FAILED.
Sorry for the slow response, the email that you had made a comment got lost in my inbox...
Thanks for taking a look at this!