Skip to content

Commit 04caa9a

Browse files
author
Brent Cook
committed
Land rapid7#6710, Add Powershell meterpreter bindings
2 parents 39bd501 + 627615d commit 04caa9a

File tree

5 files changed

+75
-3
lines changed

5 files changed

+75
-3
lines changed

Gemfile.lock

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ PATH
1313
metasploit-concern
1414
metasploit-credential (= 1.1.0)
1515
metasploit-model (= 1.1.0)
16-
metasploit-payloads (= 1.1.5)
16+
metasploit-payloads (= 1.1.6)
1717
metasploit_data_models (= 1.3.0)
1818
msgpack
1919
network_interface (~> 0.0.1)
@@ -130,7 +130,7 @@ GEM
130130
activemodel (>= 4.0.9, < 4.1.0)
131131
activesupport (>= 4.0.9, < 4.1.0)
132132
railties (>= 4.0.9, < 4.1.0)
133-
metasploit-payloads (1.1.5)
133+
metasploit-payloads (1.1.6)
134134
metasploit_data_models (1.3.0)
135135
activerecord (>= 4.0.9, < 4.1.0)
136136
activesupport (>= 4.0.9, < 4.1.0)

lib/rex/post/meterpreter/extensions/powershell/powershell.rb

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,30 @@ def initialize(client)
3131
end
3232

3333

34+
def import_file(opts={})
35+
return nil unless opts[:file]
36+
37+
# if it's a script, then we'll just use execute_string
38+
if opts[:file].end_with?('.ps1')
39+
opts[:code] = ::File.read(opts[:file])
40+
return execute_string(opts)
41+
end
42+
43+
# if it's a dll (hopefully a .NET 2.0 one) then do something different
44+
if opts[:file].end_with?('.dll')
45+
# TODO: perhaps do some kind of check to see if the DLL is a .NET assembly?
46+
binary = ::File.read(opts[:file])
47+
48+
request = Packet.create_request('powershell_assembly_load')
49+
request.add_tlv(TLV_TYPE_POWERSHELL_ASSEMBLY_SIZE, binary.length)
50+
request.add_tlv(TLV_TYPE_POWERSHELL_ASSEMBLY, binary)
51+
client.send_request(request)
52+
return true
53+
end
54+
55+
return false
56+
end
57+
3458
def execute_string(opts={})
3559
return nil unless opts[:code]
3660

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ module Powershell
88
TLV_TYPE_POWERSHELL_SESSIONID = TLV_META_TYPE_STRING | (TLV_EXTENSIONS + 1)
99
TLV_TYPE_POWERSHELL_CODE = TLV_META_TYPE_STRING | (TLV_EXTENSIONS + 2)
1010
TLV_TYPE_POWERSHELL_RESULT = TLV_META_TYPE_STRING | (TLV_EXTENSIONS + 3)
11+
TLV_TYPE_POWERSHELL_ASSEMBLY_SIZE = TLV_META_TYPE_UINT | (TLV_EXTENSIONS + 4)
12+
TLV_TYPE_POWERSHELL_ASSEMBLY = TLV_META_TYPE_RAW | (TLV_EXTENSIONS + 5)
1113

1214
end
1315
end

lib/rex/post/meterpreter/ui/console/command_dispatcher/powershell.rb

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ def name
2929
#
3030
def commands
3131
{
32+
'powershell_import' => 'Import a PS1 script or .NET Assembly DLL',
3233
'powershell_shell' => 'Create an interactive Powershell prompt',
3334
'powershell_execute' => 'Execute a Powershell command string'
3435
}
@@ -68,6 +69,51 @@ def cmd_powershell_shell(*args)
6869
shell.interact_with_channel(channel)
6970
end
7071

72+
@@powershell_import_opts = Rex::Parser::Arguments.new(
73+
'-s' => [true, 'Specify the id/name of the Powershell session to run the command in.'],
74+
'-h' => [false, 'Help banner']
75+
)
76+
77+
def powershell_import_usage
78+
print_line('Usage: powershell_import <path to file> [-s session-id]')
79+
print_line
80+
print_line('Imports a powershell script or assembly into the target.')
81+
print_line('The file must end in ".ps1" or ".dll".')
82+
print_line('Powershell scripts can be loaded into any session (via -s).')
83+
print_line('.NET assemblies are applied to all sessions.')
84+
print_line(@@powershell_import_opts.usage)
85+
end
86+
87+
#
88+
# Import a script or assembly component into the target.
89+
#
90+
def cmd_powershell_import(*args)
91+
if args.length == 0 || args.include?('-h')
92+
powershell_import_usage
93+
return false
94+
end
95+
96+
opts = {
97+
file: args.shift
98+
}
99+
100+
@@powershell_import_opts.parse(args) { |opt, idx, val|
101+
case opt
102+
when '-s'
103+
opts[:session_id] = val
104+
end
105+
}
106+
107+
result = client.powershell.import_file(opts)
108+
if result.nil? || result == false
109+
print_error("File failed to load.")
110+
elsif result == true || result.empty?
111+
print_good("File successfully imported. No result was returned.")
112+
else
113+
print_good("File successfully imported. Result:\n#{result}")
114+
end
115+
end
116+
71117
@@powershell_execute_opts = Rex::Parser::Arguments.new(
72118
'-s' => [true, 'Specify the id/name of the Powershell session to run the command in.'],
73119
'-h' => [false, 'Help banner']

metasploit-framework.gemspec

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ Gem::Specification.new do |spec|
7070
# are needed when there's no database
7171
spec.add_runtime_dependency 'metasploit-model', '1.1.0'
7272
# Needed for Meterpreter
73-
spec.add_runtime_dependency 'metasploit-payloads', '1.1.5'
73+
spec.add_runtime_dependency 'metasploit-payloads', '1.1.6'
7474
# Needed by msfgui and other rpc components
7575
spec.add_runtime_dependency 'msgpack'
7676
# get list of network interfaces, like eth* from OS.

0 commit comments

Comments
 (0)