Detecting script-based attacks on Linux
Last month, we announced the extension of Azure Security Center’s detection for Linux. This post aims to demonstrate how existing Windows detections often have Linux analogs. A specific example of this is the encoding or obfuscation of command-lines. Some of the reasons an attacker might wish to encode their commands include minimizing quoting/escaping issues when encapsulating commands in scripts and a basic means of hiding from host-based intrusion detection. These techniques have the additional benefit of avoiding the need to drop a file to disk, reducing the risk to an attacker of being detected by traditional anti-virus products.
Encoded PowerShell attacks on Windows
There are many examples of such behavior being used in attacks against Windows environments. A previous blog post highlights one such technique to encode PowerShell commands as base64. PowerShell actually makes this amazingly easy, allowing commands of the form:
powershell.exe -EncodedCommand dwByAGkAdABlAC0AbwB1AHQAcAB1AHQAIABFAG4AYwBvAGQAZQBkACAAUABvAHcAZQByAFMAaABlAGwAbAAgAHMAYwByAGkAcAB0AA==
The only real stumbling block being the requirement that the decoded command must be UTF-16 (hence the prevalence of ‘A’ in the resulting base64).
Encoded shell attacks on Linux
As with attacks on Windows systems, the same motivations exist for encoding commands on Linux systems. Namely, to avoid encapsulation issues with special characters and to evade any naïve anti-virus or log analysis.
Whilst Linux has no native equivalent to PowerShell’s -EncodedCommand parameter, it almost always comes packaged with commands such as base64 from the coreutils package (the package that provides critical commands such as cp, ls, mv, rm, etc.). On embedded systems, these same utilities are often provided through inclusion of BusyBox or similar utilities. This makes executing an encoded command as simple as redirecting the output of such a call into your shell of choice. A simple Bash example might look like:
echo ZWNobyAiT2JmdXNjYXRlZCBiYXNoIHNjcmlwdCI= | base64 -d | bash
Worth noting at this point is that on Linux especially, base64 is not the only game in town, arguably equally likely is the ubiquitous hexadecimal encoding:
echo 6563686f20224f626675736361746564206261736820736372697074220a | xxd -r -p | bash
or the even more verbose backslash escaped octal or hex:
printf '145143150157404211714214616516314314116414514440142141163150401631431621511601644212' | bash
Encoded script attacks on Linux
In addition to these dedicated decoders, most scripting languages such as Python / Perl / Ruby come with their own built-in base64 libraries, making obfuscation of such scripts extremely easy. A simple Perl example might look something like:
The one minor downside to this technique is the general requirement to import the relevant library first (in this case, I have omitted the required “use MIME::Base64” statement).
Real-world attacks observed on Linux hosts
The following example demonstrates real-world usage of this approach in Python. In this event, the behavior was observed following a successful brute-force of an SSH password.
This activity was picked up by several different analytics in Azure Security Center, starting with the successful brute-force. The following screenshot shows the detection of suspicious encoded Python of the type described in the previous section.
As you can see, the urlopen and further base64decode / exec calls downloads yet more base64 encoded Python and executes it. This behavior, beneath the outer encoding, is what the highlighted alert has detected. The script to be downloaded being the main controller code for this entirely Python botnet.
Subsequent analysis of the controller script (including configuration that it receives through further urlopen calls), appears to suggest the primary goal of this botnet is the downloading and execution of a Monero crypto-currency miner, saved as a binary named wipefs (strangely assuming the name of a little-used tool for wiping Linux filesystem signatures). Finally, persistence is achieved through the addition of a 6-hourly cron job, running yet another base64 encoded Python script, this time stored as httpsd (presumably chosen to look like an imaginary SSL variant of Apache’s httpd). The following snippet of the controller code highlights this latter operation:
Protecting Linux VMs
Many of the type of attack mentioned in this post originate with brute force of SSH passwords. There are several ways of mitigating this technique, depending on access requirements for individual machines. The simplest solution is, where possible, disabling SSH or locking down access to specific networks, as mentioned in our network best practice advice. Where ad-hoc connections from the internet are required to particular machines, enabling Azure Security Center’s JIT virtual machine access allows the granting of on-demand access for a limited time window. Lastly, as with the vast majority of SSH guidance, disabling password login by enabling public key authentication is almost always a sensible precaution.
Whilst PowerShell analytics may at first glance appear to be entirely Windows focussed, attackers targeting Linux systems exhibit similar behaviour. Fundamentally, this should come as no real surprise, as it is often the same people attacking both platforms. It is also important to remember methods of detecting them can often be similarly portable. This portability can in fact be taken a step further, as shown in the above example, using platform agnostic scripting languages such as Python, an attacker is often able to use the exact same code on both Windows and Linux, making any subsequent detections identical.