Unraveling ‘Ocean Strike’ – a crypto-mining threat

While doing some pivoting on emerging Command and Control servers, I identified an open-directory on an IP Address (144.31.106[.]169) hosting what appeared to be a collection of malicious shell scripts, as shown below.

Initial Open Directory Contents
Contents of ‘stagers’ directory

Peering into the first shell script, ‘mimicry.sh’, its purpose appears to be serving as a staging script for the actual payload – ocean_strike_v5.tar.gz – curling the payload into /tmp/.sys-task, extracting it, starting a secondary shell script, then deleting the previously created directory.  Let’s take a look inside ocean_strike_v5.tar.gz.

mimicry.sh
ocean_strike_v5 contents

The script of interest, ‘predator_v5_mimicry.sh’, is a shell script designed to perform the following actions:

  • Checks if it is running insider a container, sandbox, WSL, or other virtualization environment using /proc/1/cgroup
  • Adds an SSH key to /root/.ssh/authorized_keys to maintain persistent access to the compromised device
  • Attempts to set vm.nr_hugepages=1280 – increasing performance of certain workloads, including crypto-mining
  • Attempts to identify any other mining-related processes and kill them, including anything that mentions guard, miner, YDServi, xmrig, nanominer, kdevtmpfsi, gost, pcpcat, or proxy.sh
  • Creates directory /dev/shm/.sys-cache and copies some of the above files into it – sys-compute is copied as kworker and made executable.
    • A service is also installed named ‘System Monitoring Service’ to ensure it is always running.
  • The malware then configures what it refers to as a Mimicry Proxy via the binary sys-net-d seen above – this is installed into /usr/bin/sys-net-d with a service setup named ‘Network Monitoring Service’.
  • The malware then attempts to build something it refers to as a ‘Mimicry Aquarium’ using the contained docker file, spawning a container named ‘sys-aquarium-mimicry’ and mapping host port 2376 to container port 2375.
    • The container appears intended to serve as a distraction for other attackers – it installs some common Linux utilities such as curl, wget, etc, creates fake artifacts like /root/proxy.sh, creates a cron job without actually enabling it to run proxy.sh every 30 minutes, then creates an empty file at /var/run/docker.sock
    • The container itself is used to return a general HTTP 200 Status in response to any request to port 2375, the Docker Remote API.
  • The attacker then uses iptables to redirect any inbound request destined for port 2375 to port 2376 – effectively capturing traffic destined for the Docker Remote API into the container, which then supplies a ‘fake’ response.
    • This is done seemingly to prevent any additional tampering or exploitation from occurring that may interfere with their mining services.
    • Their own IP address is excluded from this port mapping rule via iptables
  • Finally, the attacker writes to a file called /dev/shm/.sys-cache/watchdog.sh a bash script that periodically wakes and checks 144.31.106[.]169 for a 200 OK response – if it is not received, it clears all iptables pre-routing and restarts the main docker service.
    • This is to ensure access to the true Docker API port is available remotely again for a potential re-compromise

In total, the malware attempts to find and kill competitors, deploys persistence via system services, runs a Docker container to proxy traffic to the Remote Docker API port, deploys a crypto-miner onto the device, and implements a dead mans switch designed to clear the routing trap should the main IP address fail to respond.

Dead Mans Switch in predator_v5_mimicry.sh

After some research, I reached the following conclusions:

  • sys-net-d appears to be a Sliver agent payload (tracks with Sliver C2 exposed on the IP address already).
  • sys-compute is an XMRig mining binary, avoiding the overhead of having to download it dynamically on victim systems.

While digging into other directories, I found multiple older versions of their persistence and deployment scripts along with potential targets in the ‘ocean_strike.tar.gz’ file, as shown below:

Contents of ocean_strike.tar.gz

The observed sys-net-d and sys-compute are the same Sliver payload and XMRig binary previously noted.  There are variations of persistence scripts present, but all ‘deployer’ scripts are 0-byte files as of the time of this analysis.  Sys-guard is a simple shell script that configures ocean-miner along with a decoy Docker container, another variation of Monero mining.

What’s more interesting is the files labelled ‘fortress_results’ – these appear to be scans of potential victim IP addresses – each victim had an exposed Remote Docker API port that the threat actor tested to determine the level of access they could achieve remotely, attempting to create a privileged container to facilitate mining operations.

fortress_results_v2 demonstrating attempts to create privileged containers on exposed Docker instances across the Internet on a variety of victim networks

Revisiting one of the earlier files, we identified another Monero miner staging script named ‘cuckoo_stager.sh’.  This is similar to the ‘Ocean Strike’ payload but had slightly different checks for containers, competitors, deployment, persistence, and fail-over operations.  A snippet is shown below.

cuckoo_stager.sh snippet

In essence, this script attempted to:

  • Identify if it’s in a container via the presence of ‘docker’ in /proc/1/cgroup and also checking for the presence of systemd
  • Kill other competing mining processes
  • Download xmrig and Yggdrasil payloads binaries from the main control address (144.31.106[.]169)
  • Configure and launch xmrig for Monero mining
  • Persist itself via cron jobs
  • Hide initial access by deleting bash history and timestomping xmrig binary and configuration files to appear older than they are
  • Delete the initial stager (/tmp/cuckoo_stager.sh)
  • Then, optionally, starting Yggdrasil mesh network in an attempt to obfuscate traffic destinations and avoid initial detection at the network level

There was a final script named ‘dead_man_switch.sh’ that appears similar in nature to the fallback utilized in the original ‘Ocean Strike’ script in that it periodically checks if a specific heartbeat is being populated – if no heartbeat is detected after a 30 minute period, it assumes there is a catastrophic error with the script and attempts to re-expose the original Docker API ports instead of the mimicked port so that remote access can once again be gained by the actor.

dead_man_switch.sh snippet

Conclusions

This is an interesting malware variant that is explicitly targeting the Remote Docker API in an attempt to create high-privileged containers on exposed ports that are then leveraged to instantiate Monero-mining operations – this is not a new technique, but it is still interesting from a defensive perspective to study the source and implementation so that we can further harden relevant systems.

Once the malware is deployed, the Docker API port (2375) is then proxied to 2376 via a ‘decoy’ container and iptables, likely to ensure that other actors cannot launch competing resources – but in such a way that the C2 IP address can still reach the original port without interference.

Monero mining attacks are not new and neither is inhibiting further use of the Remote Docker API (https://www.akamai.com/blog/security-research/new-malware-targeting-docker-apis-akamai-hunt) – but the naming conventions and strategies used here seem to indicate this may be part of a more organized campaign.

Be on the lookout for the below indicators in your network.

Indicators of Compromise

  • 144.31.106[.]169
    • C2 Address
  • 45g4uGaXz3PeaN94ns6CwXDa2oxNFKrYPAEjqZSjhem2RQju9JtuQgbSLrvq9pN35dUeUgS8996G7P9eZ9bX78m4CjwGeP3
    • Monero Wallet
  • cuckoo_stager.sh
    • Payload Variation deploying mining, persistence, etc
  • mimicry.sh
    • Shell Script serving as Initial Access payload
    • SHA256: 148b56141e04ff98c408c50320eb76398854f0a071c3276779b8d0db99332157
  • predator_v5_mimicry.sh
    • SHA256: dba481116b7c451bf018518c02431b3c342c92c047d77ceaacedcf6a44e376c4
  • dead_man_switch.sh
    • SHA256: 31a4271fc2c4ab2a7939358f98e7cb339115ede6180a500f7a441b468e09a8d6
  • persist.sh
    • SHA256: 5e37c6b3bf5192e334d6ff0c5995f73f60b27b18cbdcedcbcc579d32aeea2e7c
  • persist_v4_proxy.sh
    • SHA256: 8ceebbd3112d462ff6aa00b6e5330b75d29c872ee8dee86bbba968c88fea857a
  • predator_v3_ocean.sh
    • SHA256: a07a12709f8fef2b4667fcbd8947f670f2bfbf92751553184fb3ed8f93679537
  • sys-compute
    • XMRig Binary
    • SHA256: 92dcc363ed05c5e4ae9008f7d0d41b1ad1ae9caead9d4f3598c566b185078b4b
  • sys-net-d
    • Sliver Payload
    • SHA256: 448fbd7b3389fe2aa421de224d065cea7064de0869a036610e5363c931df5b7c
  • sys-guard
    • Shell Script configuring ocean-miner
    • SHA256: 3aad38a1792dfeede3cb47181bd3570047e3c71b659ebdf811fc5e2bae0bc9d5
  • /dev/shm/.sys/, /dev/shm/.sys-cache
    • Directories created to host payloads/binaries/configs

Responding to Active Threats in Low-Maturity Environments

As a DFIR professional, multiple times I’ve been in the position of having to assist a low-maturity client with containment and remediation of an advanced adversary – think full domain compromise, ransomware events, multiple C2 servers/persistency mechanisms, etc. In many environments you may have some or none of the centralized logging required to effectively track and identify adversary actions such as Windows Server and Domain Controller Event Logging, Firewall Syslog, VPN authentications, EDR/Host-based data, etc. These types of log items are sadly a low-priority objective in many organizations who have not experienced a critical security incident – and while useful to emergency on-board in an active threat scenario, the vast majority of prior threat actor steps will be lost to the void.

So, how can you identify and hunt active threats in a low-maturity environments with little-to-no centralized visibility? I will walk through a standard domain compromise response scenario and describe some useful techniques I tend to rely on for hunting in these types of networks.

During multiple recent investigations I’ve worked to assist clients in events where a bad actor has managed to capture Domain Admin credentials as well as get and maintain access on Domain Controllers through one or more persistency mechanisms – custom C2 channels, SOCKS proxies, remote control software such as VNC/Screen Connect/TeamViewer, etc. There is a certain order of operations to consider when approaching these scenarios – you could of course start by just resetting Domain Admin passwords but if there is still software running on compromised devices as these users then it won’t really impact the threat actors operations – the initial goal should be to damage their C2 operations as much as possible – hunt and disrupt.

  • Hunt – Threat Hunting activities using known IOCS/TTPs, environment anomaly analysis, statistical trends or outliers, suspicious activity, vulnerability monitoring, etc.
  • Disrupt – Upon detecting a true-positive incident, working towards breaking up attacker control of compromised hosts – blocking IP/URL C2 addresses, killing processes, disabling users, etc.
  • Contain – Work towards limiting additional threat actor impact in the environment – disabling portions of the network, remote access mechanisms such as VPN, mass password resets, etc.
  • Destroy – Eradicating threat actor persistence mechanisms – typically I would recommend to reimage/rebuild any known-compromised device but this can also include software/RAT removal, malicious user deletions, un-doing AD/GPO configuration changes, etc.
  • Restore – Working towards ‘business as usual’ IT operations – this may include rebuilding servers/applications, restoring from backups (you are doing environment-wide backups, right?) and other health-related activities
  • Monitor – Monitor the right data in your environment to ensure you can catch threat actors earlier in the cyber kill chain the next time a breach occurs – and restart the hunting cycle.

The cycle above represents a methodology useful not only in responding to active incidents but for use in general cyber-security operations as a means to find and respond to unknown threats operating within your network environment. When responding to a known incident, we are typically in either the hunt or disrupt phase depending on what we know with respect to Indicators of Compromise (IOCs). Your SOC team is typically alerted to a Domain Compromise event through an alert – this may be a UBA, EDR, AV, IDS or some other alert – what’s important is the host generating the event. Your investigation will typically start on that host where it is critically important to capture as much data as possible as your current goal is identify Tactics, Techniques, Procedures and IOCs associated with your current threat for use in identifying additional compromised machines. Some of the data I use for this initial goal is described below;

  • Windows Event Logs – Use these to identify lateral movement (RDP/SMB activity, Remote Authentications, etc), service installs, scheduled task deployments, application installations, PowerShell operations, BITS transfers, etc – hopefully you can find some activity for use in additional hunting here.
  • Running Processes/Command-Lines
  • Network Connections
  • Prefetch – Any interesting executable names/hashes?
  • Autoruns – Any recently modified items stand out?
  • Jump Lists/AmCache – References to interesting file names or remote hosts?
  • USN Journal – Any interesting file names? (The amount of times I’ve found evidence of offensive utilities without renaming in here is astounding)
  • NTUSER.DAT – always a source of interesting data if investigating a specific user account.
  • Local Users/Cached Logon Data
  • Internet History – Perhaps the threat actor pulled well-known utilities directly from GitHub?

These are just some starting points I often focus on when performing an investigation and this is by no means a comprehensive list of forensic evidence available on Windows assets. Hopefully in your initial investigation you can identify one or more of the IOC types listed below;

  • Hostnames / IP Addresses / Domains
  • File Names / Hashes
  • Service or Scheduled Task Names / Binaries / Descriptions / etc
  • Compromised Usernames

The next step is to hunt – we have some basic information to use which can help us rapidly understand whether or not a host is displaying signs of compromise and now we need to check these IOCs against additional hosts in the environment to determine scope and scale. Of course you can and should simultaneously pull on any exposed threads – for example, if you determined that Host A is compromised and also observed RDP connections from Host B to Host A that appear suspicious, you should perform the same type of IOC/TTP discovery on Host B to gather additional useful information – this type of attack tracing can often lead to ‘patient zero’ – the initial source of the compromise.

Back to the opening of this post – how can you perform environment hunting in a low-maturity environment that may lack the centralized logging or deployed agents necessary to support that type of activity? In a high-maturity space, you would have access to data such as firewall events, Windows event logs from hosts/servers/DCs, Proxy/DNS data, etc that would support this type of operation – if you don’t, you’re going to have to make do with what’s already available on endpoints – my personal preference is a reliance on PowerShell and Windows Management Instrumentation (WMI).

WMI exposes for remote querying a wealth of information that can make hunting down known-threats in any type of environment significantly easier – there are hundreds of classes available exposing data such as the following;

  • Running Processes
  • Network Connections
  • Installed Software
  • Installed Services
  • Scheduled Tasks
  • System Information
  • and more…

PowerShell and WMI can be a threat hunters best friend if used appropriately due to how easy it can be to rapidly query even a large enterprise environment. In addition to WMI, as long as you are part of the Event Log Readers group on a local device, you’ll have remote access to Windows Event logs – querying these through PowerShell is also useful when looking for specific indicators of compromise in logs such as User Authentications, RDP Logons, SMB Authentications, Service Installs and more – this will be discussed in a separate post.

As a start, lets imagine we identified a malicious IP address being used for C2 activities on the initially compromised host – our current objective is now to identify any other hosts on our network with active connections to the C2 address. Lets break this problem down step-by-step – our first goal is to identify all domain computers – we can do this by querying Active Directory and searching for all enabled Computer accounts through either Get-ADUser or, if you don’t have the AD module installed, some code such as shown below using DirectorySearcher.

$domainSearcher = New-Object DirectoryServices.DirectorySearcher([ADSI]"")
$domainSearcher.Filter = "(&(objectClass=computer)(!userAccountControl:1.2.840.113556.1.4.803:=2))"
$domainSearcher.PageSize=100000
$domainComputers = ($domainSearcher.Findall())

The code above is used for querying all ‘enabled’ computer accounts in Active Directory using an LDAP filter – read more about LDAP bit-filters here https://ldapwiki.com/wiki/Filtering%20for%20Bit%20Fields. Once we have identified these accounts, we can then iterate through the list in a basic for-loop and run our desired query against each machine – shown below.

$domainSearcher = New-Object DirectoryServices.DirectorySearcher([ADSI]"")
$domainSearcher.Filter = "(&(objectClass=computer)(!userAccountControl:1.2.840.113556.1.4.803:=2))"
$domainSearcher.PageSize=100000
$domainComputers = ($domainSearcher.Findall())
$domainComputers.Properties.dnshostname | ForEach {
    $ComputerName = $_
    $NetworkConnections = Get-WmiObject -Namespace ROOT\StandardCIMV2 -Class MSFT_NetTCPConnection -ComputerName $ComputerName -ErrorAction SilentlyContinue | Select-Object LocalAddress,LocalPort,RemoteAddress,RemotePort,OwningProcess,PSComputerName,State
    $NetworkConnections | Export-CSV -NoTypeInformation -Path '.\DomainNetworkConnections.csv' -Append
}

..and that’s it – you now have a PowerShell script that can be used to query all enabled domain computers via WMI remotely (provided you have the appropriate permissions) and retrieve network TCP connections. Granted, if the C2 channel is over UDP this won’t help you but that’s typically not the case (looking at you stateless malware..). Of course, this is a pretty basic script – how can we spruce it up? Well for starters, we could of course add a progress bar and some log information so we know it’s actually doing something – easy enough.

$domainSearcher = New-Object DirectoryServices.DirectorySearcher([ADSI]"")
$domainSearcher.Filter = "(&(objectClass=computer)(!userAccountControl:1.2.840.113556.1.4.803:=2))"
$domainSearcher.PageSize=100000
$domainComputers = ($domainSearcher.Findall())
$ComputerCount = $domainComputers.Count
$CurrentCount = 0
$domainComputers.Properties.dnshostname | ForEach {
    $CurrentCount += 1
    Write-Progress -Activity "Querying Computers.." -Status "Progress:" -PercentComplete ($CurrentCount/$ComputerCount*100)
    $ComputerName = $_
    Write-Host "Checking $ComputerName"
    $NetworkConnections = Get-WmiObject -Namespace ROOT\StandardCIMV2 -Class MSFT_NetTCPConnection -ComputerName $ComputerName -ErrorAction SilentlyContinue | Select-Object LocalAddress,LocalPort,RemoteAddress,RemotePort,OwningProcess,PSComputerName,State
    $NetworkConnections | Export-CSV -NoTypeInformation -Path '.\DomainNetworkConnections.csv' -Append
}

Looking better – now we have a progress bar letting us know how much is left as well as some communication to the end user describing the current computer being queried. This script, as is, will work – but now the real question is how long will it take? As it stands, this is a single-threaded operation – if your organization has any significant number of servers to query, this can end up taking a very long time. How can we improve this? Multi-threading, of course, is the obvious solution here – lets take advantage of our modern CPUs and perform multiple queries simultaneously in order to expedite this process.

$domainSearcher = New-Object DirectoryServices.DirectorySearcher([ADSI]"")
$domainSearcher.Filter = "(&(objectClass=computer)(!userAccountControl:1.2.840.113556.1.4.803:=2))"
$domainSearcher.PageSize=100000
$domainComputers = ($domainSearcher.Findall())
$ComputerCount = $domainComputers.Count
$CurrentDir = Get-Location
$Config= [hashtable]::Synchronized(@{})
$Config.Path = "$CurrentDir\connections.csv"
$Config.FinishedCount = 0
$ScriptBlock = {
    param($Computer, $Config)
    $cons = Get-WmiObject -Namespace ROOT\StandardCIMV2 -Class MSFT_NetTCPConnection -ComputerName $Computer -ErrorAction SilentlyContinue | Select-Object LocalAddress,RemoteAddress,LocalPort,RemotePort,OwningProcess,PSComputerName,State,PrimaryStatus
    $cons | Export-CSV -NoTypeInformation -Path $Configuration.Path -Append
    $Config.FinishedCount ++
}
$SessionState = [System.Management.Automation.Runspaces.InitialSessionState]::CreateDefault()
$RunspacePool = [RunspaceFactory]::CreateRunspacePool(1, 20, $SessionState, $Host)
$RunspacePool.Open()
$Jobs = New-Object System.Collections.ArrayList
$domainComputers.Properties.dnshostname | ForEach {
    $PowerShell = [powershell]::Create()
	$PowerShell.RunspacePool = $RunspacePool
    $Computer = $_
    $PowerShell.AddScript($ScriptBlock).AddArgument($Computer).AddArgument($Config) | Out-Null
    $Job = New-Object -TypeName PSObject -Property @{
        Runspace = $PowerShell.BeginInvoke()
        Powershell = $PowerShell
    }
    $Jobs.Add($Job) | Out-Null
}

while ($Jobs.Runspace.IsCompleted -contains $false) {
    $x = $Config.FinishedCount
    Write-Progress -Activity "Still Querying: " -Status "Progress:" -PercentComplete ($x/$ComputerCount*100)
    Write-Host (Get-date).Tostring() "Still Querying...[$x/$ComputerCount]"
	Start-Sleep 5
}

Awesome – we now have a multi-threaded script capable of querying remote computers asynchronously through WMI and storing the results of each query in a single CSV file. I’m not going to spend too much time discussing how or why the different components of the above script work – if you’d like to learn more about Runspace Pools and their use in PowerShell scripts, I recommend checking out these links:

Hopefully the usefulness of the code above makes sense from an incident response perspective when you have an IP address you need to use to find additional compromised devices – but what if the attacker is using an unknown IP? As previously mentioned, WMI’s usefulness extends far beyond just gathering TCP connections – we can add to the above script block to gather running processes, installed services, configured scheduled tasks, installed applications and many other pieces of useful information that can serve you well from a hunting and response perspective.

In fact, I’ve found this type of utility so useful that I went ahead and developed it into a more robust piece of software able to accept arguments for data that should be collected, maximum threads to run with, the ability to accept a list of computers rather than always querying AD and the ability to only export results if the result contains one or more specified IOCs provided to the program – Windows Management Instrumentation Hunter (WMIH) is here to help.

GitHub: github.com/joeavanzato/wmihunter

WMIHunter can be used as both a command-line or GUI-based program and enabled asynchronous collection of data via WMI from remote hosts based on enabled Computer Accounts in Active Directory or computers specified in a .txt file supplied to the program. Users can modify the max threads used as well as specify which data sources to target – some can take significantly longer than others. Very soon I’ll be adding the ability to filter IOCs such as IP Addresses, Process Names, Service Names, etc in order to limit what evidence is retrieved.

Give it a whirl in your next investigation.