Install Elastic Filebeat as daemon on OSX

Filebeat

Filebeat is a lightweight shipper for logs. You can use it to collect logs from endpoints and store them centrally in Elastic. You can use it to collect logs from Linux systems, but it also works on Apple OSX. Installing filebeat is straightforward

curl -L -O https://artifacts.elastic.co/downloads/beats/filebeat/filebeat-7.15.0-darwin-x86_64.tar.gz
tar xzvf filebeat-7.15.0-darwin-x86_64.tar.gz
ln -s filebeat-7.15.0-darwin-x86_64 filebeat

After installing filebeat you have to enable the system module

sudo filebeat/filebeat enable system

Then update the configuration file (filebeat/filebeat.yml)

- type: log
  enabled: true
  paths:
    - /var/log/install.log

setup.ilm.enabled: auto
setup.ilm.rollover_alias: "filebeat-cudeso"
setup.ilm.pattern: "{now}-000001"
setup.ilm.policy_name: "filebeat-cudeso"

output.elasticsearch:
  hosts: ["elastic:9200"]

Daemon

I used the information from a support post on the Elastic site: https://discuss.elastic.co/t/deploying-filebeat-on-macos-x/37785/11. This post describes what needs to be in the plist and how to add it to launchd.

Create the file /Library/LaunchDaemons/co.elastic.filebeat.plist

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>Label</key>
    <string>co.elastic.filebeat</string>
    <key>ProgramArguments</key>
    <array>
        <string>/Users/john/Scripts/filebeat/filebeat</string>
        <string>-c</string>
        <string>/Users/john/Scripts/filebeat/filebeat.yml</string>
	<string>--strict.perms=false</string>
    </array>
    <key>KeepAlive</key>
    <true/>
</dict>
</plist>

Register the daemon

launchctl load /Library/LaunchDaemons/co.elastic.filebeat.plist 

Then verify the daemon is running and check the filebeat logs.

Use Mobile Verification Tool to check if your iPhone is affected by Pegasus spyware

Pegasus

The Pegasus spyware made by the Israel-based company NSO Group has been used in targeted surveillance attacks against activists, journalists and businesspeople. Its details, and methods to detect it, were revealed by CitizenLab (hacks from the Bahraini government on activists) with a forensic methodology report made available by Amnesty International.

Because both the tools and the indicators of compromise are made available it’s fairly easy to do these checks yourself.

Environment

Setup a Python virtual environment with python 3 and activate the virtual environment.

virtualenv mvt
source mvt/bin/activate

Install the Mobile Verification Toolkit in that virtual environment.

pip3 install mvt

Get a backup from your iPhone. If you do backups with an Apple computer (OSX) you can find them at

/Users/John/Library/Application Support/MobileSync/Backup

Copy the latest backup to the Linux environment where you installed mvt. Note that you can install mvt also on your OSX but I prefer to use it in a Linux system.

You then have to decrypt the backup.

mvt-ios decrypt-backup -p -d /path/to/decrypted_iphone /path/to/backup

Make sure that /path/to/backup is local to the system where you run mvt. I was unable to execute it when the iPhone backup was mounted via a remote file system.

Fetch the latest indicators from https://github.com/AmnestyTech/investigations/tree/master/2021-07-18_nso. These are in STIX2 format, you need the file pegasus.stix2.

Then verify your decrypted backup.

mvt-ios check-backup -i pegasus.stix2 -o /path/to/output /path/to/decrypted_iphone

It’s not abnormal to see a list of warnings or errors.

In the output folder you’ll find a bunch of JSON files. Check if any of these files contain a suffix _detected.

Delete the decrypted backup from your system, as well as the output files.

Identify malicious servers / Cobalt Strike servers with JARM

For a new assignment I wanted to use JARM to group servers with a similar configuration. Why JARM? Because it’s an easy way to quickly identify and group servers based on their configuration.

What is JARM?

JARM is an active fingerprinting of TLS servers made available by Salesforce Engineering. It sends 10 TLS Client Hello packets to a server and captures specific attributes of the responses. These responses are then aggregated and hashed. A JARM fingerprint consists of 62 characters where:

  • The first 30 characters cover the ciphers and TLS version chosen by the server;
  • The remaining 32 characters are based on the extensions sent by the server.

Important to realise is that JARM is not an identification if a server is malicious or not, it is just an identification is a server has a configuration corresponding with a specific fingerprint. And although it’s not an identification if a server is malicious, because C2 servers for a specific campaign are often configured similarly, this provides a fingerprint to highlight the malicious servers once you have identified the configuration of one such server.

You can create a JARM fingerprint yourself with the code at Github. But instead of then doing a large network scan and checking the fingerprint of the servers, you can use Shodan. Shodan allows you to query for all the servers with a specific JARM fingerprint with the query

ssl.jarm:<JARM>

The advantage of using Shodan is obvious. Although JARM is an active fingerprinting tool, by using it via Shodan you can fingerprint the servers passively. You create a JARM fingerprint from one server and then use Shodan to identify which other servers are out there, without sending packets to these servers.

Cobalt Strike servers

Cobalt Strike servers are typically not the type of servers you want to see in your network range.

As an example for identifying servers based on a configuration fingerprint, you can use one of the known JARM fingerprints of Cobalt Strike servers, such as those from Michael Koczwara.

Python script

I wrote a short Python script that takes a list of JARM fingerprints, queries Shodan for these fingerprints and then dumps the hostname and the CN field that was found in the SSL certificate.

In this script

  • Add your Shodan API key in SHODAN_API_KEY;
  • Store the JARM fingerprints in the file jarm.txt;
  • The detailed output of the query is stored in results.txt;
  • The important details of the query, meaning JARM fingerprint, IP and hostname are stored in output.txt.

import shodan
import sys
import json

SHODAN_API_KEY = ""
country="be"
network=""
hits = 0
api = shodan.Shodan(SHODAN_API_KEY)

try:
    f = open('jarm.txt','r')
    f_result = open('results.txt','w')
    f_output = open('output.txt', 'w')
    f_result.close()
    f_output.close()

    f_result = open('results.txt','a')
    f_output = open('output.txt', 'a')

    for jarm in f:
        jarm_strip = jarm.strip()
        results = api.search("ssl.jarm:{} country:{}".format(jarm_strip, country))
        #results = api.search("ssl.jarm:{} country:{} network:{}".format(jarm_strip, country, network))
        f_result.write(json.dumps(results))
        f_result.write("\n")
        for result in results['matches']:
            hits += 1
            print("{} {} {} {} {}".format(jarm_strip, result['ip_str'], result['isp'], result['hostnames'], result['ssl']['cert']['subject']))
            f_output.write("{} {} {} {} {}\n".format(jarm_strip, result['ip_str'], result['isp'], result['hostnames'], result['ssl']['cert']['subject']))

    print("Hits: {}".format(hits))

    f_result.close()
    f_output.close()
except Exception as e:
    raise e

False positives with JARM

A JARM fingerprint is only a fingerprint indicating a server has a similar configuration. As such, it’s only a first identification for triaging if a server should be further investigated. When I was compiling a list of JARM fingerprints for Cobalt Strike servers I noticed that some of the highlighted servers are related to Zimbra Collaboration servers.

Out of the 90 servers detected with JARM fingerprint 05d02d20d21d20d05c05d02d05d20dd7fc4c7c6ef19b77a4ca0787979cdc13, 24 turned out to be related to a Zimbra Collaboration Server.

(shodan) koenv@ir:~/shodan$ cat output.txt |grep -i zimbra | cut -d ' ' -f 1 | uniq -c
     24 05d02d20d21d20d05c05d02d05d20dd7fc4c7c6ef19b77a4ca0787979cdc13
(shodan) koenv@ir:~/shodan$ cat output.txt | grep 05d02d20d21d20d05c05d02d05d20dd7fc4c7c6ef19b77a4ca0787979cdc13 | wc -l
90

Conclusion

Once you have identified/created a suitable JARM fingerprint, Shodan is an excellent resource to locate additional servers corresponding to your fingerprint, without having to touch any of these servers.

Cobalt Strike Hunting – Key items to look for

Cobalt Strike

Cobalt Strike (S0154) is a commercial penetration testing platform which is used by many red teams and, unfortunately, also by many criminal threat actors. In this post I summarise the findings from a SANS Digital Forensics and Incident Response keynote by Chad Tilbury : Cobalt Strike Threat Hunting. The YouTube video provides much more details but below you can find those findings that were relevant for me during an IR case.

This post includes references to commands of the Volatility memory forensics platform but you can use any platform of your choice. Some of the logs sources that are valuable during IR such as network traffic (detect beaconing, traffic frequency), PowerShell logs and Sysmon logs were unfortunately not available.

Hunting

Process tree

Look at running processes and the process tree via pstree. Two anomalies to look for are:

  • PowerShell spawning itself;
  • PowerShell spawning (multiple) rundll32.exe processes.

Rundll32.exe is spawned as a sacrificial process in which Cobalt Strike injects its malicious code. By default, Cobalt Strike will spawn rundll32.exe. This is customisable and can as well be svchost.exe or any other process. The take-away is to look for multiple, similar, processes all spawned from PowerShell.

Command line arguments rundll32

The next item to look for are the command line arguments that are used for starting rundll32.exe from PowerShell.

If you notice a rundll32.exe process spawned from PowerShell without any DLL supplied as an argument then there is a high chance that something is wrong. By default Cobalt Strike spawns rundll32.exe without arguments but this is also customisable. If you observe multiple rundll32.exe processes (or for example svchost.exe) spawned with the same command line parameters or all without command line arguments then there’s a high chance it’s Cobalt Strike.

32-bit version PowerShell

Review if a 32-bit version of PowerShell was used to launch the subprocesses. You can detect this by looking at the startup path (via cmdline) of the command. If it includes syswow64 (take note of different forms of capitalisation) then it’s using the 32-bit version.

Execute and Write memory pages

You can find injected code with malfind which allows you to find ‘hidden’ or injected DLLs in user memory. Pay special attention to the memory pages that are marked both as executable and writable. Don’t be confused if the first page only contains zeros. Cobalt Strike can will not inject its code in the first page but you’ll be able to find the injected code after the set of zeros.

Named pipes

The default configuration of Cobalt Strike uses named pipes for the communication between different processes:

  • \\.\pipe\MSSE-###-server (default Artifact Kit binaries)
  • \\.\pipe\status_## (payload staging for lateral movement)
  • \\[target]\pipe\msagent_## (SMB beacon)
  • \\.\pipe\postex_ssh_#### (SSH sessions)
  • \\.\pipe\postex_#### (post exploitation)
  • \\.\pipe\####### (7-10 #s) (post exploitation before version 4.2)

The configuration of these named pipes can be changed, but in a lot of cases attackers will just stick to the default settings.

In Volatility you can list the named pipes via the handles command and specify the process PID. Note that the list of named pipes can provide you hints of other machines most likely also affected by the intrusion (for example if the named pipe has references to machine names/IPs for post exploitation or SMB beacons).

For an initial triage and to reduce the volume of results it makes sense to look only

  • at processes which normally would not use named pipes;
  • for 32-bit processes (syswow32) using named pipes;
  • or for named pipes with a seaming-less random name.

As an additional note, the number of characters of the name of the named pipe is a giveaway for what command is being issued. For example mimikatz (8 chars) will have a named pipe name of 8 characters long. Also see the blog of F-Secure Detecting Cobalt Strike Default Modules via Named Pipe Analysis and a list of malleable profiles (including the different pipe names) from Michael Haag.

A last remark on named pipes. Because Cobalt Strike uses named pipes to deliver shellcode you should make sure your sandbox emulates named pipes as otherwise Cobalt Strike might not find its shellcode. See also the blog from NVISO Anatomy of Cobalt Strike’s DLL Stager.

PowerShell logging

PowerShell logging is essential to further identify Cobalt Strike activity. Because so many attacks make use of PowerShell in one way or another (not only for Cobalt Strike), it is a treasure cave for doing incident response. Unfortunately PowerShell auditing (logging) is not always enabled in corporate environments (as was the case for the IR for which I did this post).

If you do have PowerShell logging enabled, then look for

  • DownloadString / IEX downloads for localhost (http://127.0.0.1);
  • Large blocks of encoded PowerShell (FromBase64String, EncodedCommand);
  • System.Reflection or ReflectedDelegate.

Crowdstrike has an overview of PowerShell log observations of Cobalt Strike commands: Getting the Bacon from the Beacon.

Extract Cobalt Strike configuration

If you have a memory dump of a system running Cobalt Strike then you can use the 1768.py tool from Didier Stevens to decrypt and dump the configuration file of the Cobalt Strike beacon. The tool will scan the memory dump, look for the XOR key and then decrypt and dump the configuration of the beacon. You can dump the configuration in tabular output, CSV or JSON.


Image from https://blog.didierstevens.com/2020/11/07/1768-k/

The dump of the configuration file gives you immediate access to the named pipes, the C2 server (name, port and URI) and the HTTP user agent used for beaconing. All this information is something you can use to further identify compromised devices.

Sigma rules

There are a couple of Sigma rules that you can use to detect Cobalt Strike :

Summary

PowerShell spawning itself
PowerShell spawning (multiple) rundll32.exe processes
Multiple rundll32.exe with no command line arguments or all with the same command line arguments
32-bit version of PowerShell started (via syswow64)
Memory set as EXECUTE and WRITE
List of default Cobalt Strike named pipes
PowerShell downloads for http:/127.0.0.1
Encoded PowerShell

Update 20210914

These resources provide additional useful information for detecting and fighting Cobalt Strike:

  • Cobalt Strike, a Defender’s Guide: A very extensive guide with information on which Event IDs to look for during the different stages, such as Execution, Defense Evasion, Discovery, Privilege Escalation, …
  • Awesome-CobaltStrike-Defence: A Github repository with pointers to hunting and detection tools, Yara and Sigma rules and indicators of compromise.