┌──(root💀securitynik)-[/tmp/zeek] └─# zeek4 --version zeek4 version 4.2.0-dev.448
Let's look at this from 3 different perspectives. First, let's simply run Zeek and take a look at the logs that have been created based on our Indicators of Compromise (IoC). Running Zeek against the pcap file.
┌──(root💀securitynik)-[/tmp/zeek] └─# zeek --no-checksums -r /home/securitynik/packets/empire-full-session.pcap
Looking at the logs created.
┌──(root💀securitynik)-[/tmp/zeek] └─# ls *.log conn.log dns.log files.log http.log packet_filter.log pe.log reporter.log ssh.log weird.log
In a production environment, you will have lots more logs. So we don't want to go through all the logs. Let's leverage grep to find our IoCs..
┌──(root💀securitynik)-[/tmp/zeek] └─# grep --perl-regexp "\/(?i)(news|login|admin).*?php" *.log --color=always | more ... http.log:1637431443.514058 CcC0YB2heOkR9Gnpi4 10.0.0.110 1650 10.0.0.107 443 3 POST 10.0.0.107 /news.php - 1.1 securitynik-launcher-bat-User-Agent - 206 44506 200 OK - - (empty) - - - F4wiHG1bpwW03v71vh - - FghBlt1qKU0utYSHd7 - - http.log:1637431506.697538 CcC0YB2heOkR9Gnpi4 10.0.0.110 1650 10.0.0.107 443 4 GET 10.0.0.107 /admin/get.php - 1.1 Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko - 0 1279 200 OK - - (empty) - - - - - - FvSNLf3mGluOXZ5Qu - text/html http.log:1637431566.836488 CcC0YB2heOkR9Gnpi4 10.0.0.110 1650 10.0.0.107 443 5 GET 10.0.0.107 /login/process.php - 1.1 Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko - 0 1279 200 OK - -(empty) - - - - - - F6xbg04XsiJmRygrwc - text/html ...
At this point, we identify Zeek has logs pertaining to this activity. Let's figure out which logs this activity is found in.
┌──(root💀securitynik)-[/tmp/zeek] └─# grep --perl-regexp "\/(?i)(news|login|admin).*?php" *.log | awk --field-separator ':' '{ print $1 }' | sort | uniq --count | sort --numeric --reverse 1807 http.log
Looks like activity was only seen in the http.log. Focusing here a bit more, extracting a few key fields.
┌──(root💀securitynik)-[/tmp/zeek] └─# zeek-cut -d ts uid id.orig_h id.orig_p id.resp_h id.resp_p method host uri < http.log | grep --perl-regexp "\/(?i)(news|login|admin).*?php" ts uid id.orig_h id.orig_p id.resp_h id.resp_p method host uri .... 2021-11-20T20:21:40-0500 Cp6N4630HCYa17LUa8 10.0.0.110 1697 10.0.0.107 443 GET 10.0.0.107 /news.php 2021-11-20T20:22:20-0500 CwazkE3YeyHUnSxQ7f 10.0.0.110 1723 10.0.0.107 443 GET 10.0.0.107 /admin/get.php 2021-11-20T20:22:38-0500 C9qPFB1lfO6u42p4G3 10.0.0.110 1699 10.0.0.107 443 GET 10.0.0.107 /login/process.php ...
Picking on the first UID, to see where else activity has been identified.
┌──(root💀securitynik)-[/tmp/zeek] └─# grep "Cp6N4630HCYa17LUa8" *.log | cut --fields 1 --delimiter ":" | sort | uniq --count | sort --numeric --reverse 315 files.log 311 http.log 1 conn.log
Looking at the 1 entry in the conn.log, while there is a lot to see, extracting a few fields and looking at the duration, shows this activity lasted 18437.626252 seconds or 307 hours or 5 days.
┌──(root💀securitynik)-[/tmp/zeek] └─# zeek-cut -d ts uid id.orig_h id.orig_p id.resp_h id.resp_p proto service duration orig_bytes resp_bytes < conn.log | grep "C9qPFB1lfO6u42p4G3" 2021-11-20T18:02:04-0500 C9qPFB1lfO6u42p4G3 10.0.0.110 1699 10.0.0.107 443 tcp http 18437.626252 55444 462342
Looking at the files.log, I did not really find anything of interest.
┌──(root💀securitynik)-[/tmp/zeek] └─# grep "Cp6N4630HCYa17LUa8" files.log | more 1637449322.213963 FbvsAO3tTXJzOyuaj4 10.0.0.107 10.0.0.110 Cp6N4630HCYa17LUa8 HTTP 0 (empty) text/html - 0.000479 - F 1279 1279 0 0 F - - - - - - - 1637449382.306875 FkJSID2jB0CMQfrcZb 10.0.0.107 10.0.0.110 Cp6N4630HCYa17LUa8 HTTP 0 (empty) text/html - 0.000484 - F 1279 1279 0 0 F - - - - - - - 1637449442.421711 F4DJ7Z1wfsWwZ0INka 10.0.0.107 10.0.0.110 Cp6N4630HCYa17LUa8 HTTP 0 (empty) text/html - 0.000375 - F 1279 1279 0 0 F - - - - - - - 1637449502.518040 Fnn8vklGr2q1mTGt2 10.0.0.107 10.0.0.110 Cp6N4630HCYa17LUa8 HTTP 0 (empty) text/html - 0.000484 - F 1279 1279 0 0 F - - - - - - ....
Additionally, no files were actually extracted as can be seen by the absenece of the extracted folder.
┌──(root💀securitynik)-[/tmp/zeek] └─# ls -l extracted_files/ ls: cannot access 'extracted_files/': No such file or directory
We can spend more time poking around. However, the objective was to do some basic detection and we have achieved that.
Second, let's take a look at a Zeek signature to detect our IoC.
While Zeek can be used for signature detection, it is not the recommended way to use Zeek. However, since it does provide the capability to perform some basic signature based detection, then let's take advantage of this feature.
Couple of different ways to do this. Let's take one.
# This signature is about detecting PowerShell Empire in my lab signature powershellEmpire { # look into the IP Header at offset 6 for tcp header ip[9] == 0x06 # Look for the soruce address representing out protected network # Look at the IP header, start at offset 12 and span 4 bytes for the # the source IP header ip[12:4] == 10.0.0.110/32 # Look for the destination address # Look at the IP header, start at offset 15 and span 4 bytes for the # the destination IP # Consider anything going to none 10.0.0.0/32 destinations as something we don't own. header ip[16:4] != 10.0.0.110/32 # Look for destination port 443 header tcp[2:2] == 443 # Look for the ASCII strings news or admin or login followed by php http /.*(news|admin|login).*\.php/ # Ensure the 3-way handshake has completed and # The traffic is coming from an originator tcp-state established,originator # Generate an event event "SUSPICIOUS POSSIBLE Powershell Empire Activity" }
Running Zeek
┌──(root💀securitynik)-[/tmp] └─# zeek --readfile /home/securitynik/packets/empire-full-session.pcap --rulefile /home/securitynik/packets/powershell.sig --no-checksums
Verifying the files created
┌──(root💀securitynik)-[/tmp] └─# ls *.log conn.log dns.log files.log http.log notice.log packet_filter.log pe.log signatures.log ssh.log weird.log
Taking at look at the signature.log file, we see ...
┌──(root💀securitynik)-[/tmp] └─# zeek-cut -d -m < signatures.log ts uid src_addr src_port dst_addr dst_port note sig_id event_msg sub_msg sig_count host_count 2021-11-20T13:03:59-0500 CskRYg2jaNXRC0fLy8 10.0.0.110 1650 10.0.0.107 443 Signatures::Sensitive_Signature powershellEmpire 10.0.0.110: SUSPICIOUS POSSIBLE Powershell Empire Activity /news.php - - 2021-11-20T16:03:25-0500 Ckr2TD12vYGon6LR58 10.0.0.110 1686 10.0.0.107 443 Signatures::Sensitive_Signature powershellEmpire 10.0.0.110: SUSPICIOUS POSSIBLE Powershell Empire Activity /admin/get.php - - 2021-11-20T16:51:53-0500 CI3BQ34gmAYusuQ6Yf 10.0.0.110 1689 10.0.0.107 443 Signatures::Sensitive_Signature powershellEmpire 10.0.0.110: SUSPICIOUS POSSIBLE Powershell Empire Activity /login/process.php - - 2021-11-20T17:12:48-0500 CT0hHi3BBoldu0NY3e 10.0.0.110 1690 10.0.0.107 443 Signatures::Sensitive_Signature ...
Looking at the notice.log we see ...
┌──(root💀securitynik)-[/tmp] └─# zeek-cut -d -m < notice.log | more ts uid id.orig_h id.orig_p id.resp_h id.resp_p fuid file_mime_type file_desc proto note msg sub src dst p n peer_descr actions suppress_for remote_location.country_code remote_location.region remote_location.city remote_location .latitude remote_location.longitude 2021-11-20T13:03:59-0500 CskRYg2jaNXRC0fLy8 10.0.0.110 1650 10.0.0.107 443 - - - tcp Signatures::Sensitive_S ignature 10.0.0.110: SUSPICIOUS POSSIBLE Powershell Empire Activity /news.php 10.0.0.110 10.0.0.107 443 - - Notice: :ACTION_LOG 3600.000000 - - - - - ...
Third, Zeek Scripting
Now that we have used a signature to detect our Powershell Empire activity, let's now get to the more laborious but better way of using Zeek. Specifically it's scripting capabilities.
# Define the modules to use @load base/files/extract @load base/files/hash @load base/frameworks/notice @load base/frameworks/logging @load base/protocols/conn @load base/protocols/http module PSEmpire; export { global hit_count: int = 0; global compromised_psempire_hosts: set[addr] = { } &redef &write_expire=7 days; global empire_file_names: pattern = /news|admin|process/; # Setup our notice capabilities redef enum Notice::Type += { suspicious_psempire_compromised_host }; redef Notice::type_suppression_intervals += { [suspicious_psempire_compromised_host] = 1day }; # Setup our own log redef enum Log::ID += { PSEmpire::LOG }; option msg = "Say what you want to see"; # Define the log the fields for the PSEmpire log records type Info: record { ts: time &log; # Add a timestamp src_host: addr &log; # Add the source IP src_port: port &log; # Add the source port dst_host: addr &log; # Add the destination IP dst_port: port &log; # Add the destination port msg: string &log; # Add the message info }; } # Execute this when zeek starts up event zeek_init() { print "[*] Zeek has started ... "; print "[*] Initializing the log stream ... "; Log::create_stream(LOG, [$columns=Info, $path="PSEmpire"]); print "-----------------------------------------------------"; print "[*] Hunting for POWERSHELL EMPIRE Activity ... "; print "-----------------------------------------------------"; } # Check the HTTP Request event http_request(c: connection, method: string, original_URI: string, unescaped_URI: string, version: string) { # print fmt("[*] HTTP Request structure: METHOD:%s | ORIGINAL_URI:%s | UNESCAPED_URI:%s", method, original_URI, unescaped_URI); if (c$id$orig_h == 10.0.0.110 && c$id$resp_p == 443/tcp && empire_file_names in original_URI) { PSEmpire::hit_count += 1; print fmt("[*] SUSPICIOUS POWERSHELL EMPIRE ACTIVITY %s:%s -> %s:%s", c$id$orig_h, c$id$orig_p, c$id$resp_h, c$id$resp_p); add compromised_psempire_hosts[c$id$orig_h]; # Write information to our log file Log::write(LOG, Info( $ts=network_time(), $src_host = c$id$orig_h, $src_port = c$id$orig_p, $dst_host = c$id$resp_h, $dst_port = c$id$resp_p, $msg = "PowerShell Empire Activity Found!" )); # Raise our notice NOTICE([ $note=suspicious_psempire_compromised_host, $msg = "SUSPICIOUS POWERSHELL EMPIRE ACTIVITY", $sub = "A host is suspected to be infected with Powershell Empire CnC", $conn = c, $suppress_for = 60min, $identifier = cat(c$id$orig_h,c$id$orig_p,c$id$resp_h,c$id$resp_p) ]); } } # Get the file Hashes and extract the files event file_new(f: fa_file) { if (f$source == "HTTP") { for ( cid, cconns in f$conns ) { # print cconns$http$uri; if ( empire_file_names in cconns$http$uri && cid$orig_h in compromised_psempire_hosts && cid$resp_h != 10.0.0.110 ) { local f_name = "EMPTY"; local f_name_array = split_string(cconns$http$uri, /\//); # Split the string by / if (|f_name_array| == 3) f_name = f_name_array[2]; else f_name = f_name_array[1]; local extracted_filename = fmt("%s-%s-%s", f$source, f$id, f_name ); print fmt(" \\-> Extracted file: %s", extracted_filename ); # Get the SHA256 Hashes of the files Files::add_analyzer(f, Files::ANALYZER_SHA256); # Extract the file Files::add_analyzer(f, Files::ANALYZER_EXTRACT, [$extract_filename=extracted_filename]); } } } ` } # Finally some quick code to execute when Zeek ends event zeek_done() { if ( |PSEmpire::hit_count| == 0 ) { print "-----------------------------------------------------"; print "[*] Lucky you! No PowerShell Empire IoCs found!"; return; } print "-----------------------------------------------------"; print fmt("[*] We have %d hits for Powershell related IoCs", PSEmpire::hit_count ); print fmt("[*] There is/are %d unique IP(s) across the %d hits", |compromised_psempire_hosts|, PSEmpire::hit_count); for ( ip in compromised_psempire_hosts ) { print fmt (" \\- %s", ip); } print "[*] Zeek has ended. :-( "; }
When the above code is run, get on the screen.
┌──(securitynik㉿securitynik)-[/tmp] └─$ zeek --readfile /home/securitynik/packets/empire-full-session.pcap --rulefile /home/securitynik/packets/powershell.sig --no-checksums /home/securitynik/packets/psEmpire.zeek [*] Zeek has started ... [*] Initializing the log stream ... ----------------------------------------------------- [*] Hunting for POWERSHELL EMPIRE Activity ... ----------------------------------------------------- [*] SUSPICIOUS POWERSHELL EMPIRE ACTIVITY 10.0.0.110:1650/tcp -> 10.0.0.107:443/tcp \-> Extracted file: HTTP-FJAtZe4W1F6uRwvmM3-news.php [*] SUSPICIOUS POWERSHELL EMPIRE ACTIVITY 10.0.0.110:1650/tcp -> 10.0.0.107:443/tcp \-> Extracted file: HTTP-FbbFCu4T8l288RA3R7-news.php \-> Extracted file: HTTP-FIWqGKoACo24S6dS8-news.php [*] SUSPICIOUS POWERSHELL EMPIRE ACTIVITY 10.0.0.110:1650/tcp -> 10.0.0.107:443/tcp \-> Extracted file: HTTP-F4wiHG1bpwW03v71vh-news.php \-> Extracted file: HTTP-FghBlt1qKU0utYSHd7-news.php ... ----------------------------------------------------- [*] We have 1807 hits for Powershell related IoCs [*] There is/are 1 unique IP(s) across the 1807 hits \- 10.0.0.110 [*] Zeek has ended. :-(
Notice above it says we extracted the files. Let's verify that by using ls on the extract_files folder
┌──(securitynik㉿securitynik)-[/tmp] └─$ ls extract_files/ HTTP-F04ExA4j0mLQtMJfxd-get.php HTTP-Fesf2Y3CmRhWycz7Pf-process.php HTTP-Fp6Yax4DsWlpbttqR6-news.php HTTP-F04Fi9X4phawyMEXl-news.php HTTP-FeshuKrFfyOgA6uH5-process.php HTTP-FPCjffBSpvM3iS48f-process.php HTTP-F05oktdIUrPchmqg-news.php HTTP-FEskgT2JF9cCFwnBcf-get.php HTTP-FpcYVS1xKGD4yv2rCb-get.php HTTP-F05vS52MRDIDb1BIyi-process.php HTTP-Festcg1mUFuQygmVe-get.php HTTP-FPeqrG4Ur8DlkjgYv4-process.php ....
Looks good!
┌──(securitynik㉿securitynik)-[/tmp] └─$ zeek-cut -d ts fuid tx_hosts rx_hosts conn_uids source filename sha256 < files.log | more 2021-11-20T13:03:59-0500 FJAtZe4W1F6uRwvmM3 10.0.0.107 10.0.0.110 CjvLUV2ZfpZGgNfiHj HTTP - 41ab2b2d10eb9fdcf1d667ec4f45db99ec1761c06a4e2d61a789710fb0c08a04 2021-11-20T13:04:02-0500 FbbFCu4T8l288RA3R7 10.0.0.110 10.0.0.107 CjvLUV2ZfpZGgNfiHj HTTP - bf896ec20bad0ee772db7ea44b6bf45c2b7401687808bf8474591f0e1612b01c 2021-11-20T13:04:02-0500 FIWqGKoACo24S6dS8 10.0.0.107 10.0.0.110 CjvLUV2ZfpZGgNfiHj HTTP - 600df9d2f01f3388e2154bfacc557b6add77cddce147b1cdebc30ad188b57edb
Looking at our custom log file PSempire.log.
┌──(securitynik㉿securitynik)-[/tmp] └─$ zeek-cut -d -m < PSEmpire.log | more ts src_host src_port dst_host dst_port msg 2021-11-20T13:03:59-0500 10.0.0.110 1650 10.0.0.107 443 PowerShell Empire Activity Found! 2021-11-20T13:04:02-0500 10.0.0.110 1650 10.0.0.107 443 PowerShell Empire Activity Found! 2021-11-20T13:04:03-0500 10.0.0.110 1650 10.0.0.107 443 PowerShell Empire Activity Found! 2021-11-20T13:05:06-0500 10.0.0.110 1650 10.0.0.107 443 PowerShell Empire Activity Found! 2021-11-20T13:06:06-0500 10.0.0.110 1650 10.0.0.107 443 PowerShell Empire Activity Found! 2021-11-20T13:07:06-0500 10.0.0.110 1650 10.0.0.107 443 PowerShell Empire Activity Found! 2021-11-20T13:08:07-0500 10.0.0.110 1650 10.0.0.107 443 PowerShell Empire Activity Found! 2021-11-20T13:09:07-0500 10.0.0.110 1650 10.0.0.107 443 PowerShell Empire Activity Found!
Well That's it for this post and this series.
Other posts in this series:
Beginning Powershell Empire - The Attack in 10 steps
Powershell Empire Log Analysis
Powershell Empire Packet Analysis
Powershell Empire Detection with Snort
Powershell Empire - Detection with Zeek
References:
No comments:
Post a Comment