USB Device Parsing Logparser Scripts

Filed under Computer Forensics

By Guest Blogger Dave Kleiman:

Recently I had a need to check a network consisting of a few hundred systems in order to identify systems that had certain USB devices attached. There was not a need to check for “deleted” registry keys or unallocated space in the registry database. I needed to collect the standard USB keys, and compare them to a list of “Friendly Names” and “Serial Numbers” provided to me.

Standard keys I collected:

\HKLM\SYSTEM\ControlSet001\Enum\USBSTOR
\HKLM\SYSTEM\ControlSet001\Enum\USB
\HKLM\SYSTEM\MountedDevices
\HKLM\SYSTEM\ControlSet001\Control\DeviceClasses\{53f56307-b6bf-11d0-94f2-00a0c91efb8b}

Additionally, I wanted to collect the times stamps for each respective registry entry. The challenge was to do this quickly without having to traverse a building to each office and collect them direct with a standard forensic tool.

My solution, Log Parser. That is right Log Parser(r), a free tool from Microsoft (http://www.microsoft.com/downloads/details.aspx?displaylang=en&FamilyID=890cd06b-abf8-4c25-91b2-f8d975cf8c07) that will parse the File System, Registry, and whole lot more.

Log Parser allows for variables in scripts. I created a script for each registry key I wanted to retrieve, and added a variable so I could enter the Host name for each.

First, I told Log Parser what I wanted values I wanted to retrieve from each registry key with a SELECT statement (Note I chose select 2000 for these scripts, you can change that number to as much or as little as you see fit, in this the top 2000 entries are retrieved):

SSELECT TOP 2000
ComputerName,
Path,
KeyName,
Valuename,
ValueType,
Value,
LastWriteTime

Then I indicated the output with the INTO statement and utilized an asterisk (*) to allow it to append the variable name to each file name

INTO *-USBSTOR.csv

Finally the FROM statement tells Log Parser what key to retrieve, and the variable %NAME% allows for entry of a Hostname at the command prompt.

FROM \\%NAME%\HKLM\SYSTEM\ControlSet001\Enum\USBSTOR

You can download the easy to use scripts HERE.

Unzip the package into a directory.

Drop down to a command prompt and type:

logparser file:_DeviceClasses.sql?name=HostA

logparser file:_MountedDevice.sql?name=HostA

logparser file:_USBSTOR.sql?name=HostA

logparser file:_USB.sql?name=HostA

Continue to do this for each Host, and it will return and individual CSV for each script separated by Host Name as outlined below:

HostA.network.local-DeviceClass.csv

HostA.network.local-MountedDevices.csv

HostA.network.local-USBSTOR.csv

HostA.network.local-USB.csv

I have many Log Parser scripts made for Forensics and the instructions on how to use them available on my website: http://www.computerforensicexaminer.com/computer-forensics-expert-florida-miami-palm-beach-lauderdale-dave-kleiman-forensic-training-files/

Respectfully, Dave Kleiman – http://www.ComputerForensicExaminer.com – sans@davekleiman.com

One Comment

  1. Dimitrios Kapsalis
    Posted October 5, 2009 at 3:42 pm | Permalink

    I wrote a meterpreter script which can be used to pull the USB device information from a system.

    [code]
    #
    # This is a Meterpreter script designed to be used by the Metasploit Framework
    #
    # Meterpreter script for pulling forensics data from registry for any USB device
    # connected to system
    #
    # Provided by Dimitrios Kapsalis
    # Verion: 0.1

    require 'fileutils'

    #Function for writing data to a file
    def m_filewrt(file2wrt, data2wrt)
    output = ::File.open(file2wrt, "a")
    data2wrt.each_line do |d|
    output.puts(d)
    end
    output.close
    end

    # ====================================================================================================================================
    # Checking for UAC
    # ====================================================================================================================================
    def m_uaccheck(session)
    uac = false
    winversion = session.sys.config.sysinfo
    if winversion['OS']=~ /Windows Vista/ or winversion['OS']=~ /Windows 7/
    if session.sys.config.getuid != "NT AUTHORITY\\SYSTEM"
    begin
    print_status("Checking if UAC is enabled .....")
    key = session.sys.registry.open_key(HKEY_LOCAL_MACHINE, 'HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System')
    if key.query_value('Identifier') == 1
    print_status("UAC is Enabled")
    uac = true
    end
    key.close

    rescue::Exception => e
    print_status("Error Checking UAC: #{e.class} #{e}")
    end
    end
    end
    return uac
    end

    # ====================================================================================================================================
    # Print message to file on target
    # ====================================================================================================================================
    def m_writetofile(session,file,message)
    cmd = "cmd /c echo #{message} >> #{file}"
    m_exec(session, cmd)
    end

    # ====================================================================================================================================
    # Delete a file (meterpreter has no unlink API yet)
    # ====================================================================================================================================
    def m_unlink(session, path)
    r = session.sys.process.execute("cmd.exe /c del /F /S /Q " + path, nil, {'Hidden' => 'true'})
    while(r.name)
    select(nil, nil, nil, 0.10)
    end
    r.close
    end

    # ====================================================================================================================================
    # Exec a command and return the results
    # ====================================================================================================================================
    def m_exec(session, cmd)
    begin
    r = session.sys.process.execute(cmd, nil, {'Hidden' => true, 'Channelized' => true})
    b = ""
    while(d = r.channel.read)
    b < e
    print_status("Error Running Command #{cmd}: #{e.class} #{e}")
    end
    end

    # ====================================================================================================================================
    # Function to upload files
    # ====================================================================================================================================
    def m_upload(session,file)
    location = session.fs.file.expand_path("%temp%")
    fileontrgt = "#{location}\\#{rand(100)}.exe"
    print_status(" -- Uploading #{file}....")
    session.fs.file.upload_file("#{fileontrgt}","#{file}")
    print_status(" -- #{file} uploaded!")
    print_status(" -- File on target #{fileontrgt}")
    return fileontrgt
    end

    # ====================================================================================================================================
    # Function to download files
    # ====================================================================================================================================
    def m_download(session,src,dst)
    location = session.fs.file.expand_path("%temp%")
    print_status(" -- Downloading #{src}....")
    session.fs.file.download_file("#{dst}","#{src}")
    print_status(" -- #{dst} downloaded!")
    end

    # ====================================================================================================================================
    # Script proper
    # ====================================================================================================================================

    # The 'client' object holds the Meterpreter session
    # Aliasing here for plugin compatibility
    session = client

    script_name = "installedsoftware"

    # Extract the host and port
    host,port = session.tunnel_peer.split(':')

    print_status("New session on #{host}:#{port}...")

    # Create a directory for the logs
    logs = ::File.join(Msf::Config.config_directory, 'logs',script_name , host + "_" + Time.now.strftime("%Y%m%d.%M%S")+sprintf("%.5d",rand(100000)) )

    # Create the log directory
    ::FileUtils.mkdir_p(logs)

    print_status("-- Files saved to #{logs}...")

    location = session.fs.file.expand_path("%temp%")
    filename = "#{rand(100)}.dat"
    fileontrgt = "#{location}\\#{filename}"
    print_status("-- Data logged to #{fileontrgt}....")

    begin

    #===============================================================================================================================
    #===============================================================================================================================
    #===============================================================================================================================
    # Pull USB history Pull USB history Pull USB history Pull USB history Pull USB history Pull USB history Pull USB history
    #===============================================================================================================================
    #===============================================================================================================================
    #===============================================================================================================================

    #===========================================================================================
    # Dump USB device history
    #===========================================================================================

    key = "HKLM\\SYSTEM\\CurrentControlSet\\Enum\\USBSTOR"
    root_key, base_key = session.sys.registry.splitkey(key)
    log = "#{logs}\\installedusb.txt"

    message = ""
    message << "---------------------------------------------------------------------\n"
    message << "Dumping software installed on pc per registry #{key}... \n"
    message << "---------------------------------------------------------------------\n"
    m_filewrt(log, message)

    session.sys.registry.create_key(root_key, base_key).each_key() do |device|
    puts device

    # =========================================
    # ...
    # =========================================
    session.sys.registry.create_key(root_key, "#{base_key}\\#{device}").each_key() do |intermediate|
    puts intermediate

    message = ""

    rk = session.sys.registry.open_key(root_key, "#{base_key}\\#{device}\\#{intermediate}", KEY_READ)
    cl = rk.query_value("class")
    deviceDesc = rk.query_value("DeviceDesc")
    friendlyName = rk.query_value("FriendlyName")
    hardwareID = rk.query_value("HardwareID")

    rk.close

    message << " ========================================================================================================================\n"
    message << " Friendly Name : #{friendlyName.data}\n"
    message << " - Class : #{cl.data}\n"
    message << " - DeviceDesc : #{deviceDesc.data}\n"
    message << " - HardwareID : #{hardwareID.data}\n"

    hdid = hardwareID.data
    ids = hdid.split("USBSTOR")

    ids.each do |num|
    message < e
    print_status("Exception: #{e.class} #{e} #{e.backtrace}")
    end

    print_status("Completed processing on #{host}:#{port}...")

    [/code]